]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
1a4271ac71cfa888c6bad8bdcc98818c98986643
[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.nuxeo.client.java;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32
33 import javax.ws.rs.WebApplicationException;
34 import javax.ws.rs.core.MediaType;
35 import javax.ws.rs.core.Response;
36 import javax.xml.bind.JAXBElement;
37
38 import org.collectionspace.services.authorization.AccountPermission;
39 import org.collectionspace.services.jaxb.AbstractCommonList;
40 import org.collectionspace.services.lifecycle.TransitionDef;
41 import org.collectionspace.services.client.CollectionSpaceClient;
42 import org.collectionspace.services.client.PayloadInputPart;
43 import org.collectionspace.services.client.PayloadOutputPart;
44 import org.collectionspace.services.client.PoxPayloadIn;
45 import org.collectionspace.services.client.PoxPayloadOut;
46 import org.collectionspace.services.client.workflow.WorkflowClient;
47 import org.collectionspace.services.common.authorityref.AuthorityRefList;
48 import org.collectionspace.services.common.context.JaxRsContext;
49 import org.collectionspace.services.common.context.MultipartServiceContext;
50 import org.collectionspace.services.common.context.ServiceContext;
51 import org.collectionspace.services.common.document.BadRequestException;
52 import org.collectionspace.services.common.document.DocumentException;
53 import org.collectionspace.services.common.document.DocumentUtils;
54 import org.collectionspace.services.common.document.DocumentWrapper;
55 import org.collectionspace.services.common.document.DocumentFilter;
56 import org.collectionspace.services.common.profile.Profiler;
57 import org.collectionspace.services.common.security.SecurityUtils;
58 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
59 import org.collectionspace.services.common.api.RefNameUtils;
60 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils;
61 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthRefConfigInfo;
62 import org.collectionspace.services.config.service.DocHandlerParams;
63 import org.collectionspace.services.config.service.ListResultField;
64 import org.collectionspace.services.config.service.ObjectPartType;
65 import org.collectionspace.services.config.service.ServiceBindingType;
66 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
67 import org.dom4j.Element;
68
69 import org.nuxeo.ecm.core.api.DocumentModel;
70 import org.nuxeo.ecm.core.api.DocumentModelList;
71 import org.nuxeo.ecm.core.api.model.PropertyException;
72 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
73
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76
77 /**
78  * RemoteDocumentModelHandler
79  *
80  * $LastChangedRevision: $
81  * $LastChangedDate: $
82  * @param <T> 
83  * @param <TL> 
84  */
85 public abstract class   RemoteDocumentModelHandlerImpl<T, TL>
86         extends DocumentModelHandler<T, TL> {
87
88     /** The logger. */
89     private final Logger logger = LoggerFactory.getLogger(RemoteDocumentModelHandlerImpl.class);
90
91     /* (non-Javadoc)
92      * @see org.collectionspace.services.common.document.AbstractDocumentHandlerImpl#setServiceContext(org.collectionspace.services.common.context.ServiceContext)
93      */
94     @Override
95     public void setServiceContext(ServiceContext ctx) {  //FIXME: Apply proper generics to ServiceContext<PoxPayloadIn, PoxPayloadOut>
96         if (ctx instanceof MultipartServiceContext) {
97             super.setServiceContext(ctx);
98         } else {
99             throw new IllegalArgumentException("setServiceContext requires instance of "
100                     + MultipartServiceContext.class.getName());
101         }
102     }
103
104     /*
105      * Returns the document handler parameters that were loaded at startup from the
106      * tenant bindings config file.
107      */
108         public DocHandlerParams.Params getDocHandlerParams() throws DocumentException {
109                 MultipartServiceContext sc = (MultipartServiceContext) getServiceContext();
110                 ServiceBindingType sb = sc.getServiceBinding();
111                 DocHandlerParams dhb = sb.getDocHandlerParams();
112                 if (dhb != null && dhb.getParams() != null) {
113                         return dhb.getParams();
114                 }
115                 throw new DocumentException("No DocHandlerParams configured for: "
116                                 + sb.getName());
117         }
118     
119     @Override
120     public boolean supportsHierarchy() {
121         boolean result;
122         
123         DocHandlerParams.Params params = null;
124         try {
125                         params = getDocHandlerParams();
126                 } catch (DocumentException e) {
127                         // TODO Auto-generated catch block
128                         logger.error(String.format("Could not get document handler params for class %s", this.getClass().getName()), e);
129                 }
130                 result = params.isSupportsHierarchy();
131         
132         return result;
133     }
134
135         @Override
136         public void handleWorkflowTransition(DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef)
137                         throws Exception {
138                 // Do nothing by default, but children can override if they want.  The really workflow transition happens in the WorkflowDocumemtModelHandler class
139         }
140     
141     /* (non-Javadoc)
142      * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#completeUpdate(org.collectionspace.services.common.document.DocumentWrapper)
143      */
144     @Override
145     public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
146         DocumentModel docModel = wrapDoc.getWrappedObject();
147         //return at least those document part(s) that were received
148         Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
149         MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
150         PoxPayloadIn input = ctx.getInput();
151         if (input != null) {
152                 List<PayloadInputPart> inputParts = ctx.getInput().getParts();
153                 for (PayloadInputPart part : inputParts) {
154                     String partLabel = part.getLabel();
155                 try{
156                     ObjectPartType partMeta = partsMetaMap.get(partLabel);
157                     // CSPACE-4030 - generates NPE if the part is missing.
158                     if(partMeta!=null) {
159                             Map<String, Object> unQObjectProperties = extractPart(docModel, partLabel, partMeta);
160                             if(unQObjectProperties!=null) {
161                                 addOutputPart(unQObjectProperties, partLabel, partMeta);
162                             }
163                     }
164                 } catch (Throwable t){
165
166                     logger.error("Unable to addOutputPart: "+partLabel
167                                                +" in serviceContextPath: "+this.getServiceContextPath()
168                                                +" with URI: "+this.getServiceContext().getUriInfo().getPath()
169                                                +" error: "+t);
170                 }
171                 }
172         } else {
173                 if (logger.isWarnEnabled() == true) {
174                         logger.warn("MultipartInput part was null for document id = " +
175                                         docModel.getName());
176                 }
177         }
178     }
179
180     /**
181      * Adds the output part.
182      *
183      * @param unQObjectProperties the un q object properties
184      * @param schema the schema
185      * @param partMeta the part meta
186      * @throws Exception the exception
187      * MediaType.APPLICATION_XML_TYPE
188      */
189     protected void addOutputPart(Map<String, Object> unQObjectProperties, String schema, ObjectPartType partMeta)
190             throws Exception {
191         Element doc = DocumentUtils.buildDocument(partMeta, schema,
192                 unQObjectProperties);
193         if (logger.isTraceEnabled() == true) {
194             logger.trace(doc.asXML());
195         }
196         MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
197         ctx.addOutputPart(schema, doc, partMeta.getContent().getContentType());
198     }
199     
200     /**
201      * Extract paging info.
202      *
203      * @param commonsList the commons list
204      * @return the tL
205      * @throws Exception the exception
206      */
207     public TL extractPagingInfo(TL theCommonList, DocumentWrapper<DocumentModelList> wrapDoc)
208             throws Exception {
209         AbstractCommonList commonList = (AbstractCommonList) theCommonList;
210
211         DocumentFilter docFilter = this.getDocumentFilter();
212         long pageSize = docFilter.getPageSize();
213         long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize;
214         // set the page size and page number
215         commonList.setPageNum(pageNum);
216         commonList.setPageSize(pageSize);
217         DocumentModelList docList = wrapDoc.getWrappedObject();
218         // Set num of items in list. this is useful to our testing framework.
219         commonList.setItemsInPage(docList.size());
220         // set the total result size
221         commonList.setTotalItems(docList.totalSize());
222
223         return (TL) commonList;
224     }
225
226     /* (non-Javadoc)
227      * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#extractAllParts(org.collectionspace.services.common.document.DocumentWrapper)
228      */
229     @Override
230     public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc)
231             throws Exception {
232
233         DocumentModel docModel = wrapDoc.getWrappedObject();
234         String[] schemas = docModel.getDeclaredSchemas();
235         Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
236         for (String schema : schemas) {
237             ObjectPartType partMeta = partsMetaMap.get(schema);
238             if (partMeta == null) {
239                 continue; // unknown part, ignore
240             }
241             Map<String, Object> unQObjectProperties = extractPart(docModel, schema, partMeta);
242             if(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA.equals(schema)) {
243                 addExtraCoreValues(docModel, unQObjectProperties);
244             }
245             addOutputPart(unQObjectProperties, schema, partMeta);
246         }
247         addAccountPermissionsPart();
248     }
249     
250     private void addExtraCoreValues(DocumentModel docModel, Map<String, Object> unQObjectProperties)
251                 throws Exception {
252         unQObjectProperties.put(CollectionSpaceClient.COLLECTIONSPACE_CORE_WORKFLOWSTATE, docModel.getCurrentLifeCycleState());
253     }
254     
255     private void addAccountPermissionsPart() throws Exception {
256         Profiler profiler = new Profiler("addAccountPermissionsPart():", 1);
257         profiler.start();
258         
259         MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
260         String currentServiceName = ctx.getServiceName();
261         String workflowSubResource = "/";
262         JaxRsContext jaxRsContext = ctx.getJaxRsContext();
263         if (jaxRsContext != null) {
264                 String resourceName = SecurityUtils.getResourceName(jaxRsContext.getUriInfo());
265                 workflowSubResource = workflowSubResource + resourceName + WorkflowClient.SERVICE_PATH + "/";
266         } else {
267                 workflowSubResource = workflowSubResource + currentServiceName + WorkflowClient.SERVICE_AUTHZ_SUFFIX;
268         }
269         AccountPermission accountPermission = JpaStorageUtils.getAccountPermissions(JpaStorageUtils.CS_CURRENT_USER,
270                         currentServiceName, workflowSubResource);
271         org.collectionspace.services.authorization.ObjectFactory objectFactory =
272                 new org.collectionspace.services.authorization.ObjectFactory();
273         JAXBElement<AccountPermission> ap = objectFactory.createAccountPermission(accountPermission);
274         PayloadOutputPart accountPermissionPart = new PayloadOutputPart("account_permission", ap);
275         ctx.addOutputPart(accountPermissionPart);
276         
277         profiler.stop();
278     }
279
280     /* (non-Javadoc)
281      * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#fillAllParts(org.collectionspace.services.common.document.DocumentWrapper)
282      */
283     @Override
284     public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
285
286         //TODO filling extension parts should be dynamic
287         //Nuxeo APIs lack to support stream/byte[] input, get/setting properties is
288         //not an ideal way of populating objects.
289         DocumentModel docModel = wrapDoc.getWrappedObject();
290         MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
291         PoxPayloadIn input = ctx.getInput();
292         if (input.getParts().isEmpty()) {
293             String msg = "No payload found!";
294             logger.error(msg + "Ctx=" + getServiceContext().toString());
295             throw new BadRequestException(msg);
296         }
297
298         Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
299
300         //iterate over parts received and fill those parts
301         List<PayloadInputPart> inputParts = input.getParts();
302         for (PayloadInputPart part : inputParts) {
303
304             String partLabel = part.getLabel();
305             if (partLabel == null) {
306                 String msg = "Part label is missing or empty!";
307                 logger.error(msg + "Ctx=" + getServiceContext().toString());
308                 throw new BadRequestException(msg);
309             }
310
311             //skip if the part is not in metadata
312             ObjectPartType partMeta = partsMetaMap.get(partLabel);
313             if (partMeta == null) {
314                 continue;
315             }
316             fillPart(part, docModel, partMeta, action, ctx);
317         }//rof
318
319     }
320
321     /**
322      * fillPart fills an XML part into given document model
323      * @param part to fill
324      * @param docModel for the given object
325      * @param partMeta metadata for the object to fill
326      * @throws Exception
327      */
328     protected void fillPart(PayloadInputPart part, DocumentModel docModel,
329             ObjectPartType partMeta, Action action, ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
330             throws Exception {
331         //check if this is an xml part
332         if (part.getMediaType().equals(MediaType.APPLICATION_XML_TYPE)) {
333                 Element element = part.getElementBody();
334             Map<String, Object> objectProps = DocumentUtils.parseProperties(partMeta, element, ctx);
335                 if (action == Action.UPDATE) {
336                     this.filterReadOnlyPropertiesForPart(objectProps, partMeta);
337                 }
338                 docModel.setProperties(partMeta.getLabel(), objectProps);
339             }
340         }
341
342     /**
343      * Filters out read only properties, so they cannot be set on update.
344      * TODO: add configuration support to do this generally
345      * @param objectProps the properties parsed from the update payload
346      * @param partMeta metadata for the object to fill
347      */
348     public void filterReadOnlyPropertiesForPart(
349             Map<String, Object> objectProps, ObjectPartType partMeta) {
350         // Should add in logic to filter most of the core items on update
351         if(partMeta.getLabel().equalsIgnoreCase(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA)) {
352                 objectProps.remove(CollectionSpaceClient.COLLECTIONSPACE_CORE_CREATED_AT);
353                 objectProps.remove(CollectionSpaceClient.COLLECTIONSPACE_CORE_CREATED_BY);
354                 objectProps.remove(CollectionSpaceClient.COLLECTIONSPACE_CORE_URI);
355                 objectProps.remove(CollectionSpaceClient.COLLECTIONSPACE_CORE_TENANTID);
356                 // Note that the updatedAt/updatedBy fields are set internally
357                 // in DocumentModelHandler.handleCoreValues().
358         }
359     }
360
361     /**
362      * extractPart extracts an XML object from given DocumentModel
363      * @param docModel
364      * @param schema of the object to extract
365      * @param partMeta metadata for the object to extract
366      * @throws Exception
367      */
368     protected Map<String, Object> extractPart(DocumentModel docModel, String schema)
369             throws Exception {
370         return extractPart(docModel, schema, (Map<String, Object>)null);
371     }
372     
373     /**
374      * extractPart extracts an XML object from given DocumentModel
375      * @param docModel
376      * @param schema of the object to extract
377      * @param partMeta metadata for the object to extract
378      * @throws Exception
379      */
380     @Deprecated
381     protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
382             throws Exception {
383         return extractPart(docModel, schema, partMeta, null);
384     }    
385
386     /**
387      * extractPart extracts an XML object from given DocumentModel
388      * @param docModel
389      * @param schema of the object to extract
390      * @param partMeta metadata for the object to extract
391      * @throws Exception
392      */
393     protected Map<String, Object> extractPart(
394             DocumentModel docModel, 
395             String schema,
396             Map<String, Object> addToMap)
397             throws Exception {
398         Map<String, Object> result = null;
399
400         Map<String, Object> objectProps = docModel.getProperties(schema);
401         if (objectProps != null) {
402                 //unqualify properties before sending the doc over the wire (to save bandwidh)
403                 //FIXME: is there a better way to avoid duplication of a Map/Collection?
404                 Map<String, Object> unQObjectProperties =
405                         (addToMap != null) ? addToMap : (new HashMap<String, Object>());
406                 Set<Entry<String, Object>> qualifiedEntries = objectProps.entrySet();
407                 for (Entry<String, Object> entry : qualifiedEntries) {
408                     String unqProp = getUnQProperty(entry.getKey());
409                     unQObjectProperties.put(unqProp, entry.getValue());
410                 }
411                 result = unQObjectProperties;
412         }
413
414         return result;
415     }
416     
417     /**
418      * extractPart extracts an XML object from given DocumentModel
419      * @param docModel
420      * @param schema of the object to extract
421      * @param partMeta metadata for the object to extract
422      * @throws Exception
423      */
424     @Deprecated
425     protected Map<String, Object> extractPart(
426             DocumentModel docModel, String schema, ObjectPartType partMeta,
427             Map<String, Object> addToMap)
428             throws Exception {
429         Map<String, Object> result = null;
430
431         result = this.extractPart(docModel, schema, addToMap);
432
433         return result;
434     }
435     
436     /* 
437     public String getStringPropertyFromDoc(
438                 ServiceContext ctx,
439                 String csid,
440                 String propertyXPath ) throws DocumentNotFoundException, DocumentException {
441         RepositoryInstance repoSession = null;
442         boolean releaseRepoSession = false;
443         String returnValue = null;
444
445         try{ 
446                 RepositoryJavaClientImpl repoClient = (RepositoryJavaClientImpl)this.getRepositoryClient(ctx);
447                 repoSession = this.getRepositorySession();
448                 if (repoSession == null) {
449                         repoSession = repoClient.getRepositorySession();
450                         releaseRepoSession = true;
451                 }
452
453                 try {
454                         DocumentWrapper<DocumentModel> wrapper = repoClient.getDoc(repoSession, ctx, csid);
455                         DocumentModel docModel = wrapper.getWrappedObject();
456                         returnValue = (String) docModel.getPropertyValue(propertyXPath);
457                 } catch (PropertyException pe) {
458                         throw pe;
459                 } catch (DocumentException de) {
460                         throw de;
461                 } catch (Exception e) {
462                         if (logger.isDebugEnabled()) {
463                                 logger.debug("Caught exception ", e);
464                         }
465                         throw new DocumentException(e);
466                 } finally {
467                         if (releaseRepoSession && repoSession != null) {
468                                 repoClient.releaseRepositorySession(repoSession);
469                         }
470                 }
471         } catch (Exception e) {
472                 if (logger.isDebugEnabled()) {
473                         logger.debug("Caught exception ", e);
474                 }
475                 throw new DocumentException(e);
476         }               
477
478
479         if (logger.isWarnEnabled() == true) {
480                 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
481         }
482         return returnValue;
483     }
484      */
485
486     
487
488     /* (non-Javadoc)
489      * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#getAuthorityRefs(org.collectionspace.services.common.document.DocumentWrapper, java.util.List)
490      */
491     @Override
492     public AuthorityRefList getAuthorityRefs(
493             String csid,
494             List<AuthRefConfigInfo> authRefsInfo) throws PropertyException {
495
496         AuthorityRefList authRefList = new AuthorityRefList();
497         AbstractCommonList commonList = (AbstractCommonList) authRefList;
498         
499         DocumentFilter docFilter = this.getDocumentFilter();
500         long pageSize = docFilter.getPageSize();
501         long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize;
502         // set the page size and page number
503         commonList.setPageNum(pageNum);
504         commonList.setPageSize(pageSize);
505         
506         List<AuthorityRefList.AuthorityRefItem> list = authRefList.getAuthorityRefItem();
507
508         try {
509                 int iFirstToUse = (int)(pageSize*pageNum);
510                 int nFoundInPage = 0;
511                 int nFoundTotal = 0;
512                 
513                 ArrayList<RefNameServiceUtils.AuthRefInfo> foundProps 
514                         = new ArrayList<RefNameServiceUtils.AuthRefInfo>();
515                 
516                 boolean releaseRepoSession = false;
517                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
518                 RepositoryJavaClientImpl repoClient = (RepositoryJavaClientImpl)this.getRepositoryClient(ctx);
519                 RepositoryInstance repoSession = this.getRepositorySession();
520                 if (repoSession == null) {
521                         repoSession = repoClient.getRepositorySession(ctx);
522                         releaseRepoSession = true;
523                 }
524                 
525                 try {
526                         DocumentModel docModel = repoClient.getDoc(repoSession, ctx, csid).getWrappedObject();
527                         RefNameServiceUtils.findAuthRefPropertiesInDoc(docModel, authRefsInfo, null, foundProps);
528                         // Slightly goofy pagination support - how many refs do we expect from one object?
529                         for(RefNameServiceUtils.AuthRefInfo ari:foundProps) {
530                                 if((nFoundTotal >= iFirstToUse) && (nFoundInPage < pageSize)) {
531                                         if(appendToAuthRefsList(ari, list)) {
532                                                 nFoundInPage++;
533                                         nFoundTotal++;
534                                         }
535                                 } else {
536                                         nFoundTotal++;
537                                 }
538                         }
539                 } finally {
540                         if (releaseRepoSession == true) {
541                                 repoClient.releaseRepositorySession(ctx, repoSession);
542                         }
543                 }
544                 
545             // Set num of items in list. this is useful to our testing framework.
546             commonList.setItemsInPage(nFoundInPage);
547             // set the total result size
548             commonList.setTotalItems(nFoundTotal);
549             
550         } catch (PropertyException pe) {
551             String msg = "Attempted to retrieve value for invalid or missing authority field. "
552                     + "Check authority field properties in tenant bindings.";
553             logger.warn(msg, pe);
554             throw pe;
555         } catch (Exception e) {
556             if (logger.isDebugEnabled()) {
557                 logger.debug("Caught exception in getAuthorityRefs", e);
558             }
559             Response response = Response.status(
560                     Response.Status.INTERNAL_SERVER_ERROR).entity(
561                     "Failed to retrieve authority references").type(
562                     "text/plain").build();
563             throw new WebApplicationException(response);
564         }
565
566         return authRefList;
567     }
568
569     private boolean appendToAuthRefsList(RefNameServiceUtils.AuthRefInfo ari, 
570                                                 List<AuthorityRefList.AuthorityRefItem> list)
571             throws Exception {
572         String fieldName = ari.getQualifiedDisplayName();
573         try {
574                         String refNameValue = (String)ari.getProperty().getValue();
575                         AuthorityRefList.AuthorityRefItem item = authorityRefListItem(fieldName, refNameValue);
576                         if(item!=null) {        // ignore garbage values.
577                                 list.add(item);
578                                 return true;
579                         }
580         } catch(PropertyException pe) {
581                         logger.debug("PropertyException on: "+ari.getProperty().getPath()+pe.getLocalizedMessage());
582         }
583         return false;
584     }
585
586     private AuthorityRefList.AuthorityRefItem authorityRefListItem(String authRefFieldName, String refName) {
587
588         AuthorityRefList.AuthorityRefItem ilistItem = new AuthorityRefList.AuthorityRefItem();
589         try {
590             RefNameUtils.AuthorityTermInfo termInfo = RefNameUtils.parseAuthorityTermInfo(refName);
591             ilistItem.setRefName(refName);
592             ilistItem.setAuthDisplayName(termInfo.inAuthority.displayName);
593             ilistItem.setItemDisplayName(termInfo.displayName);
594             ilistItem.setSourceField(authRefFieldName);
595             ilistItem.setUri(termInfo.getRelativeUri());
596         } catch (Exception e) {
597                 logger.error("Trouble parsing refName from value: "+refName+" in field: "+authRefFieldName+e.getLocalizedMessage());
598                 ilistItem = null;
599         }
600         return ilistItem;
601     }
602
603     /**
604      * Returns the primary value from a list of values.
605      *
606      * Assumes that the first value is the primary value.
607      * This assumption may change when and if the primary value
608      * is identified explicitly.
609      *
610      * @param values a list of values.
611      * @param propertyName the name of a property through
612      *     which the value can be extracted.
613      * @return the primary value.
614     protected String primaryValueFromMultivalue(List<Object> values, String propertyName) {
615         String primaryValue = "";
616         if (values == null || values.size() == 0) {
617             return primaryValue;
618         }
619         Object value = values.get(0);
620         if (value instanceof String) {
621             if (value != null) {
622                 primaryValue = (String) value;
623             }
624        // Multivalue group of fields
625        } else if (value instanceof Map) {
626             if (value != null) {
627                 Map map = (Map) value;
628                 if (map.values().size() > 0) {
629                     if (map.get(propertyName) != null) {
630                       primaryValue = (String) map.get(propertyName);
631                     }
632                 }
633             }
634        } else {
635             logger.warn("Unexpected type for property " + propertyName
636                     + " in multivalue list: not String or Map.");
637        }
638        return primaryValue;
639     }
640      */
641
642     /**
643      * Gets a simple property from the document.
644      *
645      * For completeness, as this duplicates DocumentModel method. 
646      *
647      * @param docModel The document model to get info from
648      * @param schema The name of the schema (part)
649      * @param propertyName The simple scalar property type
650      * @return property value as String
651      */
652     protected String getSimpleStringProperty(DocumentModel docModel, String schema, String propName) {
653         String xpath = "/"+schema+":"+propName;
654         try {
655                 return (String)docModel.getPropertyValue(xpath);
656         } catch(PropertyException pe) {
657                 throw new RuntimeException("Problem retrieving property {"+xpath+"}. Not a simple String property?"
658                                 +pe.getLocalizedMessage());
659         } catch(ClassCastException cce) {
660                 throw new RuntimeException("Problem retrieving property {"+xpath+"} as String. Not a scalar String property?"
661                                 +cce.getLocalizedMessage());
662         } catch(Exception e) {
663                 throw new RuntimeException("Unknown problem retrieving property {"+xpath+"}."
664                                 +e.getLocalizedMessage());
665         }
666     }
667
668     /**
669      * Gets first of a repeating list of scalar values, as a String, from the document.
670      *
671      * @param docModel The document model to get info from
672      * @param schema The name of the schema (part)
673      * @param listName The name of the scalar list property
674      * @return first value in list, as a String, or empty string if the list is empty
675      */
676     protected String getFirstRepeatingStringProperty(
677                 DocumentModel docModel, String schema, String listName) {
678         String xpath = "/"+schema+":"+listName+"/[0]";
679         try {
680                 return (String)docModel.getPropertyValue(xpath);
681         } catch(PropertyException pe) {
682                 throw new RuntimeException("Problem retrieving property {"+xpath+"}. Not a repeating scalar?"
683                                 +pe.getLocalizedMessage());
684         } catch(IndexOutOfBoundsException ioobe) {
685                 // Nuxeo sometimes handles missing sub, and sometimes does not. Odd.
686                 return "";      // gracefully handle missing elements
687         } catch(ClassCastException cce) {
688                 throw new RuntimeException("Problem retrieving property {"+xpath+"} as String. Not a repeating String property?"
689                                 +cce.getLocalizedMessage());
690         } catch(Exception e) {
691                 throw new RuntimeException("Unknown problem retrieving property {"+xpath+"}."
692                                 +e.getLocalizedMessage());
693         }
694     }
695    
696
697     /**
698      * Gets first of a repeating list of scalar values, as a String, from the document.
699      *
700      * @param docModel The document model to get info from
701      * @param schema The name of the schema (part)
702      * @param listName The name of the scalar list property
703      * @return first value in list, as a String, or empty string if the list is empty
704      */
705     protected String getStringValueInPrimaryRepeatingComplexProperty(
706                 DocumentModel docModel, String schema, String complexPropertyName, String fieldName) {          
707         String result = null;
708         
709         String xpath = "/" + NuxeoUtils.getPrimaryXPathPropertyName(schema, complexPropertyName, fieldName);
710         try {
711                 result = (String)docModel.getPropertyValue(xpath);
712         } catch(PropertyException pe) {
713                 throw new RuntimeException("Problem retrieving property {"+xpath+"}. Bad propertyNames?"
714                                 +pe.getLocalizedMessage());
715         } catch(IndexOutOfBoundsException ioobe) {
716                 // Nuxeo sometimes handles missing sub, and sometimes does not. Odd.
717                 result = "";    // gracefully handle missing elements
718         } catch(ClassCastException cce) {
719                 throw new RuntimeException("Problem retrieving property {"+xpath+"} as String. Not a String property?"
720                                 +cce.getLocalizedMessage());
721         } catch(Exception e) {
722                 throw new RuntimeException("Unknown problem retrieving property {"+xpath+"}."
723                                 +e.getLocalizedMessage());
724         }
725         
726         return result;
727     }
728    
729     /**
730      * Gets XPath value from schema. Note that only "/" and "[n]" are
731      * supported for xpath. Can omit grouping elements for repeating complex types, 
732      * e.g., "fieldList/[0]" can be used as shorthand for "fieldList/field[0]" and
733      * "fieldGroupList/[0]/field" can be used as shorthand for "fieldGroupList/fieldGroup[0]/field".
734      * If there are no entries for a list of scalars or for a list of complex types, 
735      * a 0 index expression (e.g., "fieldGroupList/[0]/field") will safely return an empty
736      * string. A non-zero index will throw an IndexOutOfBoundsException if there are not
737      * that many elements in the list. 
738      * N.B.: This does not follow the XPath spec - indices are 0-based, not 1-based.
739      *
740      * @param docModel The document model to get info from
741      * @param schema The name of the schema (part)
742      * @param xpath The XPath expression (without schema prefix)
743      * @return value the indicated property value as a String
744      */
745         protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
746                         String schema, ListResultField field) {
747                 Object result = null;
748
749                 result = NuxeoUtils.getXPathValue(docModel, schema, field.getXpath());
750                 
751                 return result;
752         }
753    
754 }