2 * This document is a part of the source code and related artifacts
3 * for CollectionSpace, an open source collections management system
4 * for museums and related institutions:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
24 package org.collectionspace.services.common.vocabulary.nuxeo;
26 import org.collectionspace.services.client.AuthorityClient;
27 import org.collectionspace.services.client.IQueryManager;
28 import org.collectionspace.services.client.PayloadInputPart;
29 import org.collectionspace.services.client.PayloadOutputPart;
30 import org.collectionspace.services.client.PoxPayloadIn;
31 import org.collectionspace.services.client.PoxPayloadOut;
32 import org.collectionspace.services.client.RelationClient;
34 import org.collectionspace.services.common.ResourceBase;
35 import org.collectionspace.services.common.api.CommonAPI;
36 import org.collectionspace.services.common.api.RefName;
37 import org.collectionspace.services.common.api.Tools;
38 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
39 import org.collectionspace.services.common.context.MultipartServiceContext;
40 import org.collectionspace.services.common.context.ServiceBindingUtils;
41 import org.collectionspace.services.common.context.ServiceContext;
42 import org.collectionspace.services.common.document.DocumentException;
43 import org.collectionspace.services.common.document.DocumentFilter;
44 import org.collectionspace.services.common.document.DocumentWrapper;
45 import org.collectionspace.services.common.relation.IRelationsManager;
46 import org.collectionspace.services.common.repository.RepositoryClient;
47 import org.collectionspace.services.common.vocabulary.AuthorityJAXBSchema;
48 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
49 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils;
51 import org.collectionspace.services.config.service.ListResultField;
52 import org.collectionspace.services.config.service.ObjectPartType;
54 import org.collectionspace.services.nuxeo.client.java.DocHandlerBase;
55 import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;
56 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
58 import org.collectionspace.services.relation.RelationResource;
59 import org.collectionspace.services.relation.RelationsCommon;
60 import org.collectionspace.services.relation.RelationsCommonList;
61 import org.collectionspace.services.relation.RelationsDocListItem;
62 import org.collectionspace.services.relation.RelationshipType;
64 import org.collectionspace.services.vocabulary.VocabularyItemJAXBSchema;
66 import org.nuxeo.ecm.core.api.ClientException;
67 import org.nuxeo.ecm.core.api.DocumentModel;
68 import org.nuxeo.ecm.core.api.model.PropertyException;
69 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
74 import javax.ws.rs.core.MultivaluedMap;
75 import javax.ws.rs.core.Response;
76 import javax.ws.rs.core.UriInfo;
78 import java.util.ArrayList;
79 import java.util.HashMap;
80 import java.util.List;
82 import java.util.regex.Matcher;
83 import java.util.regex.Pattern;
85 //import org.collectionspace.services.common.authority.AuthorityItemRelations;
87 * AuthorityItemDocumentModelHandler
89 * $LastChangedRevision: $
92 public abstract class AuthorityItemDocumentModelHandler<AICommon>
93 extends DocHandlerBase<AICommon> {
95 private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
96 private String authorityItemCommonSchemaName;
97 private String authorityItemTermGroupXPathBase;
99 * inVocabulary is the parent Authority for this context
101 protected String inAuthority = null;
102 protected String authorityRefNameBase = null;
103 // Used to determine when the displayName changes as part of the update.
104 protected String oldDisplayNameOnUpdate = null;
105 protected String oldRefNameOnUpdate = null;
106 protected String newRefNameOnUpdate = null;
108 public AuthorityItemDocumentModelHandler(String authorityItemCommonSchemaName) {
109 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
112 public void setInAuthority(String inAuthority) {
113 this.inAuthority = inAuthority;
116 /** Subclasses may override this to customize the URI segment. */
117 public String getAuthorityServicePath() {
118 return getServiceContext().getServiceName().toLowerCase(); // Laramie20110510 CSPACE-3932
122 public String getUri(DocumentModel docModel) {
123 // Laramie20110510 CSPACE-3932
124 String authorityServicePath = getAuthorityServicePath();
125 if(inAuthority==null) { // Only happens on queries to wildcarded authorities
127 inAuthority = (String) docModel.getProperty(authorityItemCommonSchemaName,
128 AuthorityItemJAXBSchema.IN_AUTHORITY);
129 } catch (ClientException pe) {
130 throw new RuntimeException("Could not get parent specifier for item!");
133 return "/" + authorityServicePath + '/' + inAuthority + '/' + AuthorityClient.ITEMS + '/' + getCsid(docModel);
136 protected String getAuthorityRefNameBase() {
137 return this.authorityRefNameBase;
140 public void setAuthorityRefNameBase(String value) {
141 this.authorityRefNameBase = value;
145 * Note: the Vocabulary service's VocabularyItemDocumentModelHandler class overrides this method.
147 protected ListResultField getListResultsDisplayNameField() {
148 ListResultField result = new ListResultField();
149 // Per CSPACE-5132, the name of this element remains 'displayName'
150 // for backwards compatibility, although its value is obtained
151 // from the termDisplayName field.
153 // Update: this name is now being changed to 'termDisplayName', both
154 // because this is the actual field name and because the app layer
155 // work to convert over to this field is underway. Per Patrick, the
156 // app layer treats lists, in at least some context(s), as sparse record
157 // payloads, and thus fields in list results must all be present in
158 // (i.e. represent a strict subset of the fields in) record schemas.
162 // In CSPACE-5134, these list results will change substantially
163 // to return display names for both the preferred term and for
164 // each non-preferred term (if any).
165 result.setElement(AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
166 result.setXpath(NuxeoUtils.getPrimaryXPathPropertyName(
167 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_DISPLAY_NAME));
173 * Note: the Vocabulary service's VocabularyItemDocumentModelHandler class overrides this method.
175 protected ListResultField getListResultsTermStatusField() {
176 ListResultField result = new ListResultField();
178 result.setElement(AuthorityItemJAXBSchema.TERM_STATUS);
179 result.setXpath(NuxeoUtils.getPrimaryXPathPropertyName(
180 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_STATUS));
185 private boolean isTermDisplayName(String elName) {
186 return AuthorityItemJAXBSchema.TERM_DISPLAY_NAME.equals(elName) || VocabularyItemJAXBSchema.DISPLAY_NAME.equals(elName);
191 * @see org.collectionspace.services.nuxeo.client.java.DocHandlerBase#getListItemsArray()
193 * Note: We're updating the "global" service and tenant bindings instance here -the list instance here is
194 * a reference to the tenant bindings instance in the singleton ServiceMain.
197 public List<ListResultField> getListItemsArray() throws DocumentException {
198 List<ListResultField> list = super.getListItemsArray();
200 // One-time initialization for each authority item service.
201 if (isListItemArrayExtended() == false) {
202 synchronized(AuthorityItemDocumentModelHandler.class) {
203 if (isListItemArrayExtended() == false) {
204 int nFields = list.size();
205 // Ensure that each item in a list of Authority items includes
206 // a set of common fields, so we do not depend upon configuration
207 // for general logic.
208 boolean hasDisplayName = false;
209 boolean hasShortId = false;
210 boolean hasRefName = false;
211 boolean hasTermStatus = false;
212 for (int i = 0; i < nFields; i++) {
213 ListResultField field = list.get(i);
214 String elName = field.getElement();
215 if (isTermDisplayName(elName) == true) {
216 hasDisplayName = true;
217 } else if (AuthorityItemJAXBSchema.SHORT_IDENTIFIER.equals(elName)) {
219 } else if (AuthorityItemJAXBSchema.REF_NAME.equals(elName)) {
221 } else if (AuthorityItemJAXBSchema.TERM_STATUS.equals(elName)) {
222 hasTermStatus = true;
226 ListResultField field;
227 if (!hasDisplayName) {
228 field = getListResultsDisplayNameField();
232 field = new ListResultField();
233 field.setElement(AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
234 field.setXpath(AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
238 field = new ListResultField();
239 field.setElement(AuthorityItemJAXBSchema.REF_NAME);
240 field.setXpath(AuthorityItemJAXBSchema.REF_NAME);
243 if (!hasTermStatus) {
244 field = getListResultsTermStatusField();
249 setListItemArrayExtended(true);
250 } // end of synchronized block
257 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleCreate(org.collectionspace.services.common.document.DocumentWrapper)
260 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
261 // first fill all the parts of the document
262 super.handleCreate(wrapDoc);
263 // Ensure we have required fields set properly
264 handleInAuthority(wrapDoc.getWrappedObject());
266 // FIXME: This call to synthesize a shortIdentifier from the termDisplayName
267 // of the preferred term may have been commented out, in the course of
268 // adding support for preferred / non-preferred terms, in CSPACE-4813
269 // and linked issues. Revisit this to determine whether we want to
273 handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityItemCommonSchemaName);
274 // refName includes displayName, so we force a correct value here.
275 updateRefnameForAuthorityItem(wrapDoc, authorityItemCommonSchemaName, getAuthorityRefNameBase());
279 * Note that the Vocabulary service's document-model for items overrides this method.
281 protected String getPrimaryDisplayName(DocumentModel docModel, String schema,
282 String complexPropertyName, String fieldName) {
283 String result = null;
285 result = getStringValueInPrimaryRepeatingComplexProperty(docModel, schema, complexPropertyName, fieldName);
291 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleUpdate(org.collectionspace.services.common.document.DocumentWrapper)
294 public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
295 // First, get a copy of the old displayName
296 // oldDisplayNameOnUpdate = (String) wrapDoc.getWrappedObject().getProperty(authorityItemCommonSchemaName,
297 // AuthorityItemJAXBSchema.DISPLAY_NAME);
298 oldDisplayNameOnUpdate = getPrimaryDisplayName(wrapDoc.getWrappedObject(), authorityItemCommonSchemaName,
299 getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
300 oldRefNameOnUpdate = (String) wrapDoc.getWrappedObject().getProperty(authorityItemCommonSchemaName,
301 AuthorityItemJAXBSchema.REF_NAME);
302 super.handleUpdate(wrapDoc);
304 // Now, check the new display and handle the refname update.
305 String newDisplayName = (String) getPrimaryDisplayName(wrapDoc.getWrappedObject(), authorityItemCommonSchemaName,
306 authorityItemTermGroupXPathBase,
307 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
308 if (newDisplayName != null && !newDisplayName.equals(oldDisplayNameOnUpdate)) {
309 // Need to update the refName, and then fix all references.
310 newRefNameOnUpdate = handleItemRefNameUpdateForDisplayName(wrapDoc.getWrappedObject(), newDisplayName);
312 // Mark as not needing attention in completeUpdate phase.
313 newRefNameOnUpdate = null;
314 oldRefNameOnUpdate = null;
319 * Handle display name.
321 * @param docModel the doc model
322 * @throws Exception the exception
324 // protected void handleComputedDisplayNames(DocumentModel docModel) throws Exception {
325 // // Do nothing by default.
329 * Handle refName updates for changes to display name.
330 * Assumes refName is already correct. Just ensures it is right.
332 * @param docModel the doc model
333 * @param newDisplayName the new display name
334 * @throws Exception the exception
336 protected String handleItemRefNameUpdateForDisplayName(DocumentModel docModel,
337 String newDisplayName) throws Exception {
338 RefName.AuthorityItem authItem = RefName.AuthorityItem.parse(oldRefNameOnUpdate);
339 if (authItem == null) {
340 String err = "Authority Item has illegal refName: " + oldRefNameOnUpdate;
342 throw new IllegalArgumentException(err);
344 authItem.displayName = newDisplayName;
345 String updatedRefName = authItem.toString();
346 docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REF_NAME, updatedRefName);
347 return updatedRefName;
351 * Note: The Vocabulary document handler overrides this method.
353 protected String getRefPropName() {
354 return ServiceBindingUtils.AUTH_REF_PROP;
358 * Checks to see if the refName has changed, and if so,
359 * uses utilities to find all references and update them.
362 protected void handleItemRefNameReferenceUpdate() throws Exception {
363 if (newRefNameOnUpdate != null && oldRefNameOnUpdate != null) {
364 // We have work to do.
365 if (logger.isDebugEnabled()) {
366 String eol = System.getProperty("line.separator");
367 logger.debug("Need to find and update references to Item." + eol
368 + " Old refName" + oldRefNameOnUpdate + eol
369 + " New refName" + newRefNameOnUpdate);
371 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
372 RepositoryClient repoClient = getRepositoryClient(ctx);
373 String refNameProp = getRefPropName();
375 int nUpdated = RefNameServiceUtils.updateAuthorityRefDocs(ctx, repoClient, this.getRepositorySession(),
376 oldRefNameOnUpdate, newRefNameOnUpdate, refNameProp);
377 if (logger.isDebugEnabled()) {
378 logger.debug("Updated " + nUpdated + " instances of oldRefName to newRefName");
384 * If no short identifier was provided in the input payload, generate a
385 * short identifier from the preferred term display name or term name.
387 private void handleDisplayNameAsShortIdentifier(DocumentModel docModel,
388 String schemaName) throws Exception {
389 String shortIdentifier = (String) docModel.getProperty(schemaName,
390 AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
392 if (Tools.isEmpty(shortIdentifier)) {
393 String termDisplayName = getPrimaryDisplayName(
394 docModel, authorityItemCommonSchemaName,
395 getItemTermInfoGroupXPathBase(),
396 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
398 String termName = getPrimaryDisplayName(
399 docModel, authorityItemCommonSchemaName,
400 getItemTermInfoGroupXPathBase(),
401 AuthorityItemJAXBSchema.TERM_NAME);
403 String generatedShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(termDisplayName,
405 docModel.setProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER,
406 generatedShortIdentifier);
411 * Generate a refName for the authority item from the short identifier
414 * All refNames for authority items are generated. If a client supplies
415 * a refName, it will be overwritten during create (per this method)
416 * or discarded during update (per filterReadOnlyPropertiesForPart).
418 * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
421 protected void updateRefnameForAuthorityItem(DocumentWrapper<DocumentModel> wrapDoc,
423 String authorityRefBaseName) throws Exception {
424 DocumentModel docModel = wrapDoc.getWrappedObject();
425 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
426 String displayName = getPrimaryDisplayName(docModel, authorityItemCommonSchemaName,
427 getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
429 if (Tools.isEmpty(authorityRefBaseName)) {
430 throw new Exception("Could not create the refName for this authority term, because the refName for its authority parent was empty.");
433 RefName.Authority authority = RefName.Authority.parse(authorityRefBaseName);
434 String refName = RefName.buildAuthorityItem(authority, shortIdentifier, displayName).toString();
435 docModel.setProperty(schemaName, AuthorityItemJAXBSchema.REF_NAME, refName);
439 * Check the logic around the parent pointer. Note that we only need do this on
440 * create, since we have logic to make this read-only on update.
444 * @throws Exception the exception
446 private void handleInAuthority(DocumentModel docModel) throws Exception {
447 if(inAuthority==null) { // Only happens on queries to wildcarded authorities
448 throw new IllegalStateException("Trying to Create an object with no inAuthority value!");
450 docModel.setProperty(authorityItemCommonSchemaName,
451 AuthorityItemJAXBSchema.IN_AUTHORITY, inAuthority);
455 public AuthorityRefDocList getReferencingObjects(
456 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
457 List<String> serviceTypes,
459 String itemcsid) throws Exception {
460 AuthorityRefDocList authRefDocList = null;
461 RepositoryInstance repoSession = null;
462 boolean releaseRepoSession = false;
465 RepositoryJavaClientImpl repoClient = (RepositoryJavaClientImpl)this.getRepositoryClient(ctx);
466 repoSession = this.getRepositorySession();
467 if (repoSession == null) {
468 repoSession = repoClient.getRepositorySession(ctx);
469 releaseRepoSession = true;
471 DocumentFilter myFilter = getDocumentFilter();
474 DocumentWrapper<DocumentModel> wrapper = repoClient.getDoc(repoSession, ctx, itemcsid);
475 DocumentModel docModel = wrapper.getWrappedObject();
476 String refName = (String) docModel.getPropertyValue(AuthorityItemJAXBSchema.REF_NAME);
477 authRefDocList = RefNameServiceUtils.getAuthorityRefDocs(
478 repoSession, ctx, repoClient,
482 myFilter, true /*computeTotal*/);
483 } catch (PropertyException pe) {
485 } catch (DocumentException de) {
487 } catch (Exception e) {
488 if (logger.isDebugEnabled()) {
489 logger.debug("Caught exception ", e);
491 throw new DocumentException(e);
493 // If we got/aquired a new seesion then we're responsible for releasing it.
494 if (releaseRepoSession && repoSession != null) {
495 repoClient.releaseRepositorySession(ctx, repoSession);
498 } catch (Exception e) {
499 if (logger.isDebugEnabled()) {
500 logger.debug("Caught exception ", e);
502 throw new DocumentException(e);
505 return authRefDocList;
510 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
513 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
515 Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
517 // Add the CSID to the common part, since they may have fetched via the shortId.
518 if (partMeta.getLabel().equalsIgnoreCase(authorityItemCommonSchemaName)) {
519 String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
520 unQObjectProperties.put("csid", csid);
523 return unQObjectProperties;
527 * Filters out selected values supplied in an update request.
529 * For example, filters out AuthorityItemJAXBSchema.IN_AUTHORITY, to ensure
530 * that the link to the item's parent remains untouched.
532 * @param objectProps the properties filtered out from the update payload
533 * @param partMeta metadata for the object to fill
536 public void filterReadOnlyPropertiesForPart(
537 Map<String, Object> objectProps, ObjectPartType partMeta) {
538 super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
539 String commonPartLabel = getServiceContext().getCommonPartLabel();
540 if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
541 objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
542 objectProps.remove(AuthorityItemJAXBSchema.CSID);
543 objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
544 objectProps.remove(AuthorityItemJAXBSchema.REF_NAME);
548 protected List<String> getPartialTermDisplayNameMatches(List<String> termDisplayNameList, String partialTerm) {
549 List<String> result = new ArrayList<String>();
551 for (String termDisplayName : termDisplayNameList) {
552 if (termDisplayName.toLowerCase().contains(partialTerm.toLowerCase()) == true) {
553 result.add(termDisplayName);
560 @SuppressWarnings("unchecked")
561 private List<String> getPartialTermDisplayNameMatches(DocumentModel docModel, // REM - CSPACE-5133
562 String schema, ListResultField field, String partialTerm) {
563 List<String> result = null;
565 String xpath = field.getXpath(); // results in something like "persons_common:personTermGroupList/[0]/termDisplayName"
566 int endOfTermGroup = xpath.lastIndexOf("/[0]/");
567 String propertyName = endOfTermGroup != -1 ? xpath.substring(0, endOfTermGroup) : xpath; // it may not be multivalued so the xpath passed in would be the property name
571 value = docModel.getProperty(schema, propertyName);
572 } catch (Exception e) {
573 logger.error("Could not extract term display name with property = "
577 if (value != null && value instanceof ArrayList) {
578 ArrayList<HashMap<String, Object>> termGroupList = (ArrayList<HashMap<String, Object>>)value;
579 int arrayListSize = termGroupList.size();
580 if (arrayListSize > 1) { // if there's only 1 element in the list then we've already matched the primary term's display name
581 List<String> displayNameList = new ArrayList<String>();
582 for (int i = 1; i < arrayListSize; i++) { // start at 1, skip the primary term's displayName since we will always return it
583 HashMap<String, Object> map = (HashMap<String, Object>)termGroupList.get(i);
584 String termDisplayName = (String) map.get(AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
585 displayNameList.add(i - 1, termDisplayName);
588 result = getPartialTermDisplayNameMatches(displayNameList, partialTerm);
596 protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
597 String schema, ListResultField field) {
598 Object result = null;
600 result = NuxeoUtils.getXPathValue(docModel, schema, field.getXpath());
601 String elName = field.getElement();
603 // If the list result value is the termDisplayName element, we need to check to see if a partial term query was made.
605 if (isTermDisplayName(elName) == true) {
606 MultivaluedMap<String, String> queryParams = this.getServiceContext().getQueryParams();
607 String partialTerm = queryParams != null ? queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM) : null;
608 if (partialTerm != null && partialTerm.trim().isEmpty() == false) {
609 String primaryTermDisplayName = (String)result;
610 List<String> matches = getPartialTermDisplayNameMatches(docModel, schema, field, partialTerm);
611 if (matches != null && matches.isEmpty() == false) {
612 matches.add(0, primaryTermDisplayName); // insert the primary term's display name at the beginning of the list
613 result = matches; // set the result to a list of matching term display names with the primary term's display name at the beginning
622 public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
623 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
624 super.extractAllParts(wrapDoc);
626 String showSiblings = ctx.getQueryParams().getFirst(CommonAPI.showSiblings_QP);
627 if (Tools.isTrue(showSiblings)) {
628 showSiblings(wrapDoc, ctx);
629 return; // actual result is returned on ctx.addOutputPart();
632 String showRelations = ctx.getQueryParams().getFirst(CommonAPI.showRelations_QP);
633 if (Tools.isTrue(showRelations)) {
634 showRelations(wrapDoc, ctx);
635 return; // actual result is returned on ctx.addOutputPart();
638 String showAllRelations = ctx.getQueryParams().getFirst(CommonAPI.showAllRelations_QP);
639 if (Tools.isTrue(showAllRelations)) {
640 showAllRelations(wrapDoc, ctx);
641 return; // actual result is returned on ctx.addOutputPart();
645 /** @return null on parent not found
647 protected String getParentCSID(String thisCSID) throws Exception {
648 String parentCSID = null;
650 String predicate = RelationshipType.HAS_BROADER.value();
651 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
652 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
653 if (parentList != null) {
654 if (parentList.size() == 0) {
657 RelationsCommonList.RelationListItem relationListItem = parentList.get(0);
658 parentCSID = relationListItem.getObjectCsid();
661 } catch (Exception e) {
662 logger.error("Could not find parent for this: " + thisCSID, e);
667 public void showRelations(DocumentWrapper<DocumentModel> wrapDoc,
668 MultipartServiceContext ctx) throws Exception {
669 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
671 String predicate = RelationshipType.HAS_BROADER.value();
672 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
673 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
675 RelationsCommonList childrenListOuter = getRelations(null, thisCSID, predicate);
676 List<RelationsCommonList.RelationListItem> childrenList = childrenListOuter.getRelationListItem();
678 if(logger.isTraceEnabled()) {
679 String dump = dumpLists(thisCSID, parentList, childrenList, null);
680 logger.trace("~~~~~~~~~~~~~~~~~~~~~~ showRelations ~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
683 //Assume that there are more children than parents. Will be true for parent/child, but maybe not for other relations.
684 //Now add all parents to our childrenList, to be able to return just one list of consolidated results.
685 //Not optimal, but that's the current design spec.
687 for (RelationsCommonList.RelationListItem parent : parentList) {
688 childrenList.add(parent);
691 long childrenSize = childrenList.size();
692 childrenListOuter.setTotalItems(childrenSize);
693 childrenListOuter.setItemsInPage(childrenListOuter.getItemsInPage() + added);
695 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, childrenListOuter);
696 ctx.addOutputPart(relationsPart);
699 public void showSiblings(DocumentWrapper<DocumentModel> wrapDoc,
700 MultipartServiceContext ctx) throws Exception {
701 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
702 String parentCSID = getParentCSID(thisCSID);
703 if (parentCSID == null) {
704 logger.warn("~~~~~\r\n~~~~ Could not find parent for this: " + thisCSID);
708 String predicate = RelationshipType.HAS_BROADER.value();
709 RelationsCommonList siblingListOuter = getRelations(null, parentCSID, predicate);
710 List<RelationsCommonList.RelationListItem> siblingList = siblingListOuter.getRelationListItem();
712 List<RelationsCommonList.RelationListItem> toRemoveList = newList();
715 RelationsCommonList.RelationListItem item = null;
716 for (RelationsCommonList.RelationListItem sibling : siblingList) {
717 if (thisCSID.equals(sibling.getSubjectCsid())) {
718 toRemoveList.add(sibling); //IS_A copy of the main item, i.e. I have a parent that is my parent, so I'm in the list from the above query.
721 //rather than create an immutable iterator, I'm just putting the items to remove on a separate list, then looping over that list and removing.
722 for (RelationsCommonList.RelationListItem self : toRemoveList) {
723 removeFromList(siblingList, self);
726 long siblingSize = siblingList.size();
727 siblingListOuter.setTotalItems(siblingSize);
728 siblingListOuter.setItemsInPage(siblingSize);
729 if(logger.isTraceEnabled()) {
730 String dump = dumpList(siblingList, "Siblings of: "+thisCSID);
731 logger.trace("~~~~~~~~~~~~~~~~~~~~~~ showSiblings ~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
734 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, siblingListOuter);
735 ctx.addOutputPart(relationsPart);
738 public void showAllRelations(DocumentWrapper<DocumentModel> wrapDoc, MultipartServiceContext ctx) throws Exception {
739 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
741 RelationsCommonList subjectListOuter = getRelations(thisCSID, null, null); // nulls are wildcards: predicate=*, and object=*
742 List<RelationsCommonList.RelationListItem> subjectList = subjectListOuter.getRelationListItem();
744 RelationsCommonList objectListOuter = getRelations(null, thisCSID, null); // nulls are wildcards: subject=*, and predicate=*
745 List<RelationsCommonList.RelationListItem> objectList = objectListOuter.getRelationListItem();
747 if(logger.isTraceEnabled()) {
748 String dump = dumpLists(thisCSID, subjectList, objectList, null);
749 logger.trace("~~~~~~~~~~~~~~~~~~~~~~ showAllRelations ~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
752 subjectList.addAll(objectList);
754 //now subjectList actually has records BOTH where thisCSID is subject and object.
755 long relatedSize = subjectList.size();
756 subjectListOuter.setTotalItems(relatedSize);
757 subjectListOuter.setItemsInPage(relatedSize);
759 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, subjectListOuter);
760 ctx.addOutputPart(relationsPart);
764 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
766 // We currently don't override this method with any AuthorityItemDocumentModelHandler specific functionality, so
767 // we could remove this method.
769 super.fillAllParts(wrapDoc, action);
773 public void completeCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
774 super.completeCreate(wrapDoc);
775 handleRelationsPayload(wrapDoc, false);
779 public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
780 super.completeUpdate(wrapDoc);
781 handleRelationsPayload(wrapDoc, true);
782 handleItemRefNameReferenceUpdate();
785 // Note that we must do this after we have completed the Update, so that the repository has the
786 // info for the item itself. The relations code must call into the repo to get info for each end.
787 // This could be optimized to pass in the parent docModel, since it will often be one end.
788 // Nevertheless, we should complete the item save before we do work on the relations, especially
789 // since a save on Create might fail, and we would not want to create relations for something
790 // that may not be created...
791 private void handleRelationsPayload(DocumentWrapper<DocumentModel> wrapDoc, boolean fUpdate) throws Exception {
792 ServiceContext ctx = getServiceContext();
793 PoxPayloadIn input = (PoxPayloadIn) ctx.getInput();
794 DocumentModel documentModel = (wrapDoc.getWrappedObject());
795 String itemCsid = documentModel.getName();
797 //Updates relations part
798 RelationsCommonList relationsCommonList = updateRelations(itemCsid, input, wrapDoc, fUpdate);
800 PayloadOutputPart payloadOutputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, relationsCommonList); //FIXME: REM - We should check for a null relationsCommonList and not create the new common list payload
801 ctx.setProperty(RelationClient.SERVICE_COMMON_LIST_NAME, payloadOutputPart);
803 //now we add part for relations list
804 //ServiceContext ctx = getServiceContext();
805 //PayloadOutputPart foo = (PayloadOutputPart) ctx.getProperty(RelationClient.SERVICE_COMMON_LIST_NAME);
806 ((PoxPayloadOut) ctx.getOutput()).addPart(payloadOutputPart);
809 /** updateRelations strategy:
811 go through inboundList, remove anything from childList that matches from childList
812 go through inboundList, remove anything from parentList that matches from parentList
813 go through parentList, delete all remaining
814 go through childList, delete all remaining
815 go through actionList, add all remaining.
816 check for duplicate children
817 check for more than one parent.
819 inboundList parentList childList actionList
820 ---------------- --------------- ---------------- ----------------
821 child-a parent-c child-a child-b
822 child-b parent-d child-c
825 private RelationsCommonList updateRelations(
826 String itemCSID, PoxPayloadIn input, DocumentWrapper<DocumentModel> wrapDoc, boolean fUpdate)
828 if (logger.isTraceEnabled()) {
829 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID);
831 PayloadInputPart part = input.getPart(RelationClient.SERVICE_COMMON_LIST_NAME); //input.getPart("relations_common");
833 return null; //nothing to do--they didn't send a list of relations.
835 RelationsCommonList relationsCommonListBody = (RelationsCommonList) part.getBody();
836 List<RelationsCommonList.RelationListItem> inboundList = relationsCommonListBody.getRelationListItem();
837 List<RelationsCommonList.RelationListItem> actionList = newList();
838 List<RelationsCommonList.RelationListItem> childList = null;
839 List<RelationsCommonList.RelationListItem> parentList = null;
840 DocumentModel docModel = wrapDoc.getWrappedObject();
841 String itemRefName = (String) docModel.getPropertyValue(AuthorityItemJAXBSchema.REF_NAME);
843 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
844 //Do magic replacement of ${itemCSID} and fix URI's.
845 fixupInboundListItems(ctx, inboundList, docModel, itemCSID);
847 String HAS_BROADER = RelationshipType.HAS_BROADER.value();
848 UriInfo uriInfo = ctx.getUriInfo();
849 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
852 //Run getList() once as sent to get childListOuter:
853 String predicate = RelationshipType.HAS_BROADER.value();
854 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
855 queryParams.putSingle(IRelationsManager.SUBJECT_QP, null);
856 queryParams.putSingle(IRelationsManager.SUBJECT_TYPE_QP, null);
857 queryParams.putSingle(IRelationsManager.OBJECT_QP, itemCSID);
858 queryParams.putSingle(IRelationsManager.OBJECT_TYPE_QP, null);
860 RelationResource relationResource = new RelationResource();
861 RelationsCommonList childListOuter = relationResource.getList(ctx); // Knows all query params because they are in the context.
863 //Now run getList() again, leaving predicate, swapping subject and object, to get parentListOuter.
864 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
865 queryParams.putSingle(IRelationsManager.SUBJECT_QP, itemCSID);
866 queryParams.putSingle(IRelationsManager.OBJECT_QP, null);
867 RelationsCommonList parentListOuter = relationResource.getList(ctx);
870 childList = childListOuter.getRelationListItem();
871 parentList = parentListOuter.getRelationListItem();
873 if (parentList.size() > 1) {
874 throw new Exception("Too many parents for object: " + itemCSID + " list: " + dumpList(parentList, "parentList"));
877 if (logger.isTraceEnabled()) {
878 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " got existing relations.");
882 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
883 // Note that the relations may specify the other (non-item) bit with a refName, not a CSID,
884 // and so the CSID for those may be null
885 if(inboundItem.getPredicate().equals(HAS_BROADER)) {
886 // Look for parents and children
887 if(itemCSID.equals(inboundItem.getObject().getCsid())
888 || itemRefName.equals(inboundItem.getObject().getRefName())) {
889 //then this is an item that says we have a child. That child is inboundItem
890 RelationsCommonList.RelationListItem childItem =
891 (childList == null) ? null : findInList(childList, inboundItem);
892 if (childItem != null) {
893 if (logger.isTraceEnabled()) {
894 StringBuilder sb = new StringBuilder();
895 itemToString(sb, "== Child: ", childItem);
896 logger.trace("Found inboundChild in current child list: " + sb.toString());
898 removeFromList(childList, childItem); //exists, just take it off delete list
900 if (logger.isTraceEnabled()) {
901 StringBuilder sb = new StringBuilder();
902 itemToString(sb, "== Child: ", inboundItem);
903 logger.trace("inboundChild not in current child list, will add: " + sb.toString());
905 actionList.add(inboundItem); //doesn't exist as a child, but is a child. Add to additions list
906 String newChildCsid = inboundItem.getSubject().getCsid();
907 if(newChildCsid == null) {
908 String newChildRefName = inboundItem.getSubject().getRefName();
909 if(newChildRefName==null) {
910 throw new RuntimeException("Child with no CSID or refName!");
912 if (logger.isTraceEnabled()) {
913 logger.trace("Fetching CSID for child with only refname: "+newChildRefName);
915 DocumentModel newChildDocModel =
916 ResourceBase.getDocModelForRefName(this.getRepositorySession(),
917 newChildRefName, getServiceContext().getResourceMap());
918 newChildCsid = getCsid(newChildDocModel);
920 ensureChildHasNoOtherParents(ctx, queryParams, newChildCsid);
923 } else if (itemCSID.equals(inboundItem.getSubject().getCsid())
924 || itemRefName.equals(inboundItem.getSubject().getRefName())) {
925 //then this is an item that says we have a parent. inboundItem is that parent.
926 RelationsCommonList.RelationListItem parentItem =
927 (parentList == null) ? null : findInList(parentList, inboundItem);
928 if (parentItem != null) {
929 removeFromList(parentList, parentItem); //exists, just take it off delete list
931 actionList.add(inboundItem); //doesn't exist as a parent, but is a parent. Add to additions list
934 logger.error("Parent/Child Element didn't link to this item. inboundItem: " + inboundItem);
937 logger.warn("Non-parent relation ignored. inboundItem: " + inboundItem);
940 if (logger.isTraceEnabled()) {
941 String dump = dumpLists(itemCSID, parentList, childList, actionList);
942 logger.trace("~~~~~~~~~~~~~~~~~~~~~~dump~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
945 if (logger.isTraceEnabled()) {
946 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " deleting "
947 + parentList.size() + " existing parents and " + childList.size() + " existing children.");
949 deleteRelations(parentList, ctx, "parentList"); //todo: there are items appearing on both lists....april 20.
950 deleteRelations(childList, ctx, "childList");
952 if (logger.isTraceEnabled()) {
953 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " adding "
954 + actionList.size() + " new parents and children.");
956 createRelations(actionList, ctx);
957 if (logger.isTraceEnabled()) {
958 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " done.");
960 //We return all elements on the inbound list, since we have just worked to make them exist in the system
961 // and be non-redundant, etc. That list came from relationsCommonListBody, so it is still attached to it, just pass that back.
962 return relationsCommonListBody;
965 private void ensureChildHasNoOtherParents(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
966 MultivaluedMap<String, String> queryParams, String childCSID) {
967 logger.trace("ensureChildHasNoOtherParents for: " + childCSID );
968 queryParams.putSingle(IRelationsManager.SUBJECT_QP, childCSID);
969 queryParams.putSingle(IRelationsManager.PREDICATE_QP, RelationshipType.HAS_BROADER.value());
970 queryParams.putSingle(IRelationsManager.OBJECT_QP, null); //null means ANY
972 RelationResource relationResource = new RelationResource();
973 RelationsCommonList parentListOuter = relationResource.getList(ctx);
974 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
975 //logger.warn("ensureChildHasNoOtherParents preparing to delete relations on "+childCSID+"\'s parent list: \r\n"+dumpList(parentList, "duplicate parent list"));
976 deleteRelations(parentList, ctx, "parentList-delete");
980 private void itemToString(StringBuilder sb, String prefix, RelationsCommonList.RelationListItem item ) {
982 sb.append((item.getCsid()!= null)?item.getCsid():"NO CSID");
984 sb.append((item.getSubject().getCsid()!=null)?item.getSubject().getCsid():item.getSubject().getRefName());
986 sb.append(item.getPredicate());
988 sb.append((item.getObject().getCsid()!=null)?item.getObject().getCsid():item.getObject().getRefName());
992 private String dumpLists(String itemCSID,
993 List<RelationsCommonList.RelationListItem> parentList,
994 List<RelationsCommonList.RelationListItem> childList,
995 List<RelationsCommonList.RelationListItem> actionList) {
996 StringBuilder sb = new StringBuilder();
997 sb.append("itemCSID: " + itemCSID + CR);
998 if(parentList!=null) {
999 sb.append(dumpList(parentList, "parentList"));
1001 if(childList!=null) {
1002 sb.append(dumpList(childList, "childList"));
1004 if(actionList!=null) {
1005 sb.append(dumpList(actionList, "actionList"));
1007 return sb.toString();
1009 private final static String CR = "\r\n";
1011 private String dumpList(List<RelationsCommonList.RelationListItem> list, String label) {
1012 StringBuilder sb = new StringBuilder();
1014 if (list.size() > 0) {
1015 sb.append("=========== " + label + " ==========" + CR);
1017 for (RelationsCommonList.RelationListItem item : list) {
1018 itemToString(sb, "== ", item);
1021 return sb.toString();
1024 /** Performs substitution for ${itemCSID} (see CommonAPI.AuthorityItemCSID_REPLACE for constant)
1025 * and sets URI correctly for related items.
1026 * Operates directly on the items in the list. Does not change the list ordering, does not add or remove any items.
1028 protected void fixupInboundListItems(ServiceContext ctx,
1029 List<RelationsCommonList.RelationListItem> inboundList,
1030 DocumentModel docModel,
1031 String itemCSID) throws Exception {
1032 String thisURI = this.getUri(docModel);
1033 // WARNING: the two code blocks below are almost identical and seem to ask to be put in a generic method.
1034 // beware of the little diffs in inboundItem.setObjectCsid(itemCSID); and inboundItem.setSubjectCsid(itemCSID); in the two blocks.
1035 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
1036 RelationsDocListItem inboundItemObject = inboundItem.getObject();
1037 RelationsDocListItem inboundItemSubject = inboundItem.getSubject();
1039 if (CommonAPI.AuthorityItemCSID_REPLACE.equalsIgnoreCase(inboundItemObject.getCsid())) {
1040 inboundItem.setObjectCsid(itemCSID);
1041 inboundItemObject.setCsid(itemCSID);
1042 //inboundItemObject.setUri(getUri(docModel));
1045 String objectCsid = inboundItemObject.getCsid();
1046 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, objectCsid); //null if not found.
1047 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
1048 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
1049 inboundItemObject.setUri(uri); //CSPACE-4037
1052 //uriPointsToSameAuthority(thisURI, inboundItemObject.getUri()); //CSPACE-4042
1054 if (CommonAPI.AuthorityItemCSID_REPLACE.equalsIgnoreCase(inboundItemSubject.getCsid())) {
1055 inboundItem.setSubjectCsid(itemCSID);
1056 inboundItemSubject.setCsid(itemCSID);
1057 //inboundItemSubject.setUri(getUri(docModel));
1060 String subjectCsid = inboundItemSubject.getCsid();
1061 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, subjectCsid); //null if not found.
1062 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
1063 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
1064 inboundItemSubject.setUri(uri); //CSPACE-4037
1067 //uriPointsToSameAuthority(thisURI, inboundItemSubject.getUri()); //CSPACE-4042
1072 // this method calls the RelationResource to have it create the relations and persist them.
1073 private void createRelations(List<RelationsCommonList.RelationListItem> inboundList,
1074 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) throws Exception {
1075 for (RelationsCommonList.RelationListItem item : inboundList) {
1076 RelationsCommon rc = new RelationsCommon();
1077 //rc.setCsid(item.getCsid());
1078 //todo: assignTo(item, rc);
1079 RelationsDocListItem itemSubject = item.getSubject();
1080 RelationsDocListItem itemObject = item.getObject();
1082 // Set at least one of CSID and refName for Subject and Object
1083 // Either value might be null for for each of Subject and Object
1084 String subjectCsid = itemSubject.getCsid();
1085 rc.setSubjectCsid(subjectCsid);
1087 String objCsid = itemObject.getCsid();
1088 rc.setObjectCsid(objCsid);
1090 rc.setSubjectRefName(itemSubject.getRefName());
1091 rc.setObjectRefName(itemObject.getRefName());
1093 rc.setRelationshipType(item.getPredicate());
1094 //RelationshipType foo = (RelationshipType.valueOf(item.getPredicate())) ;
1095 //rc.setPredicate(foo); //this must be one of the type found in the enum in services/jaxb/src/main/resources/relations_common.xsd
1097 // This is superfluous, since it will be fetched by the Relations Create logic.
1098 rc.setSubjectDocumentType(itemSubject.getDocumentType());
1099 rc.setObjectDocumentType(itemObject.getDocumentType());
1101 // This is superfluous, since it will be fetched by the Relations Create logic.
1102 rc.setSubjectUri(itemSubject.getUri());
1103 rc.setObjectUri(itemObject.getUri());
1104 // May not have the info here. Only really require CSID or refName.
1105 // Rest is handled in the Relation create mechanism
1106 //uriPointsToSameAuthority(itemSubject.getUri(), itemObject.getUri());
1108 PoxPayloadOut payloadOut = new PoxPayloadOut(RelationClient.SERVICE_PAYLOAD_NAME);
1109 PayloadOutputPart outputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMONPART_NAME, rc);
1110 payloadOut.addPart(outputPart);
1111 RelationResource relationResource = new RelationResource();
1112 Response res = relationResource.create(ctx, ctx.getResourceMap(),
1113 ctx.getUriInfo(), payloadOut.toXML()); //NOTE ui recycled from above to pass in unknown query params.
1117 private void deleteRelations(List<RelationsCommonList.RelationListItem> list,
1118 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1121 for (RelationsCommonList.RelationListItem item : list) {
1122 RelationResource relationResource = new RelationResource();
1123 if(logger.isTraceEnabled()) {
1124 StringBuilder sb = new StringBuilder();
1125 itemToString(sb, "==== TO DELETE: ", item);
1126 logger.trace(sb.toString());
1128 Response res = relationResource.deleteWithParentCtx(ctx, item.getCsid());
1129 if (logger.isDebugEnabled()) {
1130 logger.debug("Status of authority item deleteRelations method call was: " + res.getStatus());
1133 } catch (Throwable t) {
1134 String msg = "Unable to deleteRelations: " + Tools.errorToString(t, true);
1139 private List<RelationsCommonList.RelationListItem> newList() {
1140 List<RelationsCommonList.RelationListItem> result = new ArrayList<RelationsCommonList.RelationListItem>();
1144 protected List<RelationsCommonList.RelationListItem> cloneList(List<RelationsCommonList.RelationListItem> inboundList) {
1145 List<RelationsCommonList.RelationListItem> result = newList();
1146 for (RelationsCommonList.RelationListItem item : inboundList) {
1152 // Note that the item argument may be sparse (only refName, no CSID for subject or object)
1153 // But the list items must not be sparse
1154 private RelationsCommonList.RelationListItem findInList(
1155 List<RelationsCommonList.RelationListItem> list,
1156 RelationsCommonList.RelationListItem item) {
1157 RelationsCommonList.RelationListItem foundItem = null;
1158 for (RelationsCommonList.RelationListItem listItem : list) {
1159 if (itemsEqual(listItem, item)) { //equals must be defined, else
1160 foundItem = listItem;
1167 // Note that item2 may be sparse (only refName, no CSID for subject or object)
1168 // But item1 must not be sparse
1169 private boolean itemsEqual(RelationsCommonList.RelationListItem item1, RelationsCommonList.RelationListItem item2) {
1170 if (item1 == null || item2 == null) {
1173 RelationsDocListItem subj1 = item1.getSubject();
1174 RelationsDocListItem subj2 = item2.getSubject();
1175 RelationsDocListItem obj1 = item1.getObject();
1176 RelationsDocListItem obj2 = item2.getObject();
1177 String subj1Csid = subj1.getCsid();
1178 String subj2Csid = subj2.getCsid();
1179 String subj1RefName = subj1.getRefName();
1180 String subj2RefName = subj2.getRefName();
1182 String obj1Csid = obj1.getCsid();
1183 String obj2Csid = obj2.getCsid();
1184 String obj1RefName = obj1.getRefName();
1185 String obj2RefName = obj2.getRefName();
1188 (subj1Csid.equals(subj2Csid) || ((subj2Csid==null) && subj1RefName.equals(subj2RefName)))
1189 && (obj1Csid.equals(obj1Csid) || ((obj2Csid==null) && obj1RefName.equals(obj2RefName)))
1190 // predicate is proper, but still allow relationshipType
1191 && (item1.getPredicate().equals(item2.getPredicate())
1192 || ((item2.getPredicate()==null) && item1.getRelationshipType().equals(item2.getRelationshipType())))
1193 // Allow missing docTypes, so long as they do not conflict
1194 && (obj1.getDocumentType().equals(obj2.getDocumentType()) || obj2.getDocumentType()==null)
1195 && (subj1.getDocumentType().equals(subj2.getDocumentType()) || subj2.getDocumentType()==null);
1199 private void removeFromList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item) {
1203 /* don't even THINK of re-using this method.
1204 * String example_uri = "/locationauthorities/7ec60f01-84ab-4908-9a6a/items/a5466530-713f-43b4-bc05";
1206 private String extractInAuthorityCSID(String uri) {
1207 String IN_AUTHORITY_REGEX = "/(.*?)/(.*?)/(.*)";
1208 Pattern p = Pattern.compile(IN_AUTHORITY_REGEX);
1209 Matcher m = p.matcher(uri);
1211 if (m.groupCount() < 3) {
1212 logger.warn("REGEX-WRONG-GROUPCOUNT looking in " + uri);
1215 //String service = m.group(1);
1216 String inauth = m.group(2);
1217 //String theRest = m.group(3);
1219 //print("service:"+service+", inauth:"+inauth+", rest:"+rest);
1222 logger.warn("REGEX-NOT-MATCHED looking in " + uri);
1227 //ensures CSPACE-4042
1228 protected void uriPointsToSameAuthority(String thisURI, String inboundItemURI) throws Exception {
1229 String authorityCSID = extractInAuthorityCSID(thisURI);
1230 String authorityCSIDForInbound = extractInAuthorityCSID(inboundItemURI);
1231 if (Tools.isBlank(authorityCSID)
1232 || Tools.isBlank(authorityCSIDForInbound)
1233 || (!authorityCSID.equalsIgnoreCase(authorityCSIDForInbound))) {
1234 throw new Exception("Item URI " + thisURI + " must point to same authority as related item: " + inboundItemURI);
1238 //================= TODO: move this to common, refactoring this and CollectionObjectResource.java
1239 public RelationsCommonList getRelations(String subjectCSID, String objectCSID, String predicate) throws Exception {
1240 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
1241 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1242 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
1243 queryParams.putSingle(IRelationsManager.SUBJECT_QP, subjectCSID);
1244 queryParams.putSingle(IRelationsManager.OBJECT_QP, objectCSID);
1246 RelationResource relationResource = new RelationResource(); //is this still acting like a singleton as it should be?
1247 RelationsCommonList relationsCommonList = relationResource.getList(ctx);
1248 return relationsCommonList;
1250 //============================= END TODO refactor ==========================
1252 public String getItemTermInfoGroupXPathBase() {
1253 return authorityItemTermGroupXPathBase;
1256 public void setItemTermInfoGroupXPathBase(String itemTermInfoGroupXPathBase) {
1257 authorityItemTermGroupXPathBase = itemTermInfoGroupXPathBase;
1260 protected String getAuthorityItemCommonSchemaName() {
1261 return authorityItemCommonSchemaName;