<tenant:repositoryDomain name="default-domain" repositoryClient="nuxeo-java"/>
<tenant:properties>
- <types:item><types:key>datePattern</types:key><types:value>MM/dd/YYYY</types:value></types:item>
- <types:item><types:key>datePattern</types:key><types:value>dd.MM.YYYY</types:value></types:item>
- <!-- <types:item><types:key>datePattern</types:key><types:value>dd/MM/YYYY</types:value></types:item> -->
+ <types:item><types:key>datePattern</types:key><types:value>MM/dd/yyyy</types:value></types:item>
+ <types:item><types:key>datePattern</types:key><types:value>dd.MM.yyyy</types:value></types:item>
+ <!-- <types:item><types:key>datePattern</types:key><types:value>dd/MM/yyyy</types:value></types:item> -->
<!-- <types:item><types:key>localeLanguage</types:key><types:value>en</types:value></types:item> -->
<!-- <types:item><types:key>localeLanguage</types:key><types:value>da</types:value></types:item> -->
</tenant:properties>
<tenant:repositoryDomain name="pahma-domain" repositoryClient="nuxeo-java"/>
<tenant:properties>
- <types:item><types:key>datePattern</types:key><types:value>MM/dd/YYYY</types:value></types:item>
- <types:item><types:key>datePattern</types:key><types:value>dd.MM.YYYY</types:value></types:item>
- <!-- <types:item><types:key>datePattern</types:key><types:value>dd/MM/YYYY</types:value></types:item> -->
+ <types:item><types:key>datePattern</types:key><types:value>MM/dd/yyyy</types:value></types:item>
+ <types:item><types:key>datePattern</types:key><types:value>dd.MM.yyyy</types:value></types:item>
+ <!-- <types:item><types:key>datePattern</types:key><types:value>dd/MM/yyyy</types:value></types:item> -->
<!-- <types:item><types:key>localeLanguage</types:key><types:value>en</types:value></types:item> -->
<!-- <types:item><types:key>localeLanguage</types:key><types:value>da</types:value></types:item> -->
</tenant:properties>
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.TimeZone;
import org.collectionspace.services.common.ServiceMain;
private static final Logger logger = LoggerFactory.getLogger(DateTimeFormatUtils.class);
final static String DATE_FORMAT_PATTERN_PROPERTY_NAME = "datePattern";
+ final static String ISO_8601_FLOATING_DATE_PATTERN = "yyyy-MM-dd";
final static String ISO_8601_UTC_TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+ static Map<String,List<DateFormat>> dateFormatters = new HashMap<String,List<DateFormat>>();
+ static Map<String,List<String>> datePatterns = new HashMap<String,List<String>>();
+
// FIXME:
// - Add a method to return the default set of ISO 8601-based date patterns,
// irrespective of per-tenant configuration.
- // - Consider cacheing the lists of per-tenant date format patterns
- // and refresh the cached copies whenever tenant bindings are read.
// - Methods below related to per-tenant configuration of date formats might
// be moved to their own class.
+ // - Investigate whether the map of per-tenant date formatters might best be
+ // maintained within a singleton class.
+
+ /**
+ * Returns a list of the date formatters permitted in a service context.
+ *
+ * @param ctx a service context.
+ *
+ * @return a list of date formatters permitted in the service context.
+ * Returns an empty list of date formatters if the service context is null.
+ */
+ public static List<DateFormat> getDateFormattersForTenant(ServiceContext ctx) {
+ List<DateFormat> formatters = new ArrayList<DateFormat>();
+ if (ctx == null) {
+ return formatters;
+ }
+ return getDateFormattersForTenant(ctx.getTenantId());
+ }
+
+ /**
+ * Returns a list of the date formatters permitted for a tenant, specified
+ * by tenant ID.
+ *
+ * @param tenantId a tenant ID.
+ *
+ * @return a list of date formatters permitted for the tenant.
+ * Returns an empty list of date formatters if the tenant ID is null or empty.
+ */
+ public static List<DateFormat> getDateFormattersForTenant(String tenantId) {
+ List<DateFormat> formatters = new ArrayList<DateFormat>();
+ if (tenantId == null || tenantId.trim().isEmpty()) {
+ return formatters;
+ }
+ // If a list of date formatters for this tenant already exists, return it.
+ if (dateFormatters != null && dateFormatters.containsKey(tenantId)) {
+ formatters = dateFormatters.get(tenantId);
+ if (formatters != null && formatters.size() > 0) {
+ return formatters;
+ }
+ }
+ // Otherwise, generate that list and cache it for re-use.
+ List<String> patterns = getDateFormatPatternsForTenant(tenantId);
+ DateFormat df = null;
+ for (String pattern : patterns) {
+ df = getDateFormatter(pattern);
+ if (df != null) {
+ formatters.add(df);
+ }
+ }
+ if (dateFormatters != null) {
+ dateFormatters.put(tenantId, formatters);
+ }
+ return formatters;
+ }
/**
* Returns a list of the date format patterns permitted in a service context.
* @param tenantId a tenant ID.
*
* @return a list of date format patterns permitted for the tenant.
+ * Returns an empty list of patterns if the tenant ID is null or empty.
*/
public static List<String> getDateFormatPatternsForTenant(String tenantId) {
List<String> patterns = new ArrayList<String>();
if (tenantId == null || tenantId.trim().isEmpty()) {
return patterns;
}
+ // If a list of date patterns for this tenant already exists, return it.
+ if (datePatterns != null && datePatterns.containsKey(tenantId)) {
+ patterns = datePatterns.get(tenantId);
+ if (patterns != null && patterns.size() > 0) {
+ return patterns;
+ }
+ }
+ // Otherwise, generate that list and cache it for re-use.
TenantBindingConfigReaderImpl tReader =
ServiceMain.getInstance().getTenantBindingConfigReader();
TenantBindingType tenantBinding = tReader.getTenantBinding(tenantId);
patterns = TenantBindingUtils.getPropertyValues(tenantBinding,
DATE_FORMAT_PATTERN_PROPERTY_NAME);
- return validatePatterns(patterns);
+ patterns = validatePatterns(patterns);
+ if (datePatterns != null) {
+ datePatterns.put(tenantId, patterns);
+ }
+ return patterns;
}
+ /**
+ * Validates a list of date or date/time patterns, checking each pattern
+ * to determine whether it can be used to instantiate a date formatter.
+ *
+ * These patterns must conform to the format for date and time pattern strings
+ * specified in the Javadocs for the Java language class, java.text.SimpleDateFormat.
+ *
+ * @param patterns a list of date or date/time patterns.
+ *
+ * @return a list of valid patterns, excluding any patterns
+ * that could not be used to instantiate a date formatter.
+ */
public static List<String> validatePatterns(List<String> patterns) {
if (patterns == null) {
return new ArrayList<String>();
}
+ DateFormat df = new SimpleDateFormat();
List<String> validPatterns = new ArrayList<String>();
for (String pattern : patterns) {
try {
- DateFormat df = getDateFormatter(pattern);
+ df = getDateFormatter(pattern);
validPatterns.add(pattern);
} catch (IllegalArgumentException iae) {
logger.warn("Invalid " + DATE_FORMAT_PATTERN_PROPERTY_NAME + " property: " + pattern);
return validPatterns;
}
+ /**
+ * Returns an ISO 8601 timestamp representation of a presumptive date or
+ * date/time string. Applies the set of date formatters for a supplied tenant
+ * to attempt to parse the string.
+ *
+ * @param str a String, possibly a date or date/time String.
+ * @param tenantId a tenant ID.
+ *
+ * @return an ISO 8601 timestamp representation of that String.
+ * If the String cannot be parsed by the date formatters
+ * for the supplied tenant, return the original string.
+ */
+ public static String toIso8601Timestamp(String dateStr, String tenantId) {
+ Date date = null;
+ List<DateFormat> formatters = getDateFormattersForTenant(tenantId);
+// for (DateFormat formatter : getDateFormattersForTenant(tenantId)) {
+ for (DateFormat formatter : formatters) {
+ date = parseDate(dateStr, formatter);
+ if (date != null) {
+ break;
+ }
+ }
+ if (date == null) {
+ return dateStr;
+ } else {
+ GregorianCalendar gcal = new GregorianCalendar();
+ gcal.setTime(date);
+ String isoStr = formatAsISO8601Timestamp(gcal);
+ return isoStr;
+ }
+ }
+
/**
* Returns a representation of a calendar date and time instance,
* as an ISO 8601-formatted timestamp in the UTC time zone.
/**
* Identifies whether a presumptive date or date/time can be parsed
- * by a date parser, using a specified format pattern.
+ * by a date parser, using a supplied format pattern.
*
* @param str a String, possibly a date or date/time String.
- *
* @param pattern A date or date/time pattern.
*
* @return true, if the String can be parsed, using the pattern;
if (pattern == null || pattern.trim().isEmpty()) {
return false;
}
+ if (str == null || str.trim().isEmpty()) {
+ return false;
+ }
DateFormat df = null;
try {
df = new SimpleDateFormat(pattern);
return true;
}
+ /**
+ * Parses a presumptive date or date/time, using a supplied format pattern.
+ *
+ * @param str a String, possibly a date or date/time String.
+ * @param pattern A date or date/time pattern.
+ *
+ * @return A date value, resulting from parsing the String using the
+ * supplied pattern. Returns null if the parsing attempt fails.
+ */
+ public static Date parseDate(String str, String pattern) {
+ if (pattern == null || pattern.trim().isEmpty()) {
+ return null;
+ }
+ if (str == null || str.trim().isEmpty()) {
+ return null;
+ }
+ DateFormat df = null;
+ Date date = null;
+ try {
+ df = new SimpleDateFormat(pattern);
+ date = parseDate(str, df);
+ } catch (IllegalArgumentException iae) {
+ return null;
+ }
+ return date;
+ }
+
+ /**
+ * Parses a presumptive date or date/time, using a supplied format pattern.
+ *
+ * @param str a String, possibly a date or date/time String.
+ * @param df A date formatter.
+ *
+ * @return A date value, resulting from parsing the String using the
+ * supplied formatter. Returns null if the parsing attempt fails.
+ */
+ public static Date parseDate(String str, DateFormat df) {
+ if (df == null) {
+ return null;
+ }
+ if (str == null || str.trim().isEmpty()) {
+ return null;
+ }
+ Date date = null;
+ try {
+ df.setLenient(false);
+ date = df.parse(str);
+ } catch (ParseException pe) {
+ return null;
+ }
+ return date;
+ }
+
/**
* Returns a date formatter for a provided date or date/time pattern.
*
}
try {
df = new SimpleDateFormat(pattern);
+ df.setLenient(false);
} catch (IllegalArgumentException iae) {
logger.warn("Invalid date pattern string '" + pattern + "': " + iae.getMessage());
}
return df;
}
+
}
*/
package org.collectionspace.services.common.document;
+import java.util.Calendar;
+
import java.lang.reflect.Array;
import java.io.File;
import javax.xml.transform.TransformerException;
import org.collectionspace.services.common.ServiceMain;
+import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.datetime.DateTimeFormatUtils;
import org.collectionspace.services.common.service.ObjectPartContentType;
import org.collectionspace.services.common.service.ObjectPartType;
import org.collectionspace.services.common.service.XmlContentType;
import org.nuxeo.ecm.core.schema.types.Field;
import org.nuxeo.ecm.core.schema.types.ListType;
import org.nuxeo.ecm.core.schema.types.Schema;
+import org.nuxeo.ecm.core.schema.types.SimpleType;
import org.nuxeo.ecm.core.schema.types.Type;
import org.nuxeo.ecm.core.schema.types.JavaTypes;
+import org.nuxeo.ecm.core.schema.types.primitives.DateType;
import org.nuxeo.ecm.core.schema.types.primitives.StringType;
import org.nuxeo.ecm.core.schema.types.FieldImpl;
import org.nuxeo.ecm.core.schema.types.QName;
parent.appendChild(element);
// extract the element content
if (type.isSimpleType()) {
- element.setTextContent(type.encode(value));
+ element.setTextContent(type.encode(value));
} else if (type.isComplexType()) {
ComplexType ctype = (ComplexType) type;
if (ctype.getName().equals(TypeConstants.CONTENT)) {
}
+ /*
+ * Identifies whether a property type is a date type.
+ *
+ * @param type a type.
+ * @return true, if is a date type;
+ * false, if it is not a date type.
+ */
+ private static boolean isDateType(Type type) {
+ SimpleType st = (SimpleType) type;
+ if (st.getPrimitiveType() instanceof DateType) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
/**
* Insert multi values.
*
* @return the map
*/
public static Map<String, Object> parseProperties(ObjectPartType partMeta,
- Document document) {
+ Document document, ServiceContext ctx) {
Map<String, Object> result = null;
String schemaName = partMeta.getLabel();
Schema schema = getSchemaFromName(schemaName);
org.dom4j.io.DOMReader xmlReader = new org.dom4j.io.DOMReader();
org.dom4j.Document dom4jDocument = xmlReader.read(document);
try {
- result = loadSchema(schema, dom4jDocument.getRootElement());
+ result = loadSchema(schema, dom4jDocument.getRootElement(), ctx);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
* @throws Exception the exception
*/
@SuppressWarnings("unchecked")
- static private Map<String, Object> loadSchema(Schema schema, org.dom4j.Element schemaElement)
+ static private Map<String, Object> loadSchema(Schema schema, org.dom4j.Element schemaElement, ServiceContext ctx)
throws Exception {
String schemaName1 = schemaElement.attributeValue(ExportConstants.NAME_ATTR);
String schemaName = schema.getName();
String name = element.getName();
Field field = schema.getField(name);
if (field != null) {
- Object value = getElementData(element, field.getType());
+ Object value = getElementData(element, field.getType(), ctx);
data.put(name, value);
} else {
if (logger.isDebugEnabled() == true) {
* @return the element data
*/
@SuppressWarnings("unchecked")
- static private Object getElementData(org.dom4j.Element element, Type type) {
+ static private Object getElementData(org.dom4j.Element element, Type type,
+ ServiceContext ctx) {
Object result = null;
if (type.isSimpleType()) {
- result = type.decode(element.getText());
+ // Convert incoming date values to a canonical date representation,
+ if (isDateType(type)) {
+ result = DateTimeFormatUtils.toIso8601Timestamp((String) element.getText(),
+ ctx.getTenantId());
+ } else {
+ result = type.decode(element.getText());
+ }
} else if (type.isListType()) {
ListType ltype = (ListType) type;
List<Object> list = new ArrayList<Object>();
Iterator<org.dom4j.Element> it = element.elementIterator();
while (it.hasNext()) {
org.dom4j.Element el = it.next();
- list.add(getElementData(el, ltype.getFieldType()));
+ list.add(getElementData(el, ltype.getFieldType(), ctx));
}
Type ftype = ltype.getFieldType();
if (ftype.isSimpleType()) { // these are stored as arrays
org.dom4j.Element el = it.next();
String name = el.getName();
Object value = getElementData(el, ctype.getField(
- el.getName()).getType());
+ el.getName()).getType(), ctx);
map.put(name, value);
}
result = map;
import java.io.InputStream;
import java.util.Collection;
-import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.nuxeo.ecm.core.api.model.Property;
import org.nuxeo.ecm.core.api.model.PropertyException;
-import org.nuxeo.ecm.core.schema.types.ComplexType;
-import org.nuxeo.ecm.core.schema.types.Field;
-import org.nuxeo.ecm.core.schema.types.ListType;
import org.nuxeo.ecm.core.schema.types.Schema;
-import org.nuxeo.ecm.core.schema.types.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
if (partMeta == null) {
continue;
}
- fillPart(part, docModel, partMeta, action);
+ fillPart(part, docModel, partMeta, action, ctx);
}//rof
}
* @param partMeta metadata for the object to fill
* @throws Exception
*/
- protected void fillPart(InputPart part, DocumentModel docModel, ObjectPartType partMeta, Action action)
+ protected void fillPart(InputPart part, DocumentModel docModel,
+ ObjectPartType partMeta, Action action, ServiceContext ctx)
throws Exception {
InputStream payload = part.getBody(InputStream.class, null);
//TODO: callback to handler if registered to validate the
//document
// Map<String, Object> objectProps = DocumentUtils.parseProperties(document.getFirstChild());
- Map<String, Object> objectProps = DocumentUtils.parseProperties(partMeta, document);
+ Map<String, Object> objectProps = DocumentUtils.parseProperties(partMeta, document, ctx);
if (action == Action.UPDATE) {
this.filterReadOnlyPropertiesForPart(objectProps, partMeta);
}
import java.util.HashMap;\r
import java.util.Map;\r
\r
+import org.collectionspace.services.common.context.ServiceContext;\r
import org.collectionspace.services.common.document.DocumentUtils;\r
import org.collectionspace.services.common.service.ObjectPartType;\r
\r
*/\r
@Override\r
protected void fillPart(InputPart part, DocumentModel docModel, \r
- ObjectPartType partMeta, Action action)\r
+ ObjectPartType partMeta, Action action, ServiceContext ctx)\r
throws Exception {\r
InputStream payload = part.getBody(InputStream.class, null);\r
\r
if(logger.isDebugEnabled()) {
logger.debug("validate() action=" + action.name());
}
+
+ /*
try {
+
MultipartServiceContext mctx = (MultipartServiceContext) ctx;
MovementsCommon mc = (MovementsCommon) mctx.getInputPart(mctx.getCommonPartLabel(),
MovementsCommon.class);
// in the incoming payload are date fields whose values we
// might wish to validate, and of extracting their values,
// than hard-coding them here.
+ //
+ // See DocumentUtils.parseProperties() for one possible approach.
- /*
boolean validDateFormat = false;
String locDate = mc.getLocationDate();
for (String pattern : patterns) {
invalid = true;
msgBldr.append("\nlocationDate : unrecognized date format '" + locDate + "'");
}
- *
- */
if(action.equals(Action.CREATE)) {
//create specific validation here
} catch (Exception e) {
throw new InvalidDocumentException(e);
}
+ *
+ */
}