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 java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.ListIterator;
33 import org.collectionspace.services.client.PayloadInputPart;
34 import org.collectionspace.services.client.PayloadOutputPart;
35 import org.collectionspace.services.client.PoxPayloadIn;
36 import org.collectionspace.services.client.PoxPayloadOut;
37 import org.collectionspace.services.client.RelationClient;
38 //import org.collectionspace.services.common.authority.AuthorityItemRelations;
39 import org.collectionspace.services.common.api.CommonAPI;
40 import org.collectionspace.services.common.api.Tools;
41 import org.collectionspace.services.common.context.MultipartServiceContext;
42 import org.collectionspace.services.common.context.ServiceContext;
43 import org.collectionspace.services.common.document.DocumentWrapper;
44 import org.collectionspace.services.common.relation.IRelationsManager;
45 import org.collectionspace.services.common.service.ObjectPartType;
46 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
47 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
48 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
49 import org.collectionspace.services.relation.RelationResource;
50 import org.collectionspace.services.relation.RelationsCommon;
51 import org.collectionspace.services.relation.RelationsCommonList;
52 import org.collectionspace.services.relation.RelationsDocListItem;
53 import org.collectionspace.services.relation.RelationshipType;
54 import org.nuxeo.ecm.core.api.DocumentModel;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
58 import javax.management.relation.Relation;
59 import javax.ws.rs.core.MultivaluedMap;
60 import javax.ws.rs.core.UriInfo;
63 * AuthorityItemDocumentModelHandler
65 * $LastChangedRevision: $
68 public abstract class AuthorityItemDocumentModelHandler<AICommon, AICommonList>
69 extends RemoteDocumentModelHandlerImpl<AICommon, AICommonList> {
71 private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
73 private String authorityItemCommonSchemaName;
75 //private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
77 * item is used to stash JAXB object to use when handle is called
78 * for Action.CREATE, Action.UPDATE or Action.GET
80 protected AICommon item;
82 * itemList is stashed when handle is called
85 protected AICommonList itemList;
88 * inVocabulary is the parent Authority for this context
90 protected String inAuthority;
92 public AuthorityItemDocumentModelHandler(String authorityItemCommonSchemaName) {
93 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
96 public String getInAuthority() {
100 public void setInAuthority(String inAuthority) {
101 this.inAuthority = inAuthority;
105 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleCreate(org.collectionspace.services.common.document.DocumentWrapper)
108 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
109 // first fill all the parts of the document
110 super.handleCreate(wrapDoc);
111 handleInAuthority(wrapDoc.getWrappedObject());
115 * Check the logic around the parent pointer. Note that we only need do this on
116 * create, since we have logic to make this read-only on update.
120 * @throws Exception the exception
122 private void handleInAuthority(DocumentModel docModel) throws Exception {
123 docModel.setProperty(authorityItemCommonSchemaName,
124 AuthorityItemJAXBSchema.IN_AUTHORITY, inAuthority);
129 * getCommonPart get associated item
133 public AICommon getCommonPart() {
138 public void setCommonPart(AICommon item) {
143 * getCommonPartList get associated item (for index/GET_ALL)
147 public AICommonList getCommonPartList() {
152 public void setCommonPartList(AICommonList itemList) {
153 this.itemList = itemList;
157 public AICommon extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc)
159 throw new UnsupportedOperationException();
163 public void fillCommonPart(AICommon itemObject, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
164 throw new UnsupportedOperationException();
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)
171 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
173 Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
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);
181 return unQObjectProperties;
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
191 public void filterReadOnlyPropertiesForPart(
192 Map<String, Object> objectProps, ObjectPartType partMeta) {
193 super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
194 objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
195 objectProps.remove(AuthorityItemJAXBSchema.CSID);
199 public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc)
201 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
202 super.extractAllParts(wrapDoc);
203 String showRelations = ctx.getQueryParams().getFirst(CommonAPI.showRelations_QP);
204 if (!Tools.isTrue(showRelations)){
207 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
209 String predicate = RelationshipType.HAS_BROADER.value();
210 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
211 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
213 RelationsCommonList childrenListOuter = getRelations(null, thisCSID, predicate);
214 List<RelationsCommonList.RelationListItem> childrenList = childrenListOuter.getRelationListItem();
216 //Assume that there are more children than parents. Will be true for parent/child, but maybe not for other relations.
217 //Now add all parents to our childrenList, to be able to return just one list of consolidated results.
218 //Not optimal, but that's the current design spec.
220 for (RelationsCommonList.RelationListItem parent : parentList) {
221 childrenList.add(parent);
224 long childrenSize = childrenList.size();
225 childrenListOuter.setTotalItems(childrenSize);
226 childrenListOuter.setItemsInPage(childrenListOuter.getItemsInPage()+added);
228 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, childrenListOuter);
229 ctx.addOutputPart(relationsPart);
232 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
233 super.fillAllParts(wrapDoc, action);
234 ServiceContext ctx = getServiceContext();
235 PoxPayloadIn input = (PoxPayloadIn)ctx.getInput();
236 DocumentModel documentModel = (wrapDoc.getWrappedObject());
237 String itemCsid = documentModel.getName();
239 //TODO: create all relations.... UPDATE and CREATE will call. Updates AuthorityItem part
240 RelationsCommonList relationsCommonList = updateRelations(itemCsid, input);
241 PayloadOutputPart payloadOutputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, relationsCommonList);
242 ctx.setProperty(RelationClient.SERVICE_COMMON_LIST_NAME, payloadOutputPart);
245 public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
246 super.completeUpdate(wrapDoc);
247 //now we add part for relations list
248 ServiceContext ctx = getServiceContext();
249 PayloadOutputPart foo = (PayloadOutputPart)ctx.getProperty(RelationClient.SERVICE_COMMON_LIST_NAME);
250 ((PoxPayloadOut)ctx.getOutput()).addPart(foo);
253 //===================================================================
255 for (RelationsCommonList.RelationListItem parentListItem : parentList.getRelationListItem()) {
256 System.out.println(" parentListItems " + parentListItem);
257 //todo: if num-parents > 1 then complain.
258 //todo: if not found in update list, remove from system
259 //todo: if update list item not found in parent list, add to system.
261 for (RelationsCommonList.RelationListItem childListItem : childList.getRelationListItem()) {
262 System.out.println(" childListItem: " + childListItem);
263 //todo: if not found in update list, remove from system
264 //todo: if update list item not found in child list, add to system.
270 public RelationsCommonList updateRelations(String itemCSID, PoxPayloadIn input) throws Exception {
271 PayloadInputPart part = input.getPart(RelationClient.SERVICE_COMMON_LIST_NAME); //input.getPart("relations_common");
273 //System.out.println("Nothing to do in updateRelations: " + input);
276 RelationsCommonList relationsCommonListBody = (RelationsCommonList) part.getBody();
278 ServiceContext ctx = getServiceContext();
279 UriInfo uriInfo = ctx.getUriInfo();
280 MultivaluedMap queryParams = uriInfo.getQueryParameters();
282 String predicate = RelationshipType.HAS_BROADER.value();
283 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
284 queryParams.putSingle(IRelationsManager.SUBJECT_QP, null);
285 queryParams.putSingle(IRelationsManager.SUBJECT_TYPE_QP, null);
286 queryParams.putSingle(IRelationsManager.OBJECT_QP, itemCSID);
287 queryParams.putSingle(IRelationsManager.OBJECT_TYPE_QP, null);
289 RelationsCommonList childListOuter = (new RelationResource()).getList(ctx.getUriInfo()); //magically knows all query params because they are in the context.
291 //Leave predicate, swap subject and object.
292 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
293 queryParams.putSingle(IRelationsManager.SUBJECT_QP, itemCSID);
294 queryParams.putSingle(IRelationsManager.OBJECT_QP, null);
296 RelationsCommonList parentListOuter = (new RelationResource()).getList(ctx.getUriInfo());
298 go through inboundList, remove anything from childList that matches from childList
299 go through inboundList, remove anything from parentList that matches from parentList
300 go through parentList, delete all remaining
301 go through childList, delete all remaining
302 go through actionList, add all remaining.
303 check for duplicate children
304 check for more than one parent.
306 inboundList parentList childList actionList
307 ---------------- --------------- ---------------- ----------------
308 child-a parent-c child-a child-b
309 child-b parent-d child-c
312 String HAS_BROADER = RelationshipType.HAS_BROADER.value();
314 List<RelationsCommonList.RelationListItem> inboundList = relationsCommonListBody.getRelationListItem();
315 List<RelationsCommonList.RelationListItem> actionList = newList();
316 List<RelationsCommonList.RelationListItem> childList = childListOuter.getRelationListItem();
317 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
319 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
320 if (inboundItem.getObject().getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)){
321 inboundItem.setObjectCsid(itemCSID);
322 inboundItem.getObject().setCsid(itemCSID);
324 if (inboundItem.getSubject().getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)){
325 inboundItem.setSubjectCsid(itemCSID);
326 inboundItem.getSubject().setCsid(itemCSID);
328 if (inboundItem.getObject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
329 //then this is an item that says we have a child.
330 RelationsCommonList.RelationListItem childItem = findInList(childList, inboundItem);
331 if (childItem != null){
332 removeFromList(childList, childItem); //exists, just take it off delete list
334 actionList.add(inboundItem); //doesn't exist as a child, but is a child. Add to additions list
336 } else if (inboundItem.getSubject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
337 //then this is an item that says we have a parent
338 RelationsCommonList.RelationListItem parentItem = findInList(parentList, inboundItem);
339 if (parentItem != null){
340 removeFromList(parentList, parentItem); //exists, just take it off delete list
342 actionList.add(inboundItem); //doesn't exist as a parent, but is a parent. Add to additions list
346 System.out.println("\r\n\r\n================\r\n Element didn't match parent or child, but may have partial fields that match. inboundItem: "+inboundItem);
347 //not dealing with: hasNarrower or any other predicate.
350 deleteRelations(parentList, ctx); //todo: there are items appearing on both lists....april 20.
351 deleteRelations(childList, ctx);
352 createRelations(actionList, ctx);
354 return relationsCommonListBody;
357 private void createRelations(List<RelationsCommonList.RelationListItem> inboundList, ServiceContext ctx){
358 for (RelationsCommonList.RelationListItem item : inboundList) {
359 RelationsCommon rc = new RelationsCommon();
360 //rc.setCsid(item.getCsid());
361 String itemCsid = item.getSubject().getCsid();
362 rc.setDocumentId1(itemCsid);
363 rc.setSubjectCsid(itemCsid);
365 String objCsid = item.getObject().getCsid();
366 rc.setDocumentId2(objCsid);
367 rc.setObjectCsid(objCsid);
369 rc.setRelationshipType(item.getPredicate());
370 //RelationshipType foo = (RelationshipType.valueOf(item.getPredicate())) ;
371 //rc.setPredicate(foo);
373 rc.setDocumentType1(item.getSubject().getDocumentType());
374 rc.setDocumentType2(item.getObject().getDocumentType());
376 PoxPayloadOut payloadOut = new PoxPayloadOut(RelationClient.SERVICE_PAYLOAD_NAME);
377 PayloadOutputPart outputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMONPART_NAME, rc);
378 payloadOut.addPart(outputPart);
379 //System.out.println("\r\n==== TO CREATE: "+rc.getDocumentId1()+"==>"+rc.getPredicate()+"==>"+rc.getDocumentId2());
380 RelationResource relationResource = new RelationResource();
381 Object res = relationResource.create(ctx.getUriInfo(), payloadOut.toXML()); //NOTE ui recycled from above to pass in unknown query params.
384 private void deleteRelations(List<RelationsCommonList.RelationListItem> list,ServiceContext ctx){
386 for (RelationsCommonList.RelationListItem inboundItem : list) {
387 RelationResource relationResource = new RelationResource();
388 //System.out.println("\r\n==== TO DELETE: "+inboundItem.getCsid());
389 Object res = relationResource.delete(inboundItem.getCsid());
391 } catch (Throwable t){
392 String msg = "Unable to deleteRelations: "+ Tools.errorToString(t, true);
397 private List<RelationsCommonList.RelationListItem> newList(){
398 List<RelationsCommonList.RelationListItem> result = new ArrayList<RelationsCommonList.RelationListItem>();
401 protected List<RelationsCommonList.RelationListItem> cloneList(List<RelationsCommonList.RelationListItem> inboundList){
402 List<RelationsCommonList.RelationListItem> result = newList();
403 for (RelationsCommonList.RelationListItem item: inboundList){
408 private RelationsCommonList.RelationListItem findInList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item){
409 for (RelationsCommonList.RelationListItem listItem : list) {
410 if (itemsEqual(listItem, item)){ //equals must be defined, else
417 private boolean itemsEqual(RelationsCommonList.RelationListItem item, RelationsCommonList.RelationListItem item2){
418 if (item==null || item2==null){
421 RelationsDocListItem subj1 = item.getSubject();
422 RelationsDocListItem subj2 = item2.getSubject();
423 RelationsDocListItem obj1 = item.getObject();
424 RelationsDocListItem obj2 = item2.getObject();
426 return (subj1.getCsid().equals(subj2.getCsid()))
427 && (obj1.getCsid().equals(obj1.getCsid()))
428 && ( (item.getPredicate().equals(item2.getPredicate()))
429 && (item.getRelationshipType().equals(item2.getRelationshipType())) )
430 && (obj1.getDocumentType().equals(obj2.getDocumentType()))
431 && (subj1.getDocumentType().equals(subj2.getDocumentType())) ;
434 private void removeFromList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item){
437 //================= TODO: move this to common, refactoring this and CollectionObjectResource.java
439 public RelationsCommonList getRelations(String subjectCSID, String objectCSID, String predicate) throws Exception {
440 ServiceContext ctx = getServiceContext();
441 MultivaluedMap queryParams = ctx.getQueryParams();
442 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
443 queryParams.putSingle(IRelationsManager.SUBJECT_QP, subjectCSID);
444 queryParams.putSingle(IRelationsManager.OBJECT_QP, objectCSID);
446 RelationResource relationResource = new RelationResource();
447 RelationsCommonList relationsCommonList = relationResource.getList(ctx.getUriInfo());
448 return relationsCommonList;
451 //============================= END refactor ==========================