From: remillet Date: Tue, 31 Mar 2015 21:17:10 +0000 (-0700) Subject: CSPACE-6375: Fixed another date processing/encoding issue. X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=fa099fb862de0affa6d62c636329760056c8eb60;p=tmp%2Fjakarta-migration.git CSPACE-6375: Fixed another date processing/encoding issue. --- diff --git a/services/common/src/main/java/org/collectionspace/services/common/document/DocumentUtils.java b/services/common/src/main/java/org/collectionspace/services/common/document/DocumentUtils.java index 5b711d42a..bfd99e0d3 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/document/DocumentUtils.java +++ b/services/common/src/main/java/org/collectionspace/services/common/document/DocumentUtils.java @@ -52,8 +52,8 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; import org.collectionspace.services.common.ServiceMain; import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils; @@ -67,6 +67,7 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.InputPart; //import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; +import org.mortbay.log.Log; import org.nuxeo.ecm.core.io.ExportConstants; import org.nuxeo.common.collections.PrimitiveArrays; import org.nuxeo.ecm.core.api.DocumentModel; @@ -104,13 +105,12 @@ import org.w3c.dom.Text; /** * DocumentUtils is a collection of utilities related to document management * - * $LastChangedRevision: $ - * $LastChangedDate: $ + * $LastChangedRevision: $ $LastChangedDate: $ */ public class DocumentUtils { /** The Constant logger. */ - private static final Logger logger = - LoggerFactory.getLogger(DocumentUtils.class); + private static final Logger logger = LoggerFactory + .getLogger(DocumentUtils.class); /** The name dateVal separator. */ private static String NAME_VALUE_SEPARATOR = "|"; @@ -127,21 +127,21 @@ public class DocumentUtils { /** The XML elements with this suffix will indicate. */ private static String STRUCTURED_TYPE_SUFFIX = "List"; - - /** * The Class NameValue. */ - private static class NameValue { + private static class NameValue { /** * Instantiates a new name dateVal. */ NameValue() { - // default scoped constructor to removed "synthetic accessor" warning - } + // default scoped constructor to removed "synthetic accessor" + // warning + } + /** The name. */ - String name; + String name; /** The dateVal. */ String value; }; @@ -149,10 +149,11 @@ public class DocumentUtils { /** * Log multipart input. * - * @param multipartInput the multipart input + * @param multipartInput + * the multipart input */ public static void logMultipartInput(MultipartInput multipartInput) { - if (logger.isDebugEnabled() == true) { + if (logger.isDebugEnabled() == true) { List parts = multipartInput.getParts(); for (InputPart part : parts) { try { @@ -164,21 +165,24 @@ public class DocumentUtils { } } } - + /** - * Log byte array input stream. After logging this method resets the stream and returns it in its original state. + * Log byte array input stream. After logging this method resets the stream + * and returns it in its original state. * - * @param inputStream the input stream + * @param inputStream + * the input stream * @return the byte array input stream */ - private static ByteArrayInputStream logByteArrayInputStream(ByteArrayInputStream inputStream) { + private static ByteArrayInputStream logByteArrayInputStream( + ByteArrayInputStream inputStream) { ByteArrayInputStream result = inputStream; if (logger.isTraceEnabled() == true) { - ByteArrayInputStream bais = (ByteArrayInputStream)inputStream; + ByteArrayInputStream bais = (ByteArrayInputStream) inputStream; int length = bais.available(); - byte [] buff = new byte[length]; + byte[] buff = new byte[length]; try { bais.read(buff); } catch (Exception e) { @@ -199,12 +203,13 @@ public class DocumentUtils { /** * Gets the xML schema. * - * @param partMeta the part meta + * @param partMeta + * the part meta * @return the xML schema - * @throws Exception the exception + * @throws Exception + * the exception */ - private static File getXMLSchema(ObjectPartType partMeta) - throws Exception { + private static File getXMLSchema(ObjectPartType partMeta) throws Exception { final String FILE_SEPARATOR = System.getProperty("file.separator"); final String XML_SCHEMA_EXTENSION = ".xsd"; final String SCHEMAS_DIR = "schemas"; @@ -215,48 +220,50 @@ public class DocumentUtils { // Look for an XML Schema (.xsd) file for the incoming part payload // String serverRoot = ServiceMain.getInstance().getServerRootDir(); - String schemasDir = serverRoot + FILE_SEPARATOR + - SCHEMAS_DIR + FILE_SEPARATOR; + String schemasDir = serverRoot + FILE_SEPARATOR + SCHEMAS_DIR + + FILE_SEPARATOR; // // Send a warning to the log file if the XML Schema file is missing // - String schemaName = schemasDir + partMeta.getLabel() + XML_SCHEMA_EXTENSION; + String schemaName = schemasDir + partMeta.getLabel() + + XML_SCHEMA_EXTENSION; try { schemaFile = new File(schemaName); } catch (Exception e) { if (logger.isWarnEnabled() == true) { - logger.warn("Missing schema file for incoming payload: " + schemaName); + logger.warn("Missing schema file for incoming payload: " + + schemaName); } } return schemaFile; } - + /** - * parseProperties given payload to create XML document. this - * method also closes given stream after parsing. - * @param payload stream - * @param partMeta - * @param validate - whether or not to validate the payload with an XML Schema + * parseProperties given payload to create XML document. this method also + * closes given stream after parsing. + * + * @param payload + * stream + * @param partMeta + * @param validate + * - whether or not to validate the payload with an XML Schema * @return parsed Document * @throws Exception */ - public static Document parseDocument(InputStream payload, ObjectPartType partMeta, Boolean validate) - throws Exception { - final String JAXP_SCHEMA_SOURCE = - "http://java.sun.com/xml/jaxp/properties/schemaSource"; - final String JAXP_SCHEMA_LANGUAGE = - "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; - final String W3C_XML_SCHEMA = - "http://www.w3.org/2001/XMLSchema"; + public static Document parseDocument(InputStream payload, + ObjectPartType partMeta, Boolean validate) throws Exception { + final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; + final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; + final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; Document result = null; // Log the incoming unprocessed payload if (logger.isDebugEnabled() == true) { if (payload instanceof ByteArrayInputStream) { - payload = logByteArrayInputStream((ByteArrayInputStream)payload); + payload = logByteArrayInputStream((ByteArrayInputStream) payload); } - } + } File schemaFile = null; if (validate == true) { @@ -268,14 +275,15 @@ public class DocumentUtils { // try { // Create a builder factory - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory factory = DocumentBuilderFactory + .newInstance(); // // Lexical Control Settings that focus on content // factory.setCoalescing(true); factory.setExpandEntityReferences(true); factory.setIgnoringComments(true); - factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringElementContentWhitespace(true); // // Enable XML validation if we found an XML Schema for the payload // @@ -287,8 +295,9 @@ public class DocumentUtils { factory.setAttribute(JAXP_SCHEMA_SOURCE, schemaFile); } } catch (IllegalArgumentException e) { - String msg = "Error: JAXP DocumentBuilderFactory attribute not recognized: " + - JAXP_SCHEMA_LANGUAGE + ". Check to see if parser conforms to JAXP 1.2 spec."; + String msg = "Error: JAXP DocumentBuilderFactory attribute not recognized: " + + JAXP_SCHEMA_LANGUAGE + + ". Check to see if parser conforms to JAXP 1.2 spec."; if (logger.isWarnEnabled() == true) { logger.warn(msg); } @@ -315,16 +324,19 @@ public class DocumentUtils { } /** - * parseProperties extract given payload (XML) into Name-Value properties. this - * @param document to parse + * parseProperties extract given payload (XML) into Name-Value properties. + * this + * + * @param document + * to parse * @return map key=property name, dateVal=property dateVal * @throws Exception */ public static Map parseProperties(Node document) - throws Exception { + throws Exception { HashMap objectProps = new HashMap(); // Get a list of all elements in the document - Node root = document;//.getFirstChild(); + Node root = document;// .getFirstChild(); NodeList rootChildren = root.getChildNodes(); for (int i = 0; i < rootChildren.getLength(); i++) { Node node = rootChildren.item(i); @@ -335,14 +347,14 @@ public class DocumentUtils { Node firstChild = nodeChildren.item(0); Object value = null; if (firstChild != null) { - //first child node could be a whitespace char CSPACE-1026 - //so, check for number of children too + // first child node could be a whitespace char CSPACE-1026 + // so, check for number of children too if (firstChild.getNodeType() == Node.TEXT_NODE && nodeChildrenLen == 1) { value = getTextNodeValue(node); } else { value = getMultiValues(node); - } + } } // // Set the dateVal even if it's null. @@ -355,8 +367,9 @@ public class DocumentUtils { } /** - * getMultiStringValues retrieve multi-dateVal element values - * assumption: backend does not support more than 1 level deep hierarchy + * getMultiStringValues retrieve multi-dateVal element values assumption: + * backend does not support more than 1 level deep hierarchy + * * @param node * @return */ @@ -366,8 +379,8 @@ public class DocumentUtils { for (int i = 0; i < nodeChildren.getLength(); i++) { Node child = nodeChildren.item(i); String name = child.getNodeName(); - //assumption: backend does not support more than 2 levels deep - //hierarchy + // assumption: backend does not support more than 2 levels deep + // hierarchy String value = null; if (child.getNodeType() == Node.ELEMENT_NODE) { value = getTextNodeValue(child); @@ -380,7 +393,8 @@ public class DocumentUtils { /** * Removes all the immediate child text nodes. * - * @param parent the parent + * @param parent + * the parent * @return the element */ private static Node removeTextNodes(Node parent) { @@ -399,13 +413,14 @@ public class DocumentUtils { } /** - * getMultiValues retrieve multi-dateVal element values - * assumption: backend does not support more than 1 level deep hierarchy + * getMultiValues retrieve multi-dateVal element values assumption: backend + * does not support more than 1 level deep hierarchy + * * @param node * @return */ private static Object getMultiValues(Node node) throws Exception { - Object result = null; + Object result = null; Node nodeWithoutTextNodes = removeTextNodes(node); NodeList children = nodeWithoutTextNodes.getChildNodes(); @@ -413,7 +428,8 @@ public class DocumentUtils { Node grandChild = children.item(j).getFirstChild(); - // If any grandchild is non-null, return values for all grandchildren. + // If any grandchild is non-null, return values for all + // grandchildren. if (grandChild != null) { if (grandChild.getNodeType() == Node.TEXT_NODE) { result = getMultiStringValues(node); @@ -437,6 +453,7 @@ public class DocumentUtils { /** * getTextNodeValue retrieves text node dateVal + * * @param cnode * @return */ @@ -451,11 +468,14 @@ public class DocumentUtils { /** * isQualified check if the given dateVal is already qualified with given - * property name e.g. (in the example of a former 'otherNumber' field in - * CollectionObject) otherNumber|urn:org.collectionspace.id:24082390 is - * qualified with otherNumber but urn:org.walkerart.id:123 is not qualified - * @param name of the property, e.g. otherNumber - * @param dateVal of the property e.g. otherNumber + * property name e.g. (in the example of a former 'otherNumber' field in + * CollectionObject) otherNumber|urn:org.collectionspace.id:24082390 is + * qualified with otherNumber but urn:org.walkerart.id:123 is not qualified + * + * @param name + * of the property, e.g. otherNumber + * @param dateVal + * of the property e.g. otherNumber * @return */ private static boolean isQualified(String name, String value) { @@ -472,21 +492,19 @@ public class DocumentUtils { * qualify qualifies given property dateVal with given property name, e.g. * name=otherNumber and dateVal=urn:org.collectionspace.id:24082390 would be * qualified as otherNumber|urn:org.collectionspace.id:24082390. however, - * name=otherNumber and dateVal=otherNumber|urn:org.collectionspace.id:24082390 - * would be ignored as the given dateVal is already qualified once. + * name=otherNumber and + * dateVal=otherNumber|urn:org.collectionspace.id:24082390 would be ignored + * as the given dateVal is already qualified once. + * * @param name * @param dateVal * @return */ private static String qualify(String name, String value) { /* - String result = null; - if (isQualified(name, dateVal)) { - result = dateVal; - } else { - result = name + NAME_VALUE_SEPARATOR + dateVal; - } - return result; + * String result = null; if (isQualified(name, dateVal)) { result = + * dateVal; } else { result = name + NAME_VALUE_SEPARATOR + dateVal; } + * return result; */ return value; } @@ -494,44 +512,53 @@ public class DocumentUtils { /** * buildDocument builds org.w3c.dom.Document from given properties using * given metadata for a part + * * @param partMeta * @param rootElementName * @param objectProps * @return Document * @throws Exception */ - public static org.dom4j.Element buildDocument(ObjectPartType partMeta, String rootElementName, - Map objectProps) - throws Exception { + public static org.dom4j.Element buildDocument(ObjectPartType partMeta, + String rootElementName, Map objectProps) + throws Exception { ObjectPartContentType partContentMeta = partMeta.getContent(); XmlContentType xc = partContentMeta.getXmlContent(); if (xc == null) { return null; } - //FIXME: We support XML validation on the way in, so we should add it here (on the way out) as well. - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + // FIXME: We support XML validation on the way in, so we should add it + // here (on the way out) as well. + DocumentBuilder builder = DocumentBuilderFactory.newInstance() + .newDocumentBuilder(); Document document = builder.newDocument(); - document.setXmlStandalone(true); //FIXME: REM - Can we set this to false since it is not really standalone? + document.setXmlStandalone(true); // FIXME: REM - Can we set this to + // false since it is not really + // standalone? /* * JAXB unmarshaller recognizes the following kind of namespace * qualification only. More investigation is needed to use other prefix * * - * - * objectNumber-1252960222412 - * objectName-1252960222412 + * + * objectNumber-1252960222412 + * objectName-1252960222412 * */ String ns = "ns2"; - Element root = document.createElementNS(xc.getNamespaceURI(), ns + ":" + rootElementName); - root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); - -// String getSchemaLocation = xc.getSchemaLocation(); //FIXME: REM - w3c Document to DOM4j Document mangles this attribute -// root.setAttribute("xsi:schemaLocation", xc.getSchemaLocation()); - + Element root = document.createElementNS(xc.getNamespaceURI(), ns + ":" + + rootElementName); + root.setAttribute("xmlns:xsi", + "http://www.w3.org/2001/XMLSchema-instance"); + + // String getSchemaLocation = xc.getSchemaLocation(); //FIXME: REM - w3c + // Document to DOM4j Document mangles this attribute + // root.setAttribute("xsi:schemaLocation", xc.getSchemaLocation()); + String getNamespaceURI = xc.getNamespaceURI(); root.setAttribute("xmlns:" + ns, xc.getNamespaceURI()); document.appendChild(root); @@ -540,23 +567,28 @@ public class DocumentUtils { buildDocument(document, root, objectProps, schema); String w3cDocumentStr = xmlToString(document); - DOMReader reader = new DOMReader(); org.dom4j.Document dom4jDoc = reader.read(document); org.dom4j.Element result = dom4jDoc.getRootElement(); - result.detach(); //return just the root element detached from the DOM document - - return result;//FIXME: REM - Add if (logger.isTraceEnabled() == true) logger.trace(document.asXML()); + result.detach(); // return just the root element detached from the DOM + // document + + return result;// FIXME: REM - Add if (logger.isTraceEnabled() == true) + // logger.trace(document.asXML()); } /** * Builds the document. * - * @param document the document - * @param e the e - * @param objectProps the object props - * @throws Exception the exception + * @param document + * the document + * @param e + * the e + * @param objectProps + * the object props + * @throws Exception + * the exception */ public static void buildDocument(Document document, Element parent, Map objectProps, Schema schema) throws Exception { @@ -564,11 +596,14 @@ public class DocumentUtils { Object value = objectProps.get(prop); if (value != null) { Field field = schema.getField(prop); - // If there is no field, then we added this property to the properties, + // If there is no field, then we added this property to the + // properties, // and it must be a String (e.g., CSID) - // TODO - infer the type from the type of Object, if we need more than String - if(field==null) { - field = new FieldImpl(new QName(prop), schema, StringType.INSTANCE); + // TODO - infer the type from the type of Object, if we need + // more than String + if (field == null) { + field = new FieldImpl(new QName(prop), schema, + StringType.INSTANCE); } buildProperty(document, parent, field, value); } @@ -576,126 +611,144 @@ public class DocumentUtils { } /* - * String-encode Nuxeo date types (DateType) as ISO8601 timestamps. If the value passed in is not a Nuxeo date type, - * use Nuxeo's "encode" method for the type to string-encode the value. + * String-encode Nuxeo date types (DateType) as ISO8601 timestamps. If the + * value passed in is not a Nuxeo date type, use Nuxeo's "encode" method for + * the type to string-encode the value. */ private static String encodeValue(Type type, Object value) { String result = null; - + if (type != null && value != null) { if (type instanceof DateType) { Date dateResult = null; if (value instanceof Calendar) { - dateResult = ((Calendar)value).getTime(); + dateResult = ((Calendar) value).getTime(); } else if (value instanceof Date) { - dateResult = (Date)value; + dateResult = (Date) value; } else { - logger.error(String.format("Cannot encode type %s with value %s", type, value)); + logger.error(String.format( + "Cannot encode type %s with value %s", type, value)); } if (dateResult != null) { - result = GregorianCalendarDateTimeUtils.formatAsISO8601Timestamp(dateResult); + result = GregorianCalendarDateTimeUtils + .formatAsISO8601Timestamp(dateResult); } } else { - result = type.encode(value); // If it's not of type DateType, use the default mechanism to encode the value + result = type.encode(value); // If it's not of type DateType, + // use the default mechanism to + // encode the value } } - + return result; } - + /** * Builds the property. * - * @param document the document - * @param parent the parent - * @param field the field - * @param dateVal the dateVal - * @throws IOException Signals that an I/O exception has occurred. + * @param document + * the document + * @param parent + * the parent + * @param field + * the field + * @param dateVal + * the dateVal + * @throws IOException + * Signals that an I/O exception has occurred. */ - private static void buildProperty(Document document, Element parent, - Field field, Object value) throws IOException { - Type type = field.getType(); - //no need to qualify each element name as namespace is already added - String propName = field.getName().getLocalName(); - Element element = document.createElement(propName); - parent.appendChild(element); - // extract the element content - if (type.isSimpleType()) { - // Avoid returning scientific notation representations of - // very large or very small decimal values. See CSPACE-4691. - if (isNuxeoDecimalType(type) && valueMatchesNuxeoType(type, value)) { - element.setTextContent(nuxeoDecimalValueToDecimalString(value)); - /* - * We need a way to produce just a Date when the specified data - * type is an xs:date vs. xs:datetime. Nuxeo maps both to a Calendar. Sigh. - if(logger.isTraceEnabled() && isDateType(type)) { - String dateValType = "unknown"; - if (value instanceof java.util.Date) { - dateValType = "java.util.Date"; - } else if (value instanceof java.util.Calendar) { - dateValType = "java.util.Calendar"; - } - logger.trace("building XML for date type: "+type.getName() - +" value type: "+dateValType - +" encoded: "+encodedVal); - } - */ - } else { - String encodedVal = encodeValue(type, value); // get a String representation of the value - element.setTextContent(encodedVal); - } - } else if (type.isComplexType()) { - ComplexType ctype = (ComplexType) type; - if (ctype.getName().equals(TypeConstants.CONTENT)) { - throw new RuntimeException( - "Unexpected schema type: BLOB for field: "+propName); - } else { - buildComplex(document, element, ctype, (Map) value); - } - } else if (type.isListType()) { - if (value instanceof List) { - buildList(document, element, (ListType) type, (List) value); - } else if (value.getClass().getComponentType() != null) { - buildList(document, element, (ListType) type, - PrimitiveArrays.toList(value)); - } else { - throw new IllegalArgumentException( - "A value of list type is neither list neither array: " - + value); - } - } - } + private static void buildProperty(Document document, Element parent, + Field field, Object value) throws IOException { + Type type = field.getType(); + // no need to qualify each element name as namespace is already added + String propName = field.getName().getLocalName(); + Element element = document.createElement(propName); + parent.appendChild(element); + // extract the element content + if (type.isSimpleType()) { + // Avoid returning scientific notation representations of + // very large or very small decimal values. See CSPACE-4691. + if (isNuxeoDecimalType(type) && valueMatchesNuxeoType(type, value)) { + element.setTextContent(nuxeoDecimalValueToDecimalString(value)); + /* + * We need a way to produce just a Date when the specified data + * type is an xs:date vs. xs:datetime. Nuxeo maps both to a + * Calendar. Sigh. if(logger.isTraceEnabled() && + * isDateType(type)) { String dateValType = "unknown"; if (value + * instanceof java.util.Date) { dateValType = "java.util.Date"; + * } else if (value instanceof java.util.Calendar) { dateValType + * = "java.util.Calendar"; } + * logger.trace("building XML for date type: "+type.getName() + * +" value type: "+dateValType +" encoded: "+encodedVal); } + */ + } else { + String encodedVal = encodeValue(type, value); // get a String + // representation + // of the value + element.setTextContent(encodedVal); + } + } else if (type.isComplexType()) { + ComplexType ctype = (ComplexType) type; + if (ctype.getName().equals(TypeConstants.CONTENT)) { + throw new RuntimeException( + "Unexpected schema type: BLOB for field: " + propName); + } else { + buildComplex(document, element, ctype, (Map) value); + } + } else if (type.isListType()) { + if (value instanceof List) { + buildList(document, element, (ListType) type, (List) value); + } else if (value.getClass().getComponentType() != null) { + buildList(document, element, (ListType) type, + PrimitiveArrays.toList(value)); + } else { + throw new IllegalArgumentException( + "A value of list type is neither list neither array: " + + value); + } + } + } /** * Builds the complex. * - * @param document the document - * @param element the element - * @param ctype the ctype - * @param map the map - * @throws IOException Signals that an I/O exception has occurred. + * @param document + * the document + * @param element + * the element + * @param ctype + * the ctype + * @param map + * the map + * @throws IOException + * Signals that an I/O exception has occurred. */ - private static void buildComplex(Document document, Element element, + private static void buildComplex(Document document, Element element, ComplexType ctype, Map map) throws IOException { Iterator it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); String propName = entry.getKey().toString(); - buildProperty(document, element, - ctype.getField(propName), entry.getValue()); + buildProperty(document, element, ctype.getField(propName), + entry.getValue()); } } /** * Builds the list. * - * @param document the document - * @param element the element - * @param ltype the ltype - * @param list the list - * @throws IOException Signals that an I/O exception has occurred. + * @param document + * the document + * @param element + * the element + * @param ltype + * the ltype + * @param list + * the list + * @throws IOException + * Signals that an I/O exception has occurred. */ - private static void buildList(Document document, Element element, + private static void buildList(Document document, Element element, ListType ltype, List list) throws IOException { Field field = ltype.getField(); for (Object obj : list) { @@ -706,11 +759,13 @@ public class DocumentUtils { /** * Returns a schema, given the name of a schema. * - * @param schemaName a schema name. - * @return a schema. + * @param schemaName + * a schema name. + * @return a schema. */ public static Schema getSchemaFromName(String schemaName) { - SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); + SchemaManager schemaManager = Framework + .getLocalService(SchemaManager.class); return schemaManager.getSchema(schemaName); } @@ -719,17 +774,19 @@ public class DocumentUtils { * * If the schema part is null or empty, returns an empty string. * - * @param schemaQualifiedFieldName a schema-qualified field name. - * @return the schema part of the field name. + * @param schemaQualifiedFieldName + * a schema-qualified field name. + * @return the schema part of the field name. */ - //FIXME: Might instead use Nuxeo's built-in QName class. + // FIXME: Might instead use Nuxeo's built-in QName class. public static String getSchemaNamePart(String schemaQualifiedFieldName) { - if (schemaQualifiedFieldName == null || schemaQualifiedFieldName.trim().isEmpty()) { + if (schemaQualifiedFieldName == null + || schemaQualifiedFieldName.trim().isEmpty()) { return ""; } if (schemaQualifiedFieldName.indexOf(SCHEMA_FIELD_DELIMITER) > 0) { - String[] schemaQualifiedFieldNameParts = - schemaQualifiedFieldName.split(SCHEMA_FIELD_DELIMITER); + String[] schemaQualifiedFieldNameParts = schemaQualifiedFieldName + .split(SCHEMA_FIELD_DELIMITER); String schemaName = schemaQualifiedFieldNameParts[0]; return schemaName; } else { @@ -738,15 +795,17 @@ public class DocumentUtils { } /** - * Returns a list of delimited strings, by splitting the supplied string - * on a supplied delimiter. + * Returns a list of delimited strings, by splitting the supplied string on + * a supplied delimiter. * - * @param str A string to split on a delimiter. - * @param delmiter A delimiter on which the string will be split into parts. + * @param str + * A string to split on a delimiter. + * @param delmiter + * A delimiter on which the string will be split into parts. * - * @return A list of delimited strings. Returns an empty list if either - * the string or delimiter are null or empty, or if the delimiter cannot - * be found in the string. + * @return A list of delimited strings. Returns an empty list if either the + * string or delimiter are null or empty, or if the delimiter cannot + * be found in the string. */ public static List getDelimitedParts(String str, String delimiter) { List parts = new ArrayList(); @@ -766,11 +825,13 @@ public class DocumentUtils { /** * Gets the ancestor auth ref field name. * - * @param str the str + * @param str + * the str * @return the ancestor auth ref field name */ public static String getAncestorAuthRefFieldName(String str) { - List parts = getDelimitedParts(str, AUTHREF_FIELD_NAME_DELIMITER); + List parts = getDelimitedParts(str, + AUTHREF_FIELD_NAME_DELIMITER); if (parts.size() > 0) { return parts.get(0).trim(); } else { @@ -781,11 +842,13 @@ public class DocumentUtils { /** * Gets the descendant auth ref field name. * - * @param str the str + * @param str + * the str * @return the descendant auth ref field name */ public static String getDescendantAuthRefFieldName(String str) { - List parts = getDelimitedParts(str, AUTHREF_FIELD_NAME_DELIMITER); + List parts = getDelimitedParts(str, + AUTHREF_FIELD_NAME_DELIMITER); if (parts.size() > 1) { return parts.get(1).trim(); } else { @@ -795,20 +858,20 @@ public class DocumentUtils { /** * Returns the relevant authRef field name from a fieldName, which may - * potentially be in the form of a single field name, or a delimited pair - * of field names, that in turn consists of an ancestor field name and a + * potentially be in the form of a single field name, or a delimited pair of + * field names, that in turn consists of an ancestor field name and a * descendant field name. * * If a delimited pair of names is provided, will return the descendant - * field name from that pair, if present. Otherwise, will return the + * field name from that pair, if present. Otherwise, will return the * ancestor name from that pair. * - * Will return the relevant authRef field name as schema-qualified - * (i.e. schema prefixed), if the schema name was present, either in - * the supplied simple field name or in the ancestor name in the - * delimited pair of names. + * Will return the relevant authRef field name as schema-qualified (i.e. + * schema prefixed), if the schema name was present, either in the supplied + * simple field name or in the ancestor name in the delimited pair of names. * - * @param fieldNameOrNames A field name or delimited pair of field names. + * @param fieldNameOrNames + * A field name or delimited pair of field names. * * @return The relevant authRef field name, as described. */ @@ -818,29 +881,32 @@ public class DocumentUtils { return fName; } String descendantAuthRefFieldName = getDescendantAuthRefFieldName(fieldNameOrNames); - if (descendantAuthRefFieldName != null && !descendantAuthRefFieldName.trim().isEmpty()) { + if (descendantAuthRefFieldName != null + && !descendantAuthRefFieldName.trim().isEmpty()) { fName = descendantAuthRefFieldName; } else { fName = getAncestorAuthRefFieldName(fieldNameOrNames); } if (getSchemaNamePart(fName).isEmpty()) { String schemaName = getSchemaNamePart(getAncestorAuthRefFieldName(fieldNameOrNames)); - if (! schemaName.trim().isEmpty()) { + if (!schemaName.trim().isEmpty()) { fName = appendSchemaName(schemaName, fName); } } return fName; } - /** - * Returns a schema-qualified field name, given a schema name and field name. + * Returns a schema-qualified field name, given a schema name and field + * name. * * If the schema name is null or empty, returns the supplied field name. * - * @param schemaName a schema name. - * @param fieldName a field name. - * @return a schema-qualified field name. + * @param schemaName + * a schema name. + * @param fieldName + * a field name. + * @return a schema-qualified field name. */ public static String appendSchemaName(String schemaName, String fieldName) { if (schemaName == null || schemaName.trim().isEmpty()) { @@ -852,7 +918,8 @@ public class DocumentUtils { /** * Checks if is simple type. * - * @param prop the prop + * @param prop + * the prop * @return true, if is simple type */ public static boolean isSimpleType(Property prop) { @@ -869,11 +936,13 @@ public class DocumentUtils { /** * Checks if is list type. * - * @param prop the prop + * @param prop + * the prop * @return true, if is list type */ public static boolean isListType(Property prop) { - // TODO simplify this to return (prop!=null && prop.getType().isListType()); + // TODO simplify this to return (prop!=null && + // prop.getType().isListType()); boolean isList = false; if (prop == null) { return isList; @@ -887,11 +956,13 @@ public class DocumentUtils { /** * Checks if is complex type. * - * @param prop the prop + * @param prop + * the prop * @return true, if is complex type */ public static boolean isComplexType(Property prop) { - // TODO simplify this to return (prop!=null && prop.getType().isComplexType()); + // TODO simplify this to return (prop!=null && + // prop.getType().isComplexType()); boolean isComplex = false; if (prop == null) { return isComplex; @@ -901,315 +972,311 @@ public class DocumentUtils { } return isComplex; } - - /* - * Identifies whether a property type is a Nuxeo decimal type. - * - * Note: currently, elements declared as xs:decimal in XSD schemas - * are handled as the Nuxeo primitive DoubleType. If this type - * correspondence changes, the test below should be changed accordingly. - * - * @param type a type. - * @return true, if is a Nuxeo decimal type; - * false, if it is not a Nuxeo decimal type. - */ - private static boolean isNuxeoDecimalType(Type type) { - return ((SimpleType)type).getPrimitiveType() instanceof DoubleType; - } - - /** - * Obtains a String representation of a Nuxeo property value, where - * the latter is an opaque Object that may or may not be directly - * convertible to a string. - * - * @param obj an Object containing a property value - * @param docModel the document model associated with this property. - * @param propertyPath a path to the property, such as a property name, XPath, etc. - * @return a String representation of the Nuxeo property value. - */ - static public String propertyValueAsString(Object obj, DocumentModel docModel, String propertyPath) { - if (obj == null) { - return ""; - } - if (String.class.isAssignableFrom(obj.getClass())) { - return (String)obj; - } else { - // Handle cases where a property value returned from the repository - // can't be directly cast to a String. - // - // FIXME: This method provides specific, hard-coded formatting - // for String representations of property values. We might want - // to add the ability to specify these formats via configuration. - // - ADR 2013-04-26 - if (obj instanceof GregorianCalendar) { - return GregorianCalendarDateTimeUtils.formatAsISO8601Date((GregorianCalendar)obj); - } else if (obj instanceof Double) { - return nuxeoDecimalValueToDecimalString(obj); - } else if (obj instanceof Long) { - return nuxeoLongValueToString(obj); - } else if (obj instanceof Boolean) { - return String.valueOf(obj); - } else { - logger.warn("Could not convert value of property " + propertyPath - + " in document " + docModel.getPathAsString() + " to a String."); - logger.warn("This may be due to a new, as-yet-unhandled datatype returned from the repository"); - return ""; - } - } - } - - /* - * Returns a string representation of the value of a Nuxeo decimal type. - * - * Note: currently, elements declared as xs:decimal in XSD schemas - * are handled as the Nuxeo primitive DoubleType, and their values are - * convertible into the Java Double type. If this type correspondence - * changes, the conversion below should be changed accordingly, as - * should any 'instanceof' or similar checks elsewhere in this code. - * - * @return a string representation of the value of a Nuxeo decimal type. - * An empty string is returned if the value cannot be cast to the - * appropriate type. - */ - private static String nuxeoDecimalValueToDecimalString(Object value) { - Double doubleVal; - try { - doubleVal = (Double) value; - } catch (ClassCastException cce) { - logger.warn("Could not convert a Nuxeo decimal value to its string equivalent: " - + cce.getMessage()); - return ""; - } - // FIXME: Without a Locale supplied as an argument, NumberFormat will - // use the decimal separator and other numeric formatting conventions - // for the current default Locale. In some Locales, this could - // potentially result in returning values that might not be capable - // of being round-tripped; this should be invetigated. Alternately, - // we might standardize on a particular locale whose values are known - // to be capable of also being ingested on return. - ADR 2012-08-07 - NumberFormat formatter = NumberFormat.getInstance(); - if (formatter instanceof DecimalFormat) { - // This pattern allows up to 15 decimal digits, and will prepend - // a '0' if only fractional digits are included in the value. - ((DecimalFormat) formatter).applyPattern("0.###############"); - } - return formatter.format(doubleVal.doubleValue()); - } - - /* - * Returns a string representation of the value of a Nuxeo long type. - * - * Note: currently, elements declared as xs:integer in XSD schemas - * are handled as the Nuxeo primitive LongType, and their values are - * convertible into the Java Long type. If this type correspondence - * changes, the conversion below should be changed accordingly, as - * should any 'instanceof' or similar checks elsewhere in this code. - * - * @return a string representation of the value of a Nuxeo long type. - * An empty string is returned if the value cannot be cast to the - * appropriate type. - */ - private static String nuxeoLongValueToString(Object value) { - Long longVal; - try { - longVal = (Long) value; - } catch (ClassCastException cce) { - logger.warn("Could not convert a Nuxeo integer value to its string equivalent: " - + cce.getMessage()); - return ""; - } - return longVal.toString(); - } - - /* - * 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 isNuxeoDateType(Type type) { - return ((SimpleType)type).getPrimitiveType() instanceof DateType; - } - - private static boolean valueMatchesNuxeoType(Type type, Object value) { - try { - return type.validate(value); - } catch (TypeException te) { - return false; - } - } - + + /* + * Identifies whether a property type is a Nuxeo decimal type. + * + * Note: currently, elements declared as xs:decimal in XSD schemas are + * handled as the Nuxeo primitive DoubleType. If this type correspondence + * changes, the test below should be changed accordingly. + * + * @param type a type. + * + * @return true, if is a Nuxeo decimal type; false, if it is not a Nuxeo + * decimal type. + */ + private static boolean isNuxeoDecimalType(Type type) { + return ((SimpleType) type).getPrimitiveType() instanceof DoubleType; + } + + /** + * Obtains a String representation of a Nuxeo property value, where the + * latter is an opaque Object that may or may not be directly convertible to + * a string. + * + * @param obj + * an Object containing a property value + * @param docModel + * the document model associated with this property. + * @param propertyPath + * a path to the property, such as a property name, XPath, etc. + * @return a String representation of the Nuxeo property value. + */ + static public String propertyValueAsString(Object obj, + DocumentModel docModel, String propertyPath) { + if (obj == null) { + return ""; + } + if (String.class.isAssignableFrom(obj.getClass())) { + return (String) obj; + } else { + // Handle cases where a property value returned from the repository + // can't be directly cast to a String. + // + // FIXME: This method provides specific, hard-coded formatting + // for String representations of property values. We might want + // to add the ability to specify these formats via configuration. + // - ADR 2013-04-26 + if (obj instanceof GregorianCalendar) { + return GregorianCalendarDateTimeUtils + .formatAsISO8601Date((GregorianCalendar) obj); + } else if (obj instanceof Double) { + return nuxeoDecimalValueToDecimalString(obj); + } else if (obj instanceof Long) { + return nuxeoLongValueToString(obj); + } else if (obj instanceof Boolean) { + return String.valueOf(obj); + } else { + logger.warn("Could not convert value of property " + + propertyPath + " in document " + + docModel.getPathAsString() + " to a String."); + logger.warn("This may be due to a new, as-yet-unhandled datatype returned from the repository"); + return ""; + } + } + } + + /* + * Returns a string representation of the value of a Nuxeo decimal type. + * + * Note: currently, elements declared as xs:decimal in XSD schemas are + * handled as the Nuxeo primitive DoubleType, and their values are + * convertible into the Java Double type. If this type correspondence + * changes, the conversion below should be changed accordingly, as should + * any 'instanceof' or similar checks elsewhere in this code. + * + * @return a string representation of the value of a Nuxeo decimal type. An + * empty string is returned if the value cannot be cast to the appropriate + * type. + */ + private static String nuxeoDecimalValueToDecimalString(Object value) { + Double doubleVal; + try { + doubleVal = (Double) value; + } catch (ClassCastException cce) { + logger.warn("Could not convert a Nuxeo decimal value to its string equivalent: " + + cce.getMessage()); + return ""; + } + // FIXME: Without a Locale supplied as an argument, NumberFormat will + // use the decimal separator and other numeric formatting conventions + // for the current default Locale. In some Locales, this could + // potentially result in returning values that might not be capable + // of being round-tripped; this should be invetigated. Alternately, + // we might standardize on a particular locale whose values are known + // to be capable of also being ingested on return. - ADR 2012-08-07 + NumberFormat formatter = NumberFormat.getInstance(); + if (formatter instanceof DecimalFormat) { + // This pattern allows up to 15 decimal digits, and will prepend + // a '0' if only fractional digits are included in the value. + ((DecimalFormat) formatter).applyPattern("0.###############"); + } + return formatter.format(doubleVal.doubleValue()); + } + + /* + * Returns a string representation of the value of a Nuxeo long type. + * + * Note: currently, elements declared as xs:integer in XSD schemas are + * handled as the Nuxeo primitive LongType, and their values are convertible + * into the Java Long type. If this type correspondence changes, the + * conversion below should be changed accordingly, as should any + * 'instanceof' or similar checks elsewhere in this code. + * + * @return a string representation of the value of a Nuxeo long type. An + * empty string is returned if the value cannot be cast to the appropriate + * type. + */ + private static String nuxeoLongValueToString(Object value) { + Long longVal; + try { + longVal = (Long) value; + } catch (ClassCastException cce) { + logger.warn("Could not convert a Nuxeo integer value to its string equivalent: " + + cce.getMessage()); + return ""; + } + return longVal.toString(); + } + + /* + * 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 isNuxeoDateType(Type type) { + return ((SimpleType) type).getPrimitiveType() instanceof DateType; + } + + private static boolean valueMatchesNuxeoType(Type type, Object value) { + try { + return type.validate(value); + } catch (TypeException te) { + return false; + } + } + /** * Insert multi values. * - * @param document the document - * @param e the e - * @param vals the vals - private static void insertMultiStringValues(Document document, Element e, ArrayList vals) { - String parentName = e.getNodeName(); - - for (String o : vals) { - String val = o; - NameValue nv = unqualify(val); - Element c = document.createElement(nv.name); - e.appendChild(c); - insertTextNode(document, c, nv.dateVal); - } - } + * @param document + * the document + * @param e + * the e + * @param vals + * the vals private static void insertMultiStringValues(Document + * document, Element e, ArrayList vals) { String + * parentName = e.getNodeName(); + * + * for (String o : vals) { String val = o; NameValue nv = + * unqualify(val); Element c = document.createElement(nv.name); + * e.appendChild(c); insertTextNode(document, c, nv.dateVal); } } */ /** * Create a set of complex/structured elements from an array of Maps. * - * @param document the document - * @param e the e - * @param vals the vals - * @throws Exception the exception - private static void insertMultiHashMapValues(Document document, Element e, ArrayList> vals) - throws Exception { - String parentName = e.getNodeName(); - String childName = null; - // - // By convention, elements with a structured/complex type should have a parent element with a name ending with the suffix - // STRUCTURED_TYPE_SUFFIX. We synthesize the element name for the structured type by stripping the suffix from the parent name. - // For example, - // - if (parentName.endsWith(STRUCTURED_TYPE_SUFFIX) == true) { - int parentNameLen = parentName.length(); - childName = parentName.substring(0, parentNameLen - STRUCTURED_TYPE_SUFFIX.length()); - } else { - String msg = "Unrecognized parent element name. Elements with complex/structured " + - "types should have a parent element whose name ends with '" + - STRUCTURED_TYPE_SUFFIX + "'."; - if (logger.isErrorEnabled() == true) { - logger.error(msg); - } - throw new Exception(msg); - } - - for (Map map : vals) { - Element newNode = document.createElement(childName); - e.appendChild(newNode); - buildDocument(document, newNode, map); - } - } + * @param document + * the document + * @param e + * the e + * @param vals + * the vals + * @throws Exception + * the exception private static void + * insertMultiHashMapValues(Document document, Element e, + * ArrayList> vals) throws Exception { + * String parentName = e.getNodeName(); String childName = null; + * // // By convention, elements with a structured/complex type + * should have a parent element with a name ending with the + * suffix // STRUCTURED_TYPE_SUFFIX. We synthesize the element + * name for the structured type by stripping the suffix from the + * parent name. // For example, + * + * // if + * (parentName.endsWith(STRUCTURED_TYPE_SUFFIX) == true) { int + * parentNameLen = parentName.length(); childName = + * parentName.substring(0, parentNameLen - + * STRUCTURED_TYPE_SUFFIX.length()); } else { String msg = + * "Unrecognized parent element name. Elements with complex/structured " + * + "types should have a parent element whose name ends with '" + * + STRUCTURED_TYPE_SUFFIX + "'."; if (logger.isErrorEnabled() + * == true) { logger.error(msg); } throw new Exception(msg); } + * + * for (Map map : vals) { Element newNode = + * document.createElement(childName); e.appendChild(newNode); + * buildDocument(document, newNode, map); } } */ /** - * Create a set of elements for an array of values. Currently, we're assuming the - * values can be only of type Map or String. + * Create a set of elements for an array of values. Currently, we're + * assuming the values can be only of type Map or String. * - * @param document the document - * @param e the e - * @param vals the vals - * @throws Exception the exception - private static void insertMultiValues(Document document, Element e, ArrayList vals) - throws Exception { - if (vals != null && vals.size() > 0) { - Object firstElement = vals.get(0); - if (firstElement instanceof String) { - insertMultiStringValues(document, e, (ArrayList)vals); - } else if (firstElement instanceof Map) { - insertMultiHashMapValues(document, e, (ArrayList>)vals); - } else { - String msg = "Unbounded elements need to be arrays of either Strings or Maps. We encountered an array of " + - firstElement.getClass().getName(); - if (logger.isErrorEnabled() == true) { - logger.error(msg); - } - throw new Exception(msg); - } - } - } + * @param document + * the document + * @param e + * the e + * @param vals + * the vals + * @throws Exception + * the exception private static void insertMultiValues(Document + * document, Element e, ArrayList vals) throws Exception { if + * (vals != null && vals.size() > 0) { Object firstElement = + * vals.get(0); if (firstElement instanceof String) { + * insertMultiStringValues(document, e, + * (ArrayList)vals); } else if (firstElement instanceof + * Map) { insertMultiHashMapValues(document, e, + * (ArrayList>)vals); } else { String msg = + * "Unbounded elements need to be arrays of either Strings or Maps. We encountered an array of " + * + firstElement.getClass().getName(); if + * (logger.isErrorEnabled() == true) { logger.error(msg); } + * throw new Exception(msg); } } } */ /** * Insert text node. * - * @param document the document - * @param e the e - * @param strValue the str dateVal - private static void insertTextNode(Document document, Element e, String strValue) { - Text tNode = document.createTextNode(strValue); - e.appendChild(tNode); - } + * @param document + * the document + * @param e + * the e + * @param strValue + * the str dateVal private static void insertTextNode(Document + * document, Element e, String strValue) { Text tNode = + * document.createTextNode(strValue); e.appendChild(tNode); } */ /** - * unqualify given dateVal. - * input of otherNumber|urn:org.collectionspace.id:24082390 would be unqualified - * as name=otherNumber and dateVal=urn:org.collectionspace.id:24082390 + * unqualify given dateVal. input of + * otherNumber|urn:org.collectionspace.id:24082390 would be unqualified as + * name=otherNumber and dateVal=urn:org.collectionspace.id:24082390 + * * @param input * @return name and dateVal * @exception IllegalStateException - private static NameValue unqualify(String input) { - NameValue nv = new NameValue(); - StringTokenizer stz = new StringTokenizer(input, NAME_VALUE_SEPARATOR); - int tokens = stz.countTokens(); - if (tokens == 2) { - nv.name = stz.nextToken(); - nv.dateVal = stz.nextToken(); - // Allow null or empty values - } else if (tokens == 1) { - nv.name = stz.nextToken(); - nv.dateVal = ""; - } else { - throw new IllegalStateException("Unexpected format for multi valued element: " + input); - } - return nv; - } + * private static NameValue unqualify(String input) { + * NameValue nv = new NameValue(); StringTokenizer stz = new + * StringTokenizer(input, NAME_VALUE_SEPARATOR); int tokens = + * stz.countTokens(); if (tokens == 2) { nv.name = + * stz.nextToken(); nv.dateVal = stz.nextToken(); // Allow + * null or empty values } else if (tokens == 1) { nv.name = + * stz.nextToken(); nv.dateVal = ""; } else { throw new + * IllegalStateException + * ("Unexpected format for multi valued element: " + input); + * } return nv; } */ - - public static String getFirstString(Object list) { - if (list==null) { - return null; - } - if (list instanceof List) { - return ((List)list).size()==0?null:(String)((List)list).get(0); + + public static String getFirstString(Object list) { + if (list == null) { + return null; } - Class arrType = list.getClass().getComponentType(); - if ((arrType != null) && arrType.isPrimitive()) { - if (arrType == Integer.TYPE) { - int[] ar = (int[]) list; - return ar.length==0?null:String.valueOf(ar[0]); - } else if (arrType == Long.TYPE) { - long[] ar = (long[]) list; - return ar.length==0?null:String.valueOf(ar[0]); - } else if (arrType == Double.TYPE) { - double[] ar = (double[]) list; - return ar.length==0?null:String.valueOf(ar[0]); - } else if (arrType == Float.TYPE) { - float[] ar = (float[]) list; - return ar.length==0?null:String.valueOf(ar[0]); - } else if (arrType == Character.TYPE) { - char[] ar = (char[]) list; - return ar.length==0?null:String.valueOf(ar[0]); - } else if (arrType == Byte.TYPE) { - byte[] ar = (byte[]) list; - return ar.length==0?null:String.valueOf(ar[0]); - } else if (arrType == Short.TYPE) { - short[] ar = (short[]) list; - return ar.length==0?null:String.valueOf(ar[0]); - } - throw new IllegalArgumentException( - "Primitive list of unsupported type: " - + list); + if (list instanceof List) { + return ((List) list).size() == 0 ? null : (String) ((List) list) + .get(0); + } + Class arrType = list.getClass().getComponentType(); + if ((arrType != null) && arrType.isPrimitive()) { + if (arrType == Integer.TYPE) { + int[] ar = (int[]) list; + return ar.length == 0 ? null : String.valueOf(ar[0]); + } else if (arrType == Long.TYPE) { + long[] ar = (long[]) list; + return ar.length == 0 ? null : String.valueOf(ar[0]); + } else if (arrType == Double.TYPE) { + double[] ar = (double[]) list; + return ar.length == 0 ? null : String.valueOf(ar[0]); + } else if (arrType == Float.TYPE) { + float[] ar = (float[]) list; + return ar.length == 0 ? null : String.valueOf(ar[0]); + } else if (arrType == Character.TYPE) { + char[] ar = (char[]) list; + return ar.length == 0 ? null : String.valueOf(ar[0]); + } else if (arrType == Byte.TYPE) { + byte[] ar = (byte[]) list; + return ar.length == 0 ? null : String.valueOf(ar[0]); + } else if (arrType == Short.TYPE) { + short[] ar = (short[]) list; + return ar.length == 0 ? null : String.valueOf(ar[0]); + } + throw new IllegalArgumentException( + "Primitive list of unsupported type: " + list); } throw new IllegalArgumentException( - "A value of list type is neither list neither array: " - + list); - } + "A value of list type is neither list neither array: " + list); + } /** * writeDocument streams out given document to given output stream + * * @param document * @param os * @throws Exception */ - public static void writeDocument(Document document, OutputStream os) throws Exception { - TransformerFactory tFactory = - TransformerFactory.newInstance(); + public static void writeDocument(Document document, OutputStream os) + throws Exception { + TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer transformer = tFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); DOMSource source = new DOMSource(document); @@ -1220,7 +1287,8 @@ public class DocumentUtils { /** * Xml to string. * - * @param node the node + * @param node + * the node * @return the string */ public static String xmlToString(Node node) { @@ -1241,10 +1309,11 @@ public class DocumentUtils { } return result; - } + } /** * getXmlDocoument retrieve w3c.Document from given file + * * @param fileName * @return Document * @throws Exception @@ -1253,21 +1322,25 @@ public class DocumentUtils { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); File f = new File(fileName); if (!f.exists()) { - throw new IllegalArgumentException("Test data file " + fileName + " not found!"); + throw new IllegalArgumentException("Test data file " + fileName + + " not found!"); } // Create the builder and parse the file return factory.newDocumentBuilder().parse(f); } - + // - // Added from Nuxeo sources for creating new DocumentModel from an XML payload + // Added from Nuxeo sources for creating new DocumentModel from an XML + // payload // - + /** * Parses the properties. * - * @param partMeta the part meta - * @param document the document + * @param partMeta + * the part meta + * @param document + * the document * @return the map */ public static Map parseProperties(ObjectPartType partMeta, @@ -1276,13 +1349,13 @@ public class DocumentUtils { 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); + // org.dom4j.io.DOMReader xmlReader = new org.dom4j.io.DOMReader(); + // org.dom4j.Document dom4jDocument = xmlReader.read(document); try { - // result = loadSchema(schema, dom4jDocument.getRootElement(), ctx); + // result = loadSchema(schema, dom4jDocument.getRootElement(), ctx); result = loadSchema(schema, document, ctx); - } catch (IllegalArgumentException iae) { - throw iae; + } catch (IllegalArgumentException iae) { + throw iae; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -1290,26 +1363,33 @@ public class DocumentUtils { return result; } - - public static Map parseProperties(String schemaName, org.dom4j.Element element, ServiceContext ctx) throws Exception { + + public static Map parseProperties(String schemaName, + org.dom4j.Element element, ServiceContext ctx) throws Exception { Map result = null; Schema schema = getSchemaFromName(schemaName); result = DocumentUtils.loadSchema(schema, element, ctx); return result; } - + /** * Load schema. * - * @param schema the schema - * @param schemaElement the schema element + * @param schema + * the schema + * @param schemaElement + * the schema element * @return the map - * @throws Exception the exception + * @throws Exception + * the exception */ @SuppressWarnings("unchecked") - static private Map loadSchema(Schema schema, org.dom4j.Element schemaElement, ServiceContext ctx) - throws Exception { - String schemaName1 = schemaElement.attributeValue(ExportConstants.NAME_ATTR);//FIXME: Do we need this local var? + static private Map loadSchema(Schema schema, + org.dom4j.Element schemaElement, ServiceContext ctx) + throws Exception { + String schemaName1 = schemaElement + .attributeValue(ExportConstants.NAME_ATTR);// FIXME: Do we need + // this local var? String schemaName = schema.getName(); Map data = new HashMap(); @@ -1321,13 +1401,16 @@ public class DocumentUtils { if (field != null) { Object value = getElementData(element, field.getType(), ctx); data.put(name, value); - } else { - // FIXME: substitute an appropriate constant for "csid" below. - // One potential class to which to add that constant, if it is not already - // declared, might be AbstractCollectionSpaceResourceImpl - ADR 2012-09-24 - if (! name.equals("csid")) { // 'csid' elements in input payloads can be safely ignored. - logger.warn("Invalid input document. No such property was found [" + - name + "] in schema " + schemaName); + } else { + // FIXME: substitute an appropriate constant for "csid" below. + // One potential class to which to add that constant, if it is + // not already + // declared, might be AbstractCollectionSpaceResourceImpl - ADR + // 2012-09-24 + if (!name.equals("csid")) { // 'csid' elements in input payloads + // can be safely ignored. + logger.warn("Invalid input document. No such property was found [" + + name + "] in schema " + schemaName); } } } @@ -1338,46 +1421,54 @@ public class DocumentUtils { /** * Gets the element data. * - * @param element the element - * @param type the type + * @param element + * the element + * @param type + * the type * @return the element data */ @SuppressWarnings("unchecked") static private Object getElementData(org.dom4j.Element element, Type type, - ServiceContext ctx) throws Exception { + ServiceContext ctx) throws Exception { Object result = null; - String dateStr = ""; - + String dateStr = ""; + if (type.isSimpleType()) { - if (isNuxeoDateType(type)) { - String dateVal = element.getText(); - if (dateVal == null || dateVal.trim().isEmpty()) { - result = type.decode(""); - } else { - // Dates or date/times in any ISO 8601-based representations - // directly supported by Nuxeo will be successfully decoded. - result = type.decode(dateVal); - // All other date or date/time values must first be converted - // to a supported ISO 8601-based representation. - if (result == null) { - dateStr = DateTimeFormatUtils.toIso8601Timestamp(dateVal, - ctx.getTenantId()); - if (dateStr != null) { - result = type.decode(dateStr); - } else { - throw new IllegalArgumentException("Unrecognized date value '" - + dateVal + "' in field '" + element.getName() + "'"); - } - } - } - } else { - String textValue = element.getText(); - if (textValue != null && textValue.trim().isEmpty()) { - result = null; - } else { - result = type.decode(textValue); - } - } + if (isNuxeoDateType(type)) { + String dateVal = element.getText(); + if (dateVal == null || dateVal.trim().isEmpty()) { + result = type.decode(""); + } else { + // Dates or date/times in any ISO 8601-based representations + // directly supported by Nuxeo will be successfully decoded. + try { + result = type.decode(dateVal); + } catch (Exception e) { // Nuxeo may not be able to decode dates like "July 11, 2001", so we'll try to convert it to ISO 8601 first + Log.debug(String.format( + "Nuxeo could not decode date string '%s'. CSpace will try to convert it to an ISO 8601 timestamp for Nuxeo first.", dateVal), e); + } + // All other date or date/time values must first converted + // to a supported ISO 8601-based representation. + if (result == null) { + dateStr = DateTimeFormatUtils.toIso8601Timestamp(dateVal, ctx.getTenantId()); + if (dateStr != null) { + result = type.decode(dateStr); + } else { + throw new IllegalArgumentException( + "Unrecognized date value '" + dateVal + + "' in field '" + + element.getName() + "'"); + } + } + } + } else { + String textValue = element.getText(); + if (textValue != null && textValue.trim().isEmpty()) { + result = null; + } else { + result = type.decode(textValue); + } + } } else if (type.isListType()) { ListType ltype = (ListType) type; List list = new ArrayList(); @@ -1392,45 +1483,50 @@ public class DocumentUtils { if (klass.isPrimitive()) { return PrimitiveArrays.toPrimitiveArray(list, klass); } else { - return list.toArray((Object[])Array.newInstance(klass, list.size())); + return list.toArray((Object[]) Array.newInstance(klass, + list.size())); } } result = list; } else { ComplexType ctype = (ComplexType) type; if (ctype.getName().equals(TypeConstants.CONTENT)) { -// String mimeType = element.elementText(ExportConstants.BLOB_MIME_TYPE); -// String encoding = element.elementText(ExportConstants.BLOB_ENCODING); -// String content = element.elementTextTrim(ExportConstants.BLOB_DATA); -// if ((content == null || content.length() == 0) -// && (mimeType == null || mimeType.length() == 0)) { -// return null; // remove blob -// } -// Blob blob = null; -// if (xdoc.hasExternalBlobs()) { -// blob = xdoc.getBlob(content); -// } -// if (blob == null) { // may be the blob is embedded like a Base64 -// // encoded data -// byte[] bytes = Base64.decode(content); -// blob = new StreamingBlob(new ByteArraySource(bytes)); -// } -// blob.setMimeType(mimeType); -// blob.setEncoding(encoding); -// return blob; + // String mimeType = + // element.elementText(ExportConstants.BLOB_MIME_TYPE); + // String encoding = + // element.elementText(ExportConstants.BLOB_ENCODING); + // String content = + // element.elementTextTrim(ExportConstants.BLOB_DATA); + // if ((content == null || content.length() == 0) + // && (mimeType == null || mimeType.length() == 0)) { + // return null; // remove blob + // } + // Blob blob = null; + // if (xdoc.hasExternalBlobs()) { + // blob = xdoc.getBlob(content); + // } + // if (blob == null) { // may be the blob is embedded like a + // Base64 + // // encoded data + // byte[] bytes = Base64.decode(content); + // blob = new StreamingBlob(new ByteArraySource(bytes)); + // } + // blob.setMimeType(mimeType); + // blob.setEncoding(encoding); + // return blob; } else { // a complex type Map map = new HashMap(); Iterator it = element.elementIterator(); while (it.hasNext()) { org.dom4j.Element el = it.next(); String name = el.getName(); - Object value = getElementData(el, ctype.getField( - el.getName()).getType(), ctx); + Object value = getElementData(el, + ctype.getField(el.getName()).getType(), ctx); map.put(name, value); } result = map; } - } + } return result; - } + } }