From: Aron Roberts Date: Fri, 30 Jul 2010 23:48:30 +0000 (+0000) Subject: CSPACE-2558,CSPACE-2556: When returning lists of authority references (terms used... X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=9fa977a16b547731e30385589c39bfea8e152ee0;p=tmp%2Fjakarta-migration.git CSPACE-2558,CSPACE-2556: When returning lists of authority references (terms used) within a record, the sourceField name is once again correct for value instances of singly repeatable fields, rather than reflecting the parent field's name. This check-in assumes one level of repeatability (a parent field with 0-n child fields of the same name and type) and will require revision for complex, or nested, repeatability, but also lays the groundwork for such support. --- diff --git a/services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandlerImpl.java b/services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandlerImpl.java index 44041b891..e281c4c01 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandlerImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandlerImpl.java @@ -373,7 +373,9 @@ public abstract class AbstractDocumentHandlerImpl @Override public abstract String getQProperty(String prop); - /* (non-Javadoc) + /* + * Strip Nuxeo's schema name from the start of the field / element name. + * (non-Javadoc) * @see org.collectionspace.services.common.document.DocumentHandler#getUnQProperty(java.lang.String) */ @Override 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 cff812ba5..b05c43d38 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 @@ -473,8 +473,7 @@ public class DocumentUtils { root.setAttribute("xmlns:" + ns, xc.getNamespaceURI()); document.appendChild(root); - SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); - Schema schema = schemaManager.getSchema(partMeta.getLabel()); + Schema schema = getSchema(partMeta.getLabel()); buildDocument(document, root, objectProps, schema); @@ -557,6 +556,11 @@ public class DocumentUtils { } } + public static Schema getSchema(String label) { + SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); + return schemaManager.getSchema(label); + } + /** * Insert multi values. diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java index 51b8fc4e6..ed91ad635 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java @@ -50,9 +50,23 @@ import org.collectionspace.services.common.vocabulary.RefNameUtils; import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; + import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.model.PropertyException; + +import org.nuxeo.ecm.core.schema.SchemaManager; +import org.nuxeo.ecm.core.schema.TypeConstants; +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.nuxeo.ecm.core.schema.types.primitives.StringType; +import org.nuxeo.ecm.core.schema.types.FieldImpl; +import org.nuxeo.ecm.core.schema.types.QName; +import org.nuxeo.runtime.api.Framework; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -76,11 +90,11 @@ public abstract class RemoteDocumentModelHandlerImpl */ @Override public void setServiceContext(ServiceContext ctx) { //FIXME: Apply proper generics to ServiceContext - if(ctx instanceof MultipartServiceContext){ + if (ctx instanceof MultipartServiceContext) { super.setServiceContext(ctx); - }else{ - throw new IllegalArgumentException("setServiceContext requires instance of " + - MultipartServiceContext.class.getName()); + } else { + throw new IllegalArgumentException("setServiceContext requires instance of " + + MultipartServiceContext.class.getName()); } } @@ -94,12 +108,12 @@ public abstract class RemoteDocumentModelHandlerImpl Map partsMetaMap = getServiceContext().getPartsMetadata(); MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext(); List inputParts = ctx.getInput().getParts(); - for(InputPart part : inputParts){ + for (InputPart part : inputParts) { String partLabel = part.getHeaders().getFirst("label"); ObjectPartType partMeta = partsMetaMap.get(partLabel); // extractPart(docModel, partLabel, partMeta); - Map unQObjectProperties = extractPart(docModel, partLabel, partMeta); - addOutputPart(unQObjectProperties, partLabel, partMeta); + Map unQObjectProperties = extractPart(docModel, partLabel, partMeta); + addOutputPart(unQObjectProperties, partLabel, partMeta); } } @@ -112,16 +126,16 @@ public abstract class RemoteDocumentModelHandlerImpl * @throws Exception the exception */ private void addOutputPart(Map unQObjectProperties, String schema, ObjectPartType partMeta) - throws Exception { - Document doc = DocumentUtils.buildDocument(partMeta, schema, - unQObjectProperties); - if (logger.isDebugEnabled() == true) { - logger.debug(DocumentUtils.xmlToString(doc)); - } - MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext(); - ctx.addOutputPart(schema, doc, partMeta.getContent().getContentType()); - } - + throws Exception { + Document doc = DocumentUtils.buildDocument(partMeta, schema, + unQObjectProperties); + if (logger.isDebugEnabled() == true) { + logger.debug(DocumentUtils.xmlToString(doc)); + } + MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext(); + ctx.addOutputPart(schema, doc, partMeta.getContent().getContentType()); + } + /** * Extract paging info. * @@ -130,44 +144,43 @@ public abstract class RemoteDocumentModelHandlerImpl * @throws Exception the exception */ protected TL extractPagingInfo(TL theCommonList, DocumentWrapper wrapDoc) - throws Exception { - AbstractCommonList commonList = (AbstractCommonList)theCommonList; - - DocumentFilter docFilter = this.getDocumentFilter(); - long pageSize = docFilter.getPageSize(); - long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize; - // set the page size and page number - commonList.setPageNum(pageNum); - commonList.setPageSize(pageSize); - DocumentModelList docList = wrapDoc.getWrappedObject(); - // Set num of items in list. this is useful to our testing framework. - commonList.setItemsInPage(docList.size()); - // set the total result size - commonList.setTotalItems(docList.totalSize()); - - return (TL)commonList; + throws Exception { + AbstractCommonList commonList = (AbstractCommonList) theCommonList; + + DocumentFilter docFilter = this.getDocumentFilter(); + long pageSize = docFilter.getPageSize(); + long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize; + // set the page size and page number + commonList.setPageNum(pageNum); + commonList.setPageSize(pageSize); + DocumentModelList docList = wrapDoc.getWrappedObject(); + // Set num of items in list. this is useful to our testing framework. + commonList.setItemsInPage(docList.size()); + // set the total result size + commonList.setTotalItems(docList.totalSize()); + + return (TL) commonList; + } + + /* (non-Javadoc) + * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#extractAllParts(org.collectionspace.services.common.document.DocumentWrapper) + */ + @Override + public void extractAllParts(DocumentWrapper wrapDoc) + throws Exception { + + DocumentModel docModel = wrapDoc.getWrappedObject(); + String[] schemas = docModel.getDeclaredSchemas(); + Map partsMetaMap = getServiceContext().getPartsMetadata(); + for (String schema : schemas) { + ObjectPartType partMeta = partsMetaMap.get(schema); + if (partMeta == null) { + continue; // unknown part, ignore + } + Map unQObjectProperties = extractPart(docModel, schema, partMeta); + addOutputPart(unQObjectProperties, schema, partMeta); + } } - - - /* (non-Javadoc) - * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#extractAllParts(org.collectionspace.services.common.document.DocumentWrapper) - */ - @Override - public void extractAllParts(DocumentWrapper wrapDoc) - throws Exception { - - DocumentModel docModel = wrapDoc.getWrappedObject(); - String[] schemas = docModel.getDeclaredSchemas(); - Map partsMetaMap = getServiceContext().getPartsMetadata(); - for (String schema : schemas) { - ObjectPartType partMeta = partsMetaMap.get(schema); - if (partMeta == null) { - continue; // unknown part, ignore - } - Map unQObjectProperties = extractPart(docModel, schema, partMeta); - addOutputPart(unQObjectProperties, schema, partMeta); - } - } /* (non-Javadoc) * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#fillAllParts(org.collectionspace.services.common.document.DocumentWrapper) @@ -181,7 +194,7 @@ public abstract class RemoteDocumentModelHandlerImpl DocumentModel docModel = wrapDoc.getWrappedObject(); MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext(); MultipartInput input = ctx.getInput(); - if(input.getParts().isEmpty()){ + if (input.getParts().isEmpty()) { String msg = "No payload found!"; logger.error(msg + "Ctx=" + getServiceContext().toString()); throw new BadRequestException(msg); @@ -191,7 +204,7 @@ public abstract class RemoteDocumentModelHandlerImpl //iterate over parts received and fill those parts List inputParts = input.getParts(); - for(InputPart part : inputParts){ + for (InputPart part : inputParts) { String partLabel = part.getHeaders().getFirst("label"); if (partLabel == null) { @@ -199,10 +212,10 @@ public abstract class RemoteDocumentModelHandlerImpl logger.error(msg + "Ctx=" + getServiceContext().toString()); throw new BadRequestException(msg); } - + //skip if the part is not in metadata ObjectPartType partMeta = partsMetaMap.get(partLabel); - if(partMeta==null){ + if (partMeta == null) { continue; } fillPart(part, docModel, partMeta, action); @@ -220,20 +233,20 @@ public abstract class RemoteDocumentModelHandlerImpl protected void fillPart(InputPart part, DocumentModel docModel, ObjectPartType partMeta, Action action) throws Exception { InputStream payload = part.getBody(InputStream.class, null); - + // TODO for sub-docs - after we parse the doc, we need to look for elements that are configured as // subitem lists, for this part (schema), pull them out, and set them aside for later processing. //check if this is an xml part - if(part.getMediaType().equals(MediaType.APPLICATION_XML_TYPE)){ - if(payload != null){ + if (part.getMediaType().equals(MediaType.APPLICATION_XML_TYPE)) { + if (payload != null) { Document document = DocumentUtils.parseDocument(payload, partMeta, - false /*don't validate*/); + false /*don't validate*/); //TODO: callback to handler if registered to validate the //document Map objectProps = DocumentUtils.parseProperties(document.getFirstChild()); - if(action==Action.UPDATE) { - this.filterReadOnlyPropertiesForPart(objectProps, partMeta); + if (action == Action.UPDATE) { + this.filterReadOnlyPropertiesForPart(objectProps, partMeta); } docModel.setProperties(partMeta.getLabel(), objectProps); } @@ -247,8 +260,8 @@ public abstract class RemoteDocumentModelHandlerImpl * @param partMeta metadata for the object to fill */ public void filterReadOnlyPropertiesForPart( - Map objectProps, ObjectPartType partMeta) { - // Currently a no-op, but can be overridden in Doc handlers. + Map objectProps, ObjectPartType partMeta) { + // Currently a no-op, but can be overridden in Doc handlers. } /** @@ -260,9 +273,9 @@ public abstract class RemoteDocumentModelHandlerImpl */ protected Map extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta) throws Exception { - return extractPart( docModel, schema, partMeta, null ); + return extractPart(docModel, schema, partMeta, null); } - + /** * extractPart extracts an XML object from given DocumentModel * @param docModel @@ -271,109 +284,132 @@ public abstract class RemoteDocumentModelHandlerImpl * @throws Exception */ protected Map extractPart( - DocumentModel docModel, String schema, ObjectPartType partMeta, - Map addToMap) + DocumentModel docModel, String schema, ObjectPartType partMeta, + Map addToMap) throws Exception { - Map result = null; - + Map result = null; + MediaType mt = MediaType.valueOf(partMeta.getContent().getContentType()); - if (mt.equals(MediaType.APPLICATION_XML_TYPE)){ + if (mt.equals(MediaType.APPLICATION_XML_TYPE)) { Map objectProps = docModel.getProperties(schema); //unqualify properties before sending the doc over the wire (to save bandwidh) //FIXME: is there a better way to avoid duplication of a collection? - Map unQObjectProperties = - (addToMap!=null)? addToMap:(new HashMap()); + Map unQObjectProperties = + (addToMap != null) ? addToMap : (new HashMap()); Set> qualifiedEntries = objectProps.entrySet(); - for(Entry entry : qualifiedEntries){ + for (Entry entry : qualifiedEntries) { String unqProp = getUnQProperty(entry.getKey()); unQObjectProperties.put(unqProp, entry.getValue()); } result = unQObjectProperties; } //TODO: handle other media types - + return result; } - + /* (non-Javadoc) * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#getAuthorityRefs(org.collectionspace.services.common.document.DocumentWrapper, java.util.List) */ @Override public AuthorityRefList getAuthorityRefs( - DocumentWrapper docWrapper, - List authRefFields) throws PropertyException { - final String FIELD_REFNAME_DELIMITER = "|"; - final String FIELD_REFNAME_DELIMITER_REGEX = "\\" + FIELD_REFNAME_DELIMITER; - AuthorityRefList authRefList = new AuthorityRefList(); - try { - DocumentModel docModel = docWrapper.getWrappedObject(); - List list = authRefList.getAuthorityRefItem(); - - for (String field : authRefFields) { - // FIXME If the code used below doesn't support - // arbitrary levels of nesting; e.g. "get all authrefs - // in any children of a parent," then we might use - // docModel.getProperties() instead, - List refNames = new ArrayList(); - Object val = docModel.getPropertyValue(field); - if (val instanceof String) - refNames.add((String) val); - else if (val instanceof List) { - refNames = (List) val; + DocumentWrapper docWrapper, + List authRefFieldNames) throws PropertyException { + + final String SCHEMA_FIELD_DELIMITER = ":"; + AuthorityRefList authRefList = new AuthorityRefList(); + List list = authRefList.getAuthorityRefItem(); + String refName = ""; + + try { + DocumentModel docModel = docWrapper.getWrappedObject(); + + for (String authRefFieldName : authRefFieldNames) { + + String schemaName = ""; + // FIXME: Replacing the following by an existing utility + // method or, if not already present, create a new utility + // method for this task in the common package. + if (authRefFieldName.indexOf(SCHEMA_FIELD_DELIMITER) > 0) { + String[] authRefFieldNameParts = + authRefFieldName.split(SCHEMA_FIELD_DELIMITER); + schemaName = authRefFieldNameParts[0]; + authRefFieldName = authRefFieldNameParts[1]; + } + + Schema schema = DocumentUtils.getSchema(schemaName); + Field field = schema.getField(authRefFieldName); + Type type = field.getType(); + + if (type.isSimpleType()) { + Object obj = docModel.getPropertyValue(authRefFieldName); + if (obj != null) { + refName = (String) obj; + if (refName != null || ! refName.trim().isEmpty()) { + list.add(authorityRefListItem(authRefFieldName, refName)); } - for (String refName : refNames) { - if (refName == null || refName.trim().isEmpty()) - continue; - try { - // If the refName is prefixed by a field name - // and a delimiter, this means that it was - // found in a child of the specified authref field. - // - // Store the child field's name as the field name. - // Then strip off the child's name and the delimiter - // from the refName. - // - // FIXME: Move this 'split' code to its own utility method. - // FIXME: Verify that the behavior description above - // is accurate for arbitrary levels of nesting. - if (refName.indexOf(FIELD_REFNAME_DELIMITER) > 0) { - String[] refNameParts = - refName.split(FIELD_REFNAME_DELIMITER_REGEX); - field = refNameParts[0]; - refName = refNameParts[1]; - } - - RefNameUtils.AuthorityTermInfo termInfo = RefNameUtils - .parseAuthorityTermInfo(refName); - AuthorityRefList.AuthorityRefItem ilistItem = new AuthorityRefList.AuthorityRefItem(); - ilistItem.setRefName(refName); - ilistItem.setAuthDisplayName(termInfo.inAuthority.displayName); - ilistItem.setItemDisplayName(termInfo.displayName); - ilistItem.setSourceField(field); - ilistItem.setUri(termInfo.getRelativeUri()); - list.add(ilistItem); - } catch (Exception e) { - // FIXME: Do we need to throw this Exception here? - if (logger.isDebugEnabled()) { - logger.debug("Caught exception in getAuthorityRefs", e); - } + } + // FIXME: The following assumes a very simple structure + // for repeatable single scalar fields: a parent (continer) + // element, containing 0-n child elements, each of the same + // name and type, with values capable of being meaningfully + // cast to String. + // + // Past release 1.0a, repeatability may consist + // of arbitrary nesting and complexity, rather than + // scalars and single-level lists. When needed, that + // might be implemented here via recursion through + // nested listTypes and/or complexTypes. + } else if (type.isListType()) { + // Get the name of the child field that comprises + // value instances of the parent (container) field. + ListType ltype = (ListType) type; + field = ltype.getField(); + String childAuthRefFieldName = field.getName().getLocalName(); + // For each value instance, add its refName to the authRefs list, + // with its source field name set to the child field's name. + List valuesList = (List) docModel.getPropertyValue(authRefFieldName); + for (Object obj : valuesList) { + if (obj != null) { + refName = (String) obj; + if (refName != null || ! refName.trim().isEmpty()) { + list.add(authorityRefListItem(childAuthRefFieldName, refName)); } } - } - } catch (PropertyException pe) { - String msg = "Attempted to retrieve value for invalid or missing authority field. " - + "Check authority field properties in tenant bindings."; - logger.warn(msg, pe); - throw pe; - } catch (Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("Caught exception in getAuthorityRefs", e); - } - Response response = Response.status( - Response.Status.INTERNAL_SERVER_ERROR).entity( - "Failed to retrieve authority references").type( - "text/plain").build(); - throw new WebApplicationException(response); - } - return authRefList; + } + } + + } + + } catch (PropertyException pe) { + String msg = "Attempted to retrieve value for invalid or missing authority field. " + + "Check authority field properties in tenant bindings."; + logger.warn(msg, pe); + throw pe; + } catch (Exception e) { + if (logger.isDebugEnabled()) { + logger.debug("Caught exception in getAuthorityRefs", e); + } + Response response = Response.status( + Response.Status.INTERNAL_SERVER_ERROR).entity( + "Failed to retrieve authority references").type( + "text/plain").build(); + throw new WebApplicationException(response); + } + return authRefList; } + + public AuthorityRefList.AuthorityRefItem authorityRefListItem(String authRefFieldName, String refName) { + + AuthorityRefList.AuthorityRefItem ilistItem = new AuthorityRefList.AuthorityRefItem(); + try { + RefNameUtils.AuthorityTermInfo termInfo = RefNameUtils.parseAuthorityTermInfo(refName); + ilistItem.setRefName(refName); + ilistItem.setAuthDisplayName(termInfo.inAuthority.displayName); + ilistItem.setItemDisplayName(termInfo.displayName); + ilistItem.setSourceField(authRefFieldName); + ilistItem.setUri(termInfo.getRelativeUri()); + } catch (Exception e) { + } + return ilistItem; + } }