]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
eed07a053b5508b827619208a3a175ee324db772
[tmp/jakarta-migration.git] /
1 /**
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:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
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.
23  */
24 package org.collectionspace.services.common.vocabulary.nuxeo;
25
26 import org.collectionspace.services.client.AuthorityClient;
27 import org.collectionspace.services.client.PayloadInputPart;
28 import org.collectionspace.services.client.PayloadOutputPart;
29 import org.collectionspace.services.client.PoxPayloadIn;
30 import org.collectionspace.services.client.PoxPayloadOut;
31 import org.collectionspace.services.client.RelationClient;
32 import org.collectionspace.services.common.api.CommonAPI;
33 import org.collectionspace.services.common.api.RefName;
34 import org.collectionspace.services.common.api.Tools;
35 import org.collectionspace.services.common.context.MultipartServiceContext;
36 import org.collectionspace.services.common.context.ServiceContext;
37 import org.collectionspace.services.common.document.DocumentWrapper;
38 import org.collectionspace.services.common.document.DocumentWrapperImpl;
39 import org.collectionspace.services.common.relation.IRelationsManager;
40 import org.collectionspace.services.common.repository.RepositoryClient;
41 import org.collectionspace.services.common.repository.RepositoryClientFactory;
42 import org.collectionspace.services.common.service.ObjectPartType;
43 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
44 import org.collectionspace.services.nuxeo.client.java.DocHandlerBase;
45 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
46 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
47 import org.collectionspace.services.relation.RelationResource;
48 import org.collectionspace.services.relation.RelationsCommon;
49 import org.collectionspace.services.relation.RelationsCommonList;
50 import org.collectionspace.services.relation.RelationsDocListItem;
51 import org.collectionspace.services.relation.RelationshipType;
52 import org.nuxeo.ecm.core.api.DocumentModel;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 import javax.ws.rs.core.MultivaluedMap;
57 import javax.ws.rs.core.UriInfo;
58 import java.util.ArrayList;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.regex.Matcher;
62 import java.util.regex.Pattern;
63
64 //import org.collectionspace.services.common.authority.AuthorityItemRelations;
65
66 /**
67  * AuthorityItemDocumentModelHandler
68  *
69  * $LastChangedRevision: $
70  * $LastChangedDate: $
71  */
72 public abstract class AuthorityItemDocumentModelHandler<AICommon>
73         extends DocHandlerBase<AICommon> {
74
75     private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
76
77         private String authorityItemCommonSchemaName;
78         
79     /**
80      * inVocabulary is the parent Authority for this context
81      */
82     protected String inAuthority;
83     protected String authorityRefNameBase;
84     
85     public AuthorityItemDocumentModelHandler(String authorityItemCommonSchemaName) {
86         this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
87     }
88
89     public String getInAuthority() {
90                 return inAuthority;
91         }
92
93         public void setInAuthority(String inAuthority) {
94                 this.inAuthority = inAuthority;
95         }
96
97     /** Subclasses may override this to customize the URI segment. */
98     public String getAuthorityServicePath(){
99         return getServiceContext().getServiceName().toLowerCase();    // Laramie20110510 CSPACE-3932
100     }
101
102     @Override
103     public String getUri(DocumentModel docModel) {
104         // Laramie20110510 CSPACE-3932
105         String authorityServicePath = getAuthorityServicePath();
106         return "/"+authorityServicePath+'/'+inAuthority+'/'+ AuthorityClient.ITEMS+'/'+getCsid(docModel);
107     }
108
109     public String getAuthorityRefNameBase(){
110         return this.authorityRefNameBase;
111     }
112
113     public void setAuthorityRefNameBase(String value){
114         this.authorityRefNameBase = value;
115     }
116
117     /* (non-Javadoc)
118      * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleCreate(org.collectionspace.services.common.document.DocumentWrapper)
119      */
120     @Override
121     public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
122         // first fill all the parts of the document
123         super.handleCreate(wrapDoc);            
124         handleInAuthority(wrapDoc.getWrappedObject());
125         // Uncomment once debugged and App layer is read to integrate
126         //handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityItemCommonSchemaName);
127         //updateRefnameForAuthorityItem(wrapDoc, authorityItemCommonSchemaName, getAuthorityRefNameBase());  //CSPACE-3178
128     }
129     
130     private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
131         String shortIdentifier = (String)docModel.getProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
132         String displayName =     (String)docModel.getProperty(schemaName, AuthorityItemJAXBSchema.DISPLAY_NAME);
133         if (Tools.isEmpty(shortIdentifier) && Tools.notEmpty(displayName)){
134             String cookedShortIdentifier = Tools.squeeze(displayName)+'-'+Tools.now().toString();
135             docModel.setProperty(schemaName , AuthorityItemJAXBSchema.SHORT_IDENTIFIER, cookedShortIdentifier);
136         }
137     }
138
139     protected void updateRefnameForAuthorityItem(DocumentWrapper<DocumentModel> wrapDoc,
140             String schemaName,
141             String authorityRefBaseName) throws Exception {
142         DocumentModel docModel = wrapDoc.getWrappedObject();
143         String shortIdentifier = (String)docModel.getProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
144         String displayName =     (String)docModel.getProperty(schemaName, AuthorityItemJAXBSchema.DISPLAY_NAME);
145         if (Tools.isEmpty(authorityRefBaseName)){
146                 throw new Exception("updateRefnameForAuthorityItem requires an authorityRefBaseName, but none was supplied.");
147         }
148         RefName.Authority authority = RefName.Authority.parse(authorityRefBaseName);
149         String refName = RefName.buildAuthorityItem(authority, shortIdentifier, displayName).toString();
150         docModel.setProperty(schemaName , AuthorityItemJAXBSchema.REF_NAME, refName);
151     }
152
153     /**
154      * Check the logic around the parent pointer. Note that we only need do this on
155      * create, since we have logic to make this read-only on update. 
156      * 
157      * @param docModel
158      * 
159      * @throws Exception the exception
160      */
161     private void handleInAuthority(DocumentModel docModel) throws Exception {
162         docModel.setProperty(authorityItemCommonSchemaName, 
163                         AuthorityItemJAXBSchema.IN_AUTHORITY, inAuthority);
164     }
165
166
167     /* (non-Javadoc)
168      * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
169      */
170     @Override
171     protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
172             throws Exception {
173         Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
174         
175         // Add the CSID to the common part
176         if (partMeta.getLabel().equalsIgnoreCase(authorityItemCommonSchemaName)) {
177                 String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
178                 unQObjectProperties.put("csid", csid);
179         }
180         
181         return unQObjectProperties;
182     }
183     
184     /**
185      * Filters out AuthorityItemJAXBSchema.IN_AUTHORITY, to ensure that
186      * the parent link remains untouched.
187      * @param objectProps the properties parsed from the update payload
188      * @param partMeta metadata for the object to fill
189      */
190     @Override
191     public void filterReadOnlyPropertiesForPart(
192                 Map<String, Object> objectProps, ObjectPartType partMeta) {
193         super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
194         String commonPartLabel = getServiceContext().getCommonPartLabel();
195         if(partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
196                 objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
197                 objectProps.remove(AuthorityItemJAXBSchema.CSID);
198         }
199     }
200
201     @Override
202     public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
203         MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
204         super.extractAllParts(wrapDoc);
205
206         String showSiblings = ctx.getQueryParams().getFirst(CommonAPI.showSiblings_QP);
207         if (Tools.isTrue(showSiblings)) {
208             showSiblings(wrapDoc, ctx);
209             return;   // actual result is returned on ctx.addOutputPart();
210         }
211
212         String showRelations = ctx.getQueryParams().getFirst(CommonAPI.showRelations_QP);
213         if (Tools.isTrue(showRelations)) {
214             showRelations(wrapDoc, ctx);
215             return;   // actual result is returned on ctx.addOutputPart();
216         }
217
218         String showAllRelations = ctx.getQueryParams().getFirst(CommonAPI.showAllRelations_QP);
219         if (Tools.isTrue(showAllRelations)) {
220             showAllRelations(wrapDoc, ctx);
221             return;   // actual result is returned on ctx.addOutputPart();
222         }
223     }
224
225     /** @return null on parent not found
226      */
227     protected String getParentCSID(String thisCSID) throws Exception {
228         String parentCSID = null;
229         try {
230             String predicate = RelationshipType.HAS_BROADER.value();
231             RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
232             List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
233             if (parentList != null) {
234                 if (parentList.size()==0){
235                     return null;
236                 }
237                 RelationsCommonList.RelationListItem relationListItem = parentList.get(0);
238                 parentCSID = relationListItem.getObjectCsid();
239             }
240             return parentCSID;
241         } catch (Exception e) {
242             logger.error("Could not find parent for this: "+thisCSID, e);
243             return null;
244         }
245     }
246
247     public void showRelations(DocumentWrapper<DocumentModel> wrapDoc,
248                                               MultipartServiceContext ctx)   throws Exception {
249         String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
250
251          String predicate = RelationshipType.HAS_BROADER.value();
252          RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
253          List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
254
255          RelationsCommonList childrenListOuter = getRelations(null, thisCSID, predicate);
256          List<RelationsCommonList.RelationListItem> childrenList = childrenListOuter.getRelationListItem();
257
258          //Assume that there are more children than parents.  Will be true for parent/child, but maybe not for other relations.
259          //Now add all parents to our childrenList, to be able to return just one list of consolidated results.
260          //Not optimal, but that's the current design spec.
261         long added = 0;
262         for (RelationsCommonList.RelationListItem parent : parentList) {
263              childrenList.add(parent);
264              added++;
265         }
266         long childrenSize = childrenList.size();
267         childrenListOuter.setTotalItems(childrenSize);
268         childrenListOuter.setItemsInPage(childrenListOuter.getItemsInPage()+added);
269
270         PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, childrenListOuter);
271         ctx.addOutputPart(relationsPart);
272     }
273
274     public void showSiblings(DocumentWrapper<DocumentModel> wrapDoc,
275                                               MultipartServiceContext ctx)   throws Exception {
276         String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
277          String parentCSID = getParentCSID(thisCSID);
278         if (parentCSID == null){
279             logger.warn("~~~~~\r\n~~~~ Could not find parent for this: "+thisCSID);
280             return;
281         }
282
283          String predicate = RelationshipType.HAS_BROADER.value();
284          RelationsCommonList siblingListOuter = getRelations(null, parentCSID, predicate);
285          List<RelationsCommonList.RelationListItem> siblingList = siblingListOuter.getRelationListItem();
286
287         List<RelationsCommonList.RelationListItem> toRemoveList = newList();
288
289
290         RelationsCommonList.RelationListItem item = null;
291         for (RelationsCommonList.RelationListItem sibling : siblingList) {
292             if (thisCSID.equals(sibling.getSubjectCsid())){
293                 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.
294             }
295         }
296         //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.
297         for (RelationsCommonList.RelationListItem self : toRemoveList) {
298             removeFromList(siblingList, self);
299         }
300
301         long siblingSize = siblingList.size();
302         siblingListOuter.setTotalItems(siblingSize);
303         siblingListOuter.setItemsInPage(siblingSize);
304
305         PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME,siblingListOuter);
306         ctx.addOutputPart(relationsPart);
307     }
308
309     public void showAllRelations(DocumentWrapper<DocumentModel> wrapDoc, MultipartServiceContext ctx)   throws Exception {
310         String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
311
312         RelationsCommonList subjectListOuter = getRelations(thisCSID, null, null);   //  nulls are wildcards:  predicate=*, and object=*
313         List<RelationsCommonList.RelationListItem> subjectList = subjectListOuter.getRelationListItem();
314
315         RelationsCommonList objectListOuter = getRelations(null, thisCSID, null);   //  nulls are wildcards:  subject=*, and predicate=*
316         List<RelationsCommonList.RelationListItem> objectList = objectListOuter.getRelationListItem();
317
318         //  MERGE LISTS:
319         subjectList.addAll(objectList);
320
321         //now subjectList actually has records BOTH where thisCSID is subject and object.
322         long relatedSize = subjectList.size();
323         subjectListOuter.setTotalItems(relatedSize);
324         subjectListOuter.setItemsInPage(relatedSize);
325
326         PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME,subjectListOuter);
327         ctx.addOutputPart(relationsPart);
328     }
329
330     public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
331         super.fillAllParts(wrapDoc, action);
332         ServiceContext ctx = getServiceContext();
333         PoxPayloadIn input = (PoxPayloadIn)ctx.getInput();
334         DocumentModel documentModel = (wrapDoc.getWrappedObject());
335         String itemCsid = documentModel.getName();
336
337         //UPDATE and CREATE will call.   Updates relations part
338         RelationsCommonList relationsCommonList = updateRelations(itemCsid, input, wrapDoc);
339
340         PayloadOutputPart payloadOutputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, relationsCommonList);
341         ctx.setProperty(RelationClient.SERVICE_COMMON_LIST_NAME, payloadOutputPart);
342     }
343
344     public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
345         super.completeUpdate(wrapDoc);
346         //now we add part for relations list
347         ServiceContext ctx = getServiceContext();
348         PayloadOutputPart foo = (PayloadOutputPart)ctx.getProperty(RelationClient.SERVICE_COMMON_LIST_NAME);
349         ((PoxPayloadOut)ctx.getOutput()).addPart(foo);
350     }
351
352      /**  updateRelations strategy:
353
354             go through inboundList, remove anything from childList that matches  from childList
355             go through inboundList, remove anything from parentList that matches  from parentList
356             go through parentList, delete all remaining
357             go through childList, delete all remaining
358             go through actionList, add all remaining.
359             check for duplicate children
360             check for more than one parent.
361
362         inboundList                           parentList                      childList          actionList
363         ----------------                          ---------------                  ----------------       ----------------
364         child-a                                   parent-c                        child-a             child-b
365         child-b                                   parent-d                        child-c
366         parent-a
367       */
368     public RelationsCommonList updateRelations(String itemCSID, PoxPayloadIn input, DocumentWrapper<DocumentModel> wrapDoc)
369      throws Exception {
370         PayloadInputPart part = input.getPart(RelationClient.SERVICE_COMMON_LIST_NAME);        //input.getPart("relations_common");
371         if (part == null) {
372             return null;  //nothing to do--they didn't send a list of relations.
373         }
374         RelationsCommonList relationsCommonListBody = (RelationsCommonList) part.getBody();
375
376         ServiceContext ctx = getServiceContext();
377         UriInfo uriInfo = ctx.getUriInfo();
378         MultivaluedMap queryParams = uriInfo.getQueryParameters();
379
380         //Run getList() once as sent to get childListOuter:
381         String predicate = RelationshipType.HAS_BROADER.value();
382         queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
383         queryParams.putSingle(IRelationsManager.SUBJECT_QP, null);
384         queryParams.putSingle(IRelationsManager.SUBJECT_TYPE_QP, null);
385         queryParams.putSingle(IRelationsManager.OBJECT_QP, itemCSID);
386         queryParams.putSingle(IRelationsManager.OBJECT_TYPE_QP, null);
387         RelationsCommonList childListOuter = (new RelationResource()).getList(ctx.getUriInfo());    //magically knows all query params because they are in the context.
388
389         //Now run getList() again, leaving predicate, swapping subject and object, to get parentListOuter.
390         queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
391         queryParams.putSingle(IRelationsManager.SUBJECT_QP, itemCSID);
392         queryParams.putSingle(IRelationsManager.OBJECT_QP, null);
393         RelationsCommonList parentListOuter = (new RelationResource()).getList(ctx.getUriInfo());
394
395         String HAS_BROADER = RelationshipType.HAS_BROADER.value();
396
397         List<RelationsCommonList.RelationListItem> inboundList = relationsCommonListBody.getRelationListItem();
398         List<RelationsCommonList.RelationListItem> actionList = newList();
399         List<RelationsCommonList.RelationListItem> childList = childListOuter.getRelationListItem();
400         List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
401
402         if (parentList.size()>1){
403             throw new Exception("Too many parents for object: "+itemCSID+" list: "+dumpList(parentList, "parentList"));
404         }
405
406         DocumentModel docModel = wrapDoc.getWrappedObject();
407
408         //Do magic replacement of ${itemCSID} and fix URI's.
409         fixupInboundListItems(ctx, inboundList, docModel, itemCSID);
410
411         for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
412             if (inboundItem.getObject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
413                 //then this is an item that says we have a child.  That child is inboundItem
414                 RelationsCommonList.RelationListItem childItem = findInList(childList, inboundItem);
415                 if (childItem != null){
416                     removeFromList(childList,  childItem);    //exists, just take it off delete list
417                 } else {
418                     actionList.add(inboundItem);   //doesn't exist as a child, but is a child.  Add to additions list
419                 }
420                 ensureChildHasNoOtherParents(ctx, queryParams, inboundItem.getSubject().getCsid());
421
422             } else if  (inboundItem.getSubject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
423                 //then this is an item that says we have a parent.  inboundItem is that parent.
424                 RelationsCommonList.RelationListItem parentItem = findInList(parentList, inboundItem);
425                 if (parentItem != null){
426                     removeFromList(parentList,  parentItem);    //exists, just take it off delete list
427                 } else {
428                     actionList.add(inboundItem);   //doesn't exist as a parent, but is a parent. Add to additions list
429                 }
430             }  else {
431                 logger.warn("Element didn't match parent or child, but may have partial fields that match. inboundItem: "+inboundItem);
432                 //not dealing with: hasNarrower or any other predicate.
433             }
434         }
435         String dump = dumpLists(itemCSID, parentList, childList, actionList);
436         //System.out.println("====dump====="+CR+dump);
437         logger.info("~~~~~~~~~~~~~~~~~~~~~~dump~~~~~~~~~~~~~~~~~~~~~~~~"+CR+ dump);
438         deleteRelations(parentList, ctx, "parentList");               //todo: there are items appearing on both lists....april 20.
439         deleteRelations(childList, ctx, "childList");
440         createRelations(actionList, ctx);
441         //We return all elements on the inbound list, since we have just worked to make them exist in the system
442         // and be non-redundant, etc.  That list came from relationsCommonListBody, so it is still attached to it, just pass that back.
443         return relationsCommonListBody;
444     }
445
446     private void ensureChildHasNoOtherParents(ServiceContext ctx, MultivaluedMap queryParams, String childCSID){
447         queryParams.putSingle(IRelationsManager.SUBJECT_QP, childCSID);
448         queryParams.putSingle(IRelationsManager.PREDICATE_QP, RelationshipType.HAS_BROADER.value());
449         queryParams.putSingle(IRelationsManager.OBJECT_QP, null);  //null means ANY
450         RelationsCommonList parentListOuter = (new RelationResource()).getList(ctx.getUriInfo());
451         List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
452         //logger.warn("ensureChildHasNoOtherParents preparing to delete relations on "+childCSID+"\'s parent list: \r\n"+dumpList(parentList, "duplicate parent list"));
453          deleteRelations(parentList, ctx, "parentList-delete");
454     }
455
456     private String dumpLists(String itemCSID,
457                                          List <RelationsCommonList.RelationListItem> parentList,
458                                          List<RelationsCommonList.RelationListItem> childList,
459                                          List<RelationsCommonList.RelationListItem> actionList){
460         StringBuffer sb = new StringBuffer();
461         sb.append("itemCSID: "+itemCSID+CR);
462         sb.append(dumpList(parentList, "parentList"));
463         sb.append(dumpList(childList, "childList"));
464         sb.append(dumpList(actionList, "actionList"));
465         return sb.toString();
466     }
467
468     private final static String CR="\r\n";
469     private final static String T = " ";
470
471     private String dumpList(List <RelationsCommonList.RelationListItem> list, String label){
472         StringBuffer sb = new StringBuffer();
473         String s;
474         if (list.size()>0) sb.append("=========== "+label+" =========="+CR);
475         for (RelationsCommonList.RelationListItem item : list) {
476             s =
477              T + item.getSubject().getCsid()    //+T4 + item.getSubject().getUri()
478                 + T + item.getPredicate()
479                 + T + item.getObject().getCsid()    //+T4  + item.getObject().getUri()
480                 + CR
481                 //+"subject:{"+item.getSubject()+"}\r\n object:{"+item.getObject()+"}"
482                 //+ CR + "relation-record: {"+item+"}"
483                 ;
484             sb.append(s);
485
486          }
487         return sb.toString();
488     }
489
490     /** Performs substitution for ${itemCSID} (see CommonAPI.AuthorityItemCSID_REPLACE for constant)
491      *   and sets URI correctly for related items.
492      *   Operates directly on the items in the list.  Does not change the list ordering, does not add or remove any items.
493      */
494     protected void fixupInboundListItems(ServiceContext ctx,
495                                                             List<RelationsCommonList.RelationListItem> inboundList,
496                                                             DocumentModel docModel,
497                                                             String itemCSID) throws Exception {
498         String thisURI = this.getUri(docModel);
499         // WARNING:  the two code blocks below are almost identical  and seem to ask to be put in a generic method.
500         //                    beware of the little diffs in  inboundItem.setObjectCsid(itemCSID); and   inboundItem.setSubjectCsid(itemCSID); in the two blocks.
501         for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
502             RelationsDocListItem inboundItemObject = inboundItem.getObject();
503             RelationsDocListItem inboundItemSubject = inboundItem.getSubject();
504
505             if (inboundItemObject.getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)){
506                 inboundItem.setObjectCsid(itemCSID);
507                 inboundItemObject.setCsid(itemCSID);
508                 inboundItemObject.setUri(getUri(docModel));
509             } else {
510                 String objectCsid = inboundItemObject.getCsid();
511                 DocumentModel itemDocModel =  NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, objectCsid);    //null if not found.
512                 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
513                 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
514                 inboundItemObject.setUri(uri);    //CSPACE-4037
515             }
516             uriPointsToSameAuthority(thisURI, inboundItemObject.getUri());    //CSPACE-4042
517
518             if (inboundItemSubject.getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)){
519                 inboundItem.setSubjectCsid(itemCSID);
520                 inboundItemSubject.setCsid(itemCSID);
521                 inboundItemSubject.setUri(getUri(docModel));
522             } else {
523                 String subjectCsid =inboundItemSubject.getCsid();
524                 DocumentModel itemDocModel =  NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, subjectCsid);    //null if not found.
525                 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
526                 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
527                 inboundItemSubject.setUri(uri);    //CSPACE-4037
528             }
529             uriPointsToSameAuthority(thisURI, inboundItemSubject.getUri());  //CSPACE-4042
530
531         }
532     }
533
534     public RepositoryClient getRepositoryClient(ServiceContext ctx) {
535         RepositoryClient repositoryClient = RepositoryClientFactory.getInstance().getClient(ctx.getRepositoryClientName());
536         return repositoryClient;
537     }
538
539     // this method calls the RelationResource to have it create the relations and persist them.
540     private void createRelations(List<RelationsCommonList.RelationListItem> inboundList, ServiceContext ctx){
541          for (RelationsCommonList.RelationListItem item : inboundList) {
542              RelationsCommon rc = new RelationsCommon();
543              //rc.setCsid(item.getCsid());
544              //todo: assignTo(item, rc);
545              RelationsDocListItem itemSubject = item.getSubject();
546              RelationsDocListItem itemObject = item.getObject();
547
548              String subjectCsid =  itemSubject.getCsid();
549              rc.setDocumentId1(subjectCsid);
550              rc.setSubjectCsid(subjectCsid);
551
552              String objCsid = item.getObject().getCsid();
553              rc.setDocumentId2(objCsid);
554              rc.setObjectCsid(objCsid);
555
556              rc.setRelationshipType(item.getPredicate());
557              //RelationshipType  foo = (RelationshipType.valueOf(item.getPredicate())) ;
558              //rc.setPredicate(foo);     //this must be one of the type found in the enum in  services/jaxb/src/main/resources/relations_common.xsd
559
560              rc.setDocumentType1(itemSubject.getDocumentType());
561              rc.setDocumentType2(itemObject.getDocumentType());
562
563              rc.setSubjectUri(itemSubject.getUri());
564              rc.setObjectUri(itemObject.getUri());
565
566
567             PoxPayloadOut payloadOut = new PoxPayloadOut(RelationClient.SERVICE_PAYLOAD_NAME);
568             PayloadOutputPart outputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMONPART_NAME, rc);
569             payloadOut.addPart(outputPart);
570             //System.out.println("\r\n==== TO CREATE: "+rc.getDocumentId1()+"==>"+rc.getPredicate()+"==>"+rc.getDocumentId2());
571             RelationResource relationResource = new RelationResource();
572             Object res = relationResource.create(ctx.getUriInfo(), payloadOut.toXML());    //NOTE ui recycled from above to pass in unknown query params.
573         }
574     }
575      private void deleteRelations(List<RelationsCommonList.RelationListItem> list,ServiceContext ctx, String listName){
576           try {
577               //if (list.size()>0){ logger.info("==== deleteRelations from : "+listName); }
578               for (RelationsCommonList.RelationListItem item : list) {
579                   RelationResource relationResource = new RelationResource();
580                   //logger.info("==== TO DELETE: " + item.getCsid() + ": " + item.getSubject().getCsid() + "--" + item.getPredicate() + "-->" + item.getObject().getCsid());
581                   Object res = relationResource.delete(item.getCsid());
582               }
583           } catch (Throwable t){
584               String msg = "Unable to deleteRelations: "+ Tools.errorToString(t, true);
585               logger.error(msg);
586           }
587      }
588
589     private  List<RelationsCommonList.RelationListItem> newList(){
590         List<RelationsCommonList.RelationListItem> result = new ArrayList<RelationsCommonList.RelationListItem>();
591         return result;
592     }
593      protected List<RelationsCommonList.RelationListItem> cloneList(List<RelationsCommonList.RelationListItem> inboundList){
594         List<RelationsCommonList.RelationListItem> result = newList();
595         for (RelationsCommonList.RelationListItem item: inboundList){
596             result.add(item);
597         }
598         return result;
599     }
600      private RelationsCommonList.RelationListItem findInList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item){
601          for (RelationsCommonList.RelationListItem listItem : list) {
602              if (itemsEqual(listItem, item)){   //equals must be defined, else
603                 return listItem;
604              }
605          }
606          return null;
607      }
608
609     private boolean itemsEqual(RelationsCommonList.RelationListItem item, RelationsCommonList.RelationListItem item2){
610         if (item==null || item2==null){
611             return false;
612         }
613         RelationsDocListItem subj1 = item.getSubject();
614         RelationsDocListItem subj2 = item2.getSubject();
615         RelationsDocListItem obj1 = item.getObject();
616         RelationsDocListItem obj2 = item2.getObject();
617
618         return     (subj1.getCsid().equals(subj2.getCsid()))
619                 && (obj1.getCsid().equals(obj1.getCsid()))
620                 && ( (item.getPredicate().equals(item2.getPredicate()))
621                 && (item.getRelationshipType().equals(item2.getRelationshipType()))   )
622                 && (obj1.getDocumentType().equals(obj2.getDocumentType()))
623                 && (subj1.getDocumentType().equals(subj2.getDocumentType())) ;
624     }
625
626      private void removeFromList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item){
627         list.remove(item);
628     }
629
630     /* don't even THINK of re-using this method.
631      * String example_uri = "/locationauthorities/7ec60f01-84ab-4908-9a6a/items/a5466530-713f-43b4-bc05";
632      */
633     private String extractInAuthorityCSID(String uri){
634         String IN_AUTHORITY_REGEX      = "/(.*?)/(.*?)/(.*)";
635         Pattern p = Pattern.compile(IN_AUTHORITY_REGEX);
636         Matcher m = p.matcher(uri);
637         if (m.find()){
638             if (m.groupCount()<3){
639                 logger.warn("REGEX-WRONG-GROUPCOUNT looking in "+uri);
640                 return "";
641             } else {
642                 //String service = m.group(1);
643                 String inauth = m.group(2);
644                 //String theRest = m.group(3);
645                 return inauth;
646                 //print("service:"+service+", inauth:"+inauth+", rest:"+rest);
647             }
648         } else {
649             logger.warn("REGEX-NOT-MATCHED looking in "+uri);
650             return "";
651         }
652     }
653
654     //ensures CSPACE-4042
655     protected void uriPointsToSameAuthority(String thisURI, String inboundItemURI) throws Exception {
656         String authorityCSID = extractInAuthorityCSID(thisURI);
657         String authorityCSIDForInbound = extractInAuthorityCSID(inboundItemURI);
658         if  (      Tools.isBlank(authorityCSID)
659                 || Tools.isBlank(authorityCSIDForInbound)
660                 ||   ( ! authorityCSID.equalsIgnoreCase(authorityCSIDForInbound) )
661             )  {
662                 throw new Exception("Item URI "+thisURI+" must point to same authority as related item: "+inboundItemURI);
663             }
664     }
665
666     //================= TODO: move this to common, refactoring this and  CollectionObjectResource.java
667     public RelationsCommonList getRelations(String subjectCSID, String objectCSID, String predicate) throws Exception {
668         ServiceContext ctx = getServiceContext();
669         MultivaluedMap queryParams = ctx.getQueryParams();
670         queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
671         queryParams.putSingle(IRelationsManager.SUBJECT_QP, subjectCSID);
672         queryParams.putSingle(IRelationsManager.OBJECT_QP, objectCSID);
673
674         RelationResource relationResource = new RelationResource();
675         RelationsCommonList relationsCommonList = relationResource.getList(ctx.getUriInfo());
676         return relationsCommonList;
677     }
678     //============================= END TODO refactor ==========================
679
680 }
681