]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
c793447490b7b6ab489741c9f640646fe4bdfac1
[tmp/jakarta-migration.git] /
1 /**\r
2  *  This document is a part of the source code and related artifacts\r
3  *  for CollectionSpace, an open source collections management system\r
4  *  for museums and related institutions:\r
5 \r
6  *  http://www.collectionspace.org\r
7  *  http://wiki.collectionspace.org\r
8 \r
9  *  Copyright 2009 University of California at Berkeley\r
10 \r
11  *  Licensed under the Educational Community License (ECL), Version 2.0.\r
12  *  You may not use this file except in compliance with this License.\r
13 \r
14  *  You may obtain a copy of the ECL 2.0 License at\r
15 \r
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt\r
17 \r
18  *  Unless required by applicable law or agreed to in writing, software\r
19  *  distributed under the License is distributed on an "AS IS" BASIS,\r
20  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
21  *  See the License for the specific language governing permissions and\r
22  *  limitations under the License.\r
23  */\r
24 package org.collectionspace.services.common.vocabulary;\r
25 \r
26 import java.util.ArrayList;\r
27 import java.util.Collection;\r
28 import java.util.HashMap;\r
29 import java.util.Iterator;\r
30 import java.util.List;\r
31 import java.util.Map;\r
32 \r
33 import org.nuxeo.ecm.core.api.ClientException;\r
34 import org.nuxeo.ecm.core.api.DocumentModel;\r
35 import org.nuxeo.ecm.core.api.DocumentModelList;\r
36 import org.nuxeo.ecm.core.api.model.Property;\r
37 import org.nuxeo.ecm.core.api.model.PropertyException;\r
38 import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;\r
39 import org.nuxeo.ecm.core.api.model.impl.primitives.StringProperty;\r
40 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;\r
41 import org.slf4j.Logger;\r
42 import org.slf4j.LoggerFactory;\r
43 \r
44 import org.collectionspace.services.client.PoxPayloadIn;\r
45 import org.collectionspace.services.client.PoxPayloadOut;\r
46 import org.collectionspace.services.common.ServiceMain;\r
47 import org.collectionspace.services.common.context.ServiceContext;\r
48 import org.collectionspace.services.common.context.AbstractServiceContextImpl;\r
49 import org.collectionspace.services.common.api.RefNameUtils;\r
50 import org.collectionspace.services.common.api.Tools;\r
51 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;\r
52 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;\r
53 import org.collectionspace.services.common.authorityref.AuthorityRefList;\r
54 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;\r
55 import org.collectionspace.services.common.context.ServiceBindingUtils;\r
56 import org.collectionspace.services.common.document.DocumentException;\r
57 import org.collectionspace.services.common.document.DocumentFilter;\r
58 import org.collectionspace.services.common.document.DocumentNotFoundException;\r
59 import org.collectionspace.services.common.document.DocumentUtils;\r
60 import org.collectionspace.services.common.document.DocumentWrapper;\r
61 import org.collectionspace.services.common.query.QueryManager;\r
62 import org.collectionspace.services.common.repository.RepositoryClient;\r
63 import org.collectionspace.services.nuxeo.client.java.DocHandlerBase;\r
64 import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;\r
65 import org.collectionspace.services.common.security.SecurityUtils;\r
66 import org.collectionspace.services.config.service.ServiceBindingType;\r
67 import org.collectionspace.services.jaxb.AbstractCommonList;\r
68 import org.collectionspace.services.nuxeo.util.NuxeoUtils;\r
69 \r
70 import com.sun.xml.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;\r
71 \r
72 /**\r
73  * RefNameServiceUtils is a collection of services utilities related to refName usage.\r
74  *\r
75  * $LastChangedRevision: $\r
76  * $LastChangedDate: $\r
77  */\r
78 public class RefNameServiceUtils {\r
79 \r
80         public static class AuthRefConfigInfo {\r
81                 public String getQualifiedDisplayName() {\r
82                 return(Tools.isBlank(schema))?\r
83                                 displayName:DocumentUtils.appendSchemaName(schema, displayName);\r
84                 }\r
85                 public String getDisplayName() {\r
86                         return displayName;\r
87                 }\r
88                 public void setDisplayName(String displayName) {\r
89                         this.displayName = displayName;\r
90                 }\r
91                 String displayName;\r
92                 String schema;\r
93                 public String getSchema() {\r
94                         return schema;\r
95                 }\r
96                 public void setSchema(String schema) {\r
97                         this.schema = schema;\r
98                 }\r
99                 public String getFullPath() {\r
100                         return fullPath;\r
101                 }\r
102                 public void setFullPath(String fullPath) {\r
103                         this.fullPath = fullPath;\r
104                 }\r
105                 String fullPath;\r
106                 protected String[] pathEls;\r
107                 public AuthRefConfigInfo(AuthRefConfigInfo arci) {\r
108                         this.displayName = arci.displayName;\r
109                         this.schema = arci.schema;\r
110                         this.fullPath = arci.fullPath;\r
111                         this.pathEls = arci.pathEls;\r
112                         // Skip the pathElse check, since we are creatign from another (presumably valid) arci.\r
113                 }\r
114                 \r
115                 public AuthRefConfigInfo(String displayName, String schema, String fullPath, String[] pathEls) {\r
116                         this.displayName = displayName;\r
117                         this.schema = schema;\r
118                         this.fullPath = fullPath;\r
119                         this.pathEls = pathEls;\r
120                         checkPathEls();\r
121                 }\r
122 \r
123                 // Split a config value string like "intakes_common:collector", or\r
124                 // "collectionobjects_common:contentPeoples|contentPeople"\r
125                 // "collectionobjects_common:assocEventGroupList/*/assocEventPlace"\r
126                 // If has a pipe ('|') second part is a displayLabel, and first is path\r
127                 // Otherwise, entry is a path, and can use the last pathElement as displayName\r
128                 // Should be schema qualified.\r
129                 public AuthRefConfigInfo(String configString) {\r
130                 String[] pair = configString.split("\\|", 2);\r
131                 String[] pathEls;\r
132                 String displayName, fullPath;\r
133                 if(pair.length == 1) {\r
134                         // no label specifier, so we'll defer getting label\r
135                         fullPath = pair[0];\r
136                         pathEls = pair[0].split("/");\r
137                         displayName = pathEls[pathEls.length-1];\r
138                 } else {\r
139                         fullPath = pair[0];\r
140                         pathEls = pair[0].split("/");\r
141                         displayName = pair[1];\r
142                 }\r
143                 String[] schemaSplit = pathEls[0].split(":",2);\r
144                 String schema; \r
145                 if(schemaSplit.length==1) {     // schema not specified\r
146                         schema = null;\r
147                 } else {\r
148                         schema = schemaSplit[0];\r
149                         if(pair.length == 1 && pathEls.length == 1) {   // simplest case of field in top level schema, no labelll\r
150                                 displayName = schemaSplit[1];   // Have to fix up displayName to have no schema\r
151                         }\r
152                 }\r
153                         this.displayName = displayName;\r
154                         this.schema = schema;\r
155                         this.fullPath = fullPath;\r
156                         this.pathEls = pathEls;\r
157                         checkPathEls();\r
158                 }\r
159                 \r
160                 protected void checkPathEls() {\r
161                 int len = pathEls.length;\r
162                 if(len<1)\r
163                         throw new InternalError("Bad values in authRef info - caller screwed up:"+fullPath);\r
164                 // Handle case of them putting a leading slash on the path\r
165                 if(len>1 && pathEls[0].endsWith(":")) {\r
166                         len--;\r
167                         String[] newArray = new String[len];\r
168                         newArray[0] = pathEls[0]+pathEls[1];\r
169                         if(len>=2) {\r
170                                 System.arraycopy(pathEls, 2, newArray, 1, len-1);\r
171                         }\r
172                         pathEls = newArray;\r
173                 }\r
174                 }\r
175         }\r
176 \r
177         public static class AuthRefInfo extends AuthRefConfigInfo {\r
178                 public Property getProperty() {\r
179                         return property;\r
180                 }\r
181                 public void setProperty(Property property) {\r
182                         this.property = property;\r
183                 }\r
184                 Property property;\r
185                 public AuthRefInfo(String displayName, String schema, String fullPath, String[] pathEls, Property prop) {\r
186                         super(displayName, schema, fullPath, pathEls);\r
187                         this.property = prop;\r
188                 }\r
189                 public AuthRefInfo(AuthRefConfigInfo arci, Property prop) {\r
190                         super(arci);\r
191                         this.property = prop;\r
192                 }\r
193         }\r
194 \r
195     private static final Logger logger = LoggerFactory.getLogger(RefNameServiceUtils.class);\r
196     \r
197     private static ArrayList<String> refNameServiceTypes = null;\r
198     \r
199     public static List<AuthRefConfigInfo> getConfiguredAuthorityRefs(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {\r
200         List<String> authRefFields =\r
201                 ((AbstractServiceContextImpl) ctx).getAllPartsPropertyValues(\r
202                 ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);\r
203         ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>(authRefFields.size()); \r
204         for(String spec:authRefFields) {\r
205                 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);\r
206                 authRefsInfo.add(arci);\r
207         }\r
208         return authRefsInfo;\r
209     }\r
210 \r
211     public static AuthorityRefDocList getAuthorityRefDocs(\r
212                 RepositoryInstance repoSession,\r
213                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,\r
214             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,\r
215             List<String> serviceTypes,\r
216             String refName,\r
217             String refPropName,\r
218             DocumentFilter filter, boolean computeTotal)\r
219                         throws DocumentException, DocumentNotFoundException {\r
220         AuthorityRefDocList wrapperList = new AuthorityRefDocList();\r
221         AbstractCommonList commonList = (AbstractCommonList) wrapperList;\r
222         int pageNum = filter.getStartPage();\r
223         int pageSize = filter.getPageSize(); \r
224         commonList.setPageNum(pageNum);\r
225         commonList.setPageSize(pageSize);\r
226         List<AuthorityRefDocList.AuthorityRefDocItem> list =\r
227                 wrapperList.getAuthorityRefDocItem();\r
228         \r
229         Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();\r
230         Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();\r
231 \r
232         RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl)repoClient;\r
233         try {\r
234                 DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,\r
235                                 serviceTypes, refName, refPropName, queriedServiceBindings, authRefFieldsByService,\r
236                                 filter.getWhereClause(), pageSize, pageNum, computeTotal);\r
237         \r
238                 if (docList == null) { // found no authRef fields - nothing to process\r
239                     return wrapperList;\r
240                 }\r
241                 // Set num of items in list. this is useful to our testing framework.\r
242                 commonList.setItemsInPage(docList.size());\r
243                 // set the total result size\r
244                 commonList.setTotalItems(docList.totalSize());\r
245                 // set the fieldsReturned list. Even though this is a fixed schema, app layer treats\r
246                 // this like other abstract common lists\r
247                 /*\r
248                         <xs:element name="docType"         type="xs:string" minOccurs="1" />\r
249                         <xs:element name="docId"           type="xs:string" minOccurs="1" />\r
250                         <xs:element name="docNumber"       type="xs:string" minOccurs="0" />\r
251                         <xs:element name="docName"         type="xs:string" minOccurs="0" />\r
252                         <xs:element name="sourceField"     type="xs:string" minOccurs="1" />\r
253                         <xs:element name="uri"             type="xs:anyURI" minOccurs="1" />\r
254                         <xs:element name="updatedAt"       type="xs:string" minOccurs="1" />\r
255                         <xs:element name="workflowState"   type="xs:string" minOccurs="1" />\r
256                         */\r
257                 String fieldList = "docType|docId|docNumber|docName|sourceField|uri|updatedAt|workflowState";\r
258                 commonList.setFieldsReturned(fieldList);\r
259                 \r
260                 int nRefsFound = processRefObjsDocList(docList, refName, queriedServiceBindings, authRefFieldsByService,\r
261                                                                 list, null);\r
262                 if(logger.isDebugEnabled() && (nRefsFound < docList.size())) {\r
263                         logger.debug("Internal curiosity: got fewer matches of refs than # docs matched...");\r
264                 }\r
265         } catch (Exception e) {\r
266                         logger.error("Could not retrieve the Nuxeo repository", e);\r
267                         wrapperList = null;\r
268         }\r
269                \r
270         return wrapperList;\r
271     }\r
272     \r
273     private static ArrayList<String> getRefNameServiceTypes() {\r
274         if(refNameServiceTypes == null) {\r
275                 refNameServiceTypes = new ArrayList<String>();\r
276                 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_AUTHORITY);\r
277                 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_OBJECT);\r
278                 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_PROCEDURE);\r
279         }\r
280         return refNameServiceTypes;\r
281     }\r
282     \r
283     // Seems like a good value - no real data to set this well.\r
284     private static final int N_OBJS_TO_UPDATE_PER_LOOP = 100;\r
285     \r
286     public static int updateAuthorityRefDocs(\r
287                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,\r
288             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,\r
289             RepositoryInstance repoSession,\r
290             String oldRefName,\r
291             String newRefName,\r
292             String refPropName ) throws Exception {\r
293         Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();\r
294         Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();\r
295         int nRefsFound = 0;\r
296         if(!(repoClient instanceof RepositoryJavaClientImpl)) {\r
297                 throw new InternalError("updateAuthorityRefDocs() called with unknown repoClient type!");\r
298         }\r
299         try {\r
300                 final int pageSize = N_OBJS_TO_UPDATE_PER_LOOP; \r
301                 int pageNumProcessed = 1;\r
302                 while(true) {   // Keep looping until we find all the refs.\r
303                         logger.debug("updateAuthorityRefDocs working on page: "+pageNumProcessed);\r
304                         // Note that we always ask the Repo for the first page, since each page we process\r
305                         // should not be found in successive searches. Slightly inefficient, but more\r
306                         // reliable (stateless).\r
307                         DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,\r
308                                         getRefNameServiceTypes(), oldRefName, refPropName,\r
309                                         queriedServiceBindings, authRefFieldsByService, null, pageSize, 0, false);\r
310                 \r
311                         if((docList == null)                    // found no authRef fields - nothing to do\r
312                                 || (docList.size() == 0)) {     // No more to handle\r
313                                 logger.debug("updateAuthorityRefDocs no more results");\r
314                             break;\r
315                         }\r
316                         logger.debug("updateAuthorityRefDocs curr page result list size: "+docList.size());\r
317                         int nRefsFoundThisPage = processRefObjsDocList(docList, oldRefName, queriedServiceBindings, authRefFieldsByService,\r
318                                                                         null, newRefName);\r
319                         if(nRefsFoundThisPage>0) {\r
320                                 ((RepositoryJavaClientImpl)repoClient).saveDocListWithoutHandlerProcessing(ctx, repoSession, docList, true);\r
321                                 nRefsFound += nRefsFoundThisPage;\r
322                         }\r
323                         pageNumProcessed++;\r
324                         if(docList.size()<pageSize) {\r
325                                 logger.debug("updateAuthorityRefDocs: assuming no more results, as nResults < pageSize");\r
326                                 break;\r
327                         }\r
328                 }\r
329         } catch (Exception e) {\r
330                 logger.error("Internal error updating the AuthorityRefDocs: " + e.getLocalizedMessage());\r
331                 logger.debug(Tools.errorToString(e, true));\r
332                 throw e;\r
333         }\r
334                 logger.debug("updateAuthorityRefDocs replaced a total of: "+nRefsFound);\r
335         return nRefsFound;\r
336     }\r
337     \r
338     private static DocumentModelList findAuthorityRefDocs(\r
339                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,\r
340             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,\r
341             RepositoryInstance repoSession,\r
342             List<String> serviceTypes,\r
343             String refName,\r
344             String refPropName,\r
345             Map<String, ServiceBindingType> queriedServiceBindings,\r
346             Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,\r
347             String whereClauseAdditions,\r
348             int pageSize, int pageNum, boolean computeTotal) throws DocumentException, DocumentNotFoundException {\r
349 \r
350         // Get the service bindings for this tenant\r
351         TenantBindingConfigReaderImpl tReader =\r
352                 ServiceMain.getInstance().getTenantBindingConfigReader();\r
353         // We need to get all the procedures, authorities, and objects.\r
354         List<ServiceBindingType> servicebindings = tReader.getServiceBindingsByType(ctx.getTenantId(), serviceTypes);\r
355         if (servicebindings == null || servicebindings.isEmpty()) {\r
356                 logger.error("RefNameServiceUtils.getAuthorityRefDocs: No services bindings found, cannot proceed!");\r
357             return null;\r
358         }\r
359         // Filter the list for current user rights\r
360         servicebindings = SecurityUtils.getReadableServiceBindingsForCurrentUser(servicebindings);\r
361         \r
362         ArrayList<String> docTypes = new ArrayList<String>();\r
363         \r
364         String query = computeWhereClauseForAuthorityRefDocs(refName, refPropName, docTypes, servicebindings, \r
365                                                                                                 queriedServiceBindings, authRefFieldsByService );\r
366         if (query == null) { // found no authRef fields - nothing to query\r
367             return null;\r
368         }\r
369         // Additional qualifications, like workflow state\r
370         if(whereClauseAdditions!=null) {\r
371                 query += " AND " + whereClauseAdditions;\r
372         }\r
373         // Now we have to issue the search\r
374         RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl)repoClient;\r
375         DocumentWrapper<DocumentModelList> docListWrapper = nuxeoRepoClient.findDocs(ctx, repoSession,\r
376                 docTypes, query, pageSize, pageNum, computeTotal);\r
377         // Now we gather the info for each document into the list and return\r
378         DocumentModelList docList = docListWrapper.getWrappedObject();\r
379         return docList;\r
380     }\r
381     \r
382     private static final boolean READY_FOR_COMPLEX_QUERY = true;\r
383     \r
384     private static String computeWhereClauseForAuthorityRefDocs(\r
385                 String refName,\r
386                 String refPropName,\r
387                 ArrayList<String> docTypes,\r
388                 List<ServiceBindingType> servicebindings,\r
389                 Map<String, ServiceBindingType> queriedServiceBindings,\r
390                 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService ) {\r
391 \r
392         boolean fFirst = true;\r
393         List<String> authRefFieldPaths;\r
394         for (ServiceBindingType sb : servicebindings) {\r
395                 // Gets the property names for each part, qualified with the part label (which\r
396                 // is also the table name, the way that the repository works).\r
397             authRefFieldPaths =\r
398                     ServiceBindingUtils.getAllPartsPropertyValues(sb,\r
399                                 refPropName, ServiceBindingUtils.QUALIFIED_PROP_NAMES);\r
400             if (authRefFieldPaths.isEmpty()) {\r
401                 continue;\r
402             }\r
403             ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>();\r
404                 for(String spec:authRefFieldPaths) {\r
405                         AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);\r
406                         authRefsInfo.add(arci);\r
407                 }\r
408 \r
409             String docType = sb.getObject().getName();\r
410             queriedServiceBindings.put(docType, sb);\r
411             authRefFieldsByService.put(docType, authRefsInfo);\r
412             docTypes.add(docType);\r
413             fFirst = false;\r
414         }\r
415         if (fFirst) { // found no authRef fields - nothing to query\r
416             return null;\r
417         }\r
418         // We used to build a complete matches query, but that was too complex.\r
419         // Just build a keyword query based upon some key pieces - the urn syntax elements and the shortID\r
420         // Note that this will also match the Item itself, but that will get filtered out when\r
421         // we compute actual matches.\r
422         AuthorityTermInfo authTermInfo = RefNameUtils.parseAuthorityTermInfo(refName);\r
423                 \r
424         String keywords = RefNameUtils.URN_PREFIX \r
425                                         + " AND " + (authTermInfo.inAuthority.name!=null?\r
426                                                         authTermInfo.inAuthority.name:authTermInfo.inAuthority.csid)\r
427                                         + " AND " + (authTermInfo.name!=null?\r
428                                                         authTermInfo.name:authTermInfo.csid);\r
429         \r
430         String whereClauseStr = QueryManager.createWhereClauseFromKeywords(keywords);\r
431         \r
432         if (logger.isTraceEnabled()) {\r
433                 logger.trace("The 'where' clause to find refObjs is: ", whereClauseStr);\r
434         }\r
435         \r
436         return whereClauseStr; \r
437     }\r
438     \r
439     /*\r
440      * Runs through the list of found docs, processing them. \r
441      * If list is non-null, then processing means gather the info for items.\r
442      * If list is null, and newRefName is non-null, then processing means replacing and updating. \r
443      *   If processing/updating, this must be called in teh context of an open session, and caller\r
444      *   must release Session after calling this.\r
445      * \r
446      */\r
447     private static int processRefObjsDocList(\r
448                 DocumentModelList docList,\r
449                 String refName,\r
450                 Map<String, ServiceBindingType> queriedServiceBindings,\r
451                 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,\r
452                         List<AuthorityRefDocList.AuthorityRefDocItem> list, \r
453                         String newAuthorityRefName) {\r
454         Iterator<DocumentModel> iter = docList.iterator();\r
455         int nRefsFoundTotal = 0;\r
456         while (iter.hasNext()) {\r
457             DocumentModel docModel = iter.next();\r
458             AuthorityRefDocList.AuthorityRefDocItem ilistItem;\r
459 \r
460             String docType = docModel.getDocumentType().getName();\r
461             docType = ServiceBindingUtils.getUnqualifiedTenantDocType(docType);\r
462             ServiceBindingType sb = queriedServiceBindings.get(docType);\r
463             if (sb == null) {\r
464                 throw new RuntimeException(\r
465                         "getAuthorityRefDocs: No Service Binding for docType: " + docType);\r
466             }\r
467             String serviceContextPath = "/" + sb.getName().toLowerCase() + "/";\r
468             \r
469             if(list == null) { // no list - should be update refName case.\r
470                 if(newAuthorityRefName==null) {\r
471                         throw new InternalError("processRefObjsDocList() called with neither an itemList nor a new RefName!");\r
472                         }\r
473                 ilistItem = null;\r
474             } else {    // Have a list - refObjs case\r
475                 if(newAuthorityRefName!=null) {\r
476                         throw new InternalError("processRefObjsDocList() called with both an itemList and a new RefName!");\r
477                 }\r
478                 ilistItem = new AuthorityRefDocList.AuthorityRefDocItem();\r
479                 String csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());\r
480                 ilistItem.setDocId(csid);\r
481                 ilistItem.setUri(serviceContextPath + csid);\r
482                 try {\r
483                     ilistItem.setWorkflowState(docModel.getCurrentLifeCycleState());\r
484                         ilistItem.setUpdatedAt(DocHandlerBase.getUpdatedAtAsString(docModel));\r
485                 } catch(Exception e) {\r
486                         logger.error("Error getting core values for doc ["+csid+"]: "+e.getLocalizedMessage());\r
487                 }\r
488                 // The id and URI are the same on all doctypes\r
489                 ilistItem.setDocType(docType);\r
490                 ilistItem.setDocNumber(\r
491                         ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NUMBER_PROP, docModel));\r
492                 ilistItem.setDocName(\r
493                         ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NAME_PROP, docModel));\r
494             }\r
495             // Now, we have to loop over the authRefFieldsByService to figure\r
496             // out which field(s) matched this.\r
497             List<AuthRefConfigInfo> matchingAuthRefFields = authRefFieldsByService.get(docType);\r
498             if (matchingAuthRefFields == null || matchingAuthRefFields.isEmpty()) {\r
499                 throw new RuntimeException(\r
500                         "getAuthorityRefDocs: internal logic error: can't fetch authRefFields for DocType.");\r
501             }\r
502             //String authRefAncestorField = "";\r
503             //String authRefDescendantField = "";\r
504             //String sourceField = "";\r
505             int nRefsFoundInDoc = 0;\r
506             \r
507             ArrayList<RefNameServiceUtils.AuthRefInfo> foundProps \r
508                                                 = new ArrayList<RefNameServiceUtils.AuthRefInfo>();\r
509             try {\r
510                     findAuthRefPropertiesInDoc(docModel, matchingAuthRefFields, refName, foundProps);\r
511                     for(RefNameServiceUtils.AuthRefInfo ari:foundProps) {\r
512                                 if(ilistItem != null) {\r
513                                 if(nRefsFoundInDoc == 0) {      // First one?\r
514                                         ilistItem.setSourceField(ari.getQualifiedDisplayName());\r
515                                 } else {        // duplicates from one object\r
516                                         ilistItem = cloneAuthRefDocItem(ilistItem, ari.getQualifiedDisplayName());\r
517                                 }\r
518                                         list.add(ilistItem);\r
519                                 } else {        // update refName case\r
520                                         Property propToUpdate = ari.getProperty();\r
521                                         propToUpdate.setValue(newAuthorityRefName);\r
522                                 }\r
523                                 nRefsFoundInDoc++;\r
524                     }\r
525             } catch (ClientException ce) {\r
526                 throw new RuntimeException(\r
527                         "getAuthorityRefDocs: Problem fetching values from repo: " + ce.getLocalizedMessage());\r
528             }\r
529             if (nRefsFoundInDoc == 0) {\r
530                 logger.warn(\r
531                         "getAuthorityRefDocs: Result: "\r
532                         + docType + " [" + NuxeoUtils.getCsid(docModel)\r
533                         + "] does not reference ["\r
534                         + refName + "]");\r
535             }\r
536             nRefsFoundTotal += nRefsFoundInDoc;\r
537         }\r
538         return nRefsFoundTotal;\r
539     }\r
540     \r
541     private static AuthorityRefDocList.AuthorityRefDocItem cloneAuthRefDocItem(\r
542                 AuthorityRefDocList.AuthorityRefDocItem ilistItem, String sourceField) {\r
543         AuthorityRefDocList.AuthorityRefDocItem newlistItem = new AuthorityRefDocList.AuthorityRefDocItem();\r
544         newlistItem.setDocId(ilistItem.getDocId());\r
545         newlistItem.setDocName(ilistItem.getDocName());\r
546         newlistItem.setDocNumber(ilistItem.getDocNumber());\r
547         newlistItem.setDocType(ilistItem.getDocType());\r
548         newlistItem.setUri(ilistItem.getUri());\r
549         newlistItem.setSourceField(sourceField);\r
550         return newlistItem;\r
551     }\r
552     \r
553     public static List<AuthRefInfo> findAuthRefPropertiesInDoc( \r
554                 DocumentModel docModel, \r
555                 List<AuthRefConfigInfo> authRefFieldInfo,\r
556                 String refNameToMatch,\r
557                 List<AuthRefInfo> foundProps\r
558                 ) {\r
559         // Assume that authRefFieldInfo is keyed by the field name (possibly mapped for UI)\r
560         // and the values are elPaths to the field, where intervening group structures in\r
561         // lists of complex structures are replaced with "*". Thus, valid paths include\r
562         // the following (note that the ServiceBindingUtils prepend schema names to configured values):\r
563         // "schemaname:fieldname"\r
564         // "schemaname:scalarlistname"\r
565         // "schemaname:complexfieldname/fieldname"\r
566         // "schemaname:complexlistname/*/fieldname"\r
567         // "schemaname:complexlistname/*/scalarlistname"\r
568         // "schemaname:complexlistname/*/complexfieldname/fieldname"\r
569         // "schemaname:complexlistname/*/complexlistname/*/fieldname"\r
570         // etc.\r
571         for (AuthRefConfigInfo arci : authRefFieldInfo) {\r
572             try {\r
573                 // Get first property and work down as needed.\r
574                         Property prop = docModel.getProperty(arci.pathEls[0]);\r
575                         findAuthRefPropertiesInProperty(foundProps, prop, arci, 0, refNameToMatch);\r
576             } catch(Exception e) {\r
577                 logger.error("Problem fetching property: "+arci.pathEls[0]);\r
578             }\r
579         }\r
580         return foundProps;\r
581     }\r
582 \r
583     public static List<AuthRefInfo> findAuthRefPropertiesInProperty(\r
584                 List<AuthRefInfo> foundProps,\r
585                 Property prop, \r
586                 AuthRefConfigInfo arci,\r
587                 int pathStartIndex,             // Supports recursion and we work down the path\r
588                 String refNameToMatch\r
589                 ) {\r
590         if (pathStartIndex >= arci.pathEls.length) {\r
591                 throw new ArrayIndexOutOfBoundsException("Index = "+pathStartIndex+" for path: "\r
592                                                                                                 +arci.pathEls.toString());\r
593         }\r
594                 AuthRefInfo ari = null;\r
595                 if (prop == null) {\r
596                         return foundProps;\r
597                 }\r
598                 \r
599                 if (prop instanceof StringProperty) {   // scalar string\r
600                         addARIifMatches(refNameToMatch, arci, prop, foundProps);\r
601                 } else if(prop instanceof List) {\r
602                         List<Property> propList = (List<Property>)prop;\r
603                         // run through list. Must either be list of Strings, or Complex\r
604                         for (Property listItemProp : propList) {\r
605                                 if(listItemProp instanceof StringProperty) {\r
606                                         if(arci.pathEls.length-pathStartIndex != 1) {\r
607                                                 logger.error("Configuration for authRefs does not match schema structure: "\r
608                                                                 +arci.pathEls.toString());\r
609                                                 break;\r
610                                         } else {\r
611                                                 addARIifMatches(refNameToMatch, arci, listItemProp, foundProps);\r
612                                         }\r
613                                 } else if(listItemProp.isComplex()) {   \r
614                                         // Just recurse to handle this. Note that since this is a list of complex, \r
615                                         // which should look like listName/*/... we add 2 to the path start index \r
616                                         findAuthRefPropertiesInProperty(foundProps, listItemProp, arci,\r
617                                                         pathStartIndex+2, refNameToMatch);\r
618                                 } else {\r
619                                         logger.error("Configuration for authRefs does not match schema structure: "\r
620                                                         +arci.pathEls.toString());\r
621                                         break;\r
622                                 }\r
623                         }\r
624                 } else if(prop.isComplex()) {\r
625                         String localPropName = arci.pathEls[pathStartIndex];\r
626                         try {\r
627                                 Property localProp = prop.get(localPropName);\r
628                                 // Now just recurse, pushing down the path 1 step\r
629                                 findAuthRefPropertiesInProperty(foundProps, localProp, arci, \r
630                                                                                                 pathStartIndex, refNameToMatch);\r
631                         } catch(PropertyNotFoundException pnfe) {\r
632                                 logger.error("Could not find property: ["+localPropName+"] in path: "+\r
633                                                                 arci.getFullPath());\r
634                                 // Fall through - ari will be null and we will continue...\r
635                         }\r
636                 } else {\r
637                                 logger.error("Configuration for authRefs does not match schema structure: "\r
638                                                 +arci.pathEls.toString());\r
639                 }\r
640 \r
641         if (ari != null) {\r
642                 foundProps.add(ari); //FIXME: REM - This is dead code.  'ari' is never touched after being initalized to null.  Why?\r
643         }\r
644         \r
645         return foundProps;\r
646     }\r
647     \r
648     private static void addARIifMatches(\r
649                 String refNameToMatch, \r
650                 AuthRefConfigInfo arci, \r
651                 Property prop, \r
652                 List<AuthRefInfo> foundProps) {\r
653                 // Need to either match a passed refName \r
654                 // OR have no refName to match but be non-empty\r
655         try {\r
656                 String value = (String)prop.getValue();\r
657                         if(((refNameToMatch!=null) && refNameToMatch.equals(value))\r
658                                         || ((refNameToMatch==null) && Tools.notBlank(value))) {\r
659                                 // Found a match\r
660                                 logger.debug("Found a match on property: "+prop.getPath()+" with value: ["+value+"]");\r
661                                 AuthRefInfo ari = new AuthRefInfo(arci, prop);\r
662                                 foundProps.add(ari);\r
663                         }\r
664         } catch(PropertyException pe) {\r
665                         logger.debug("PropertyException on: "+prop.getPath()+pe.getLocalizedMessage());\r
666         }\r
667     }\r
668 \r
669     /*\r
670      * Identifies whether the refName was found in the supplied field.\r
671      * If passed a new RefName, will set that into fields in which the old one was found.\r
672      *\r
673      * Only works for:\r
674      * * Scalar fields\r
675      * * Repeatable scalar fields (aka multi-valued fields)\r
676      *\r
677      * Does not work for:\r
678      * * Structured fields (complexTypes)\r
679      * * Repeatable structured fields (repeatable complexTypes)\r
680     private static int refNameFoundInField(String oldRefName, Property fieldValue, String newRefName) {\r
681         int nFound = 0;\r
682         if (fieldValue instanceof List) {\r
683                 List<Property> fieldValueList = (List) fieldValue;\r
684                 for (Property listItemValue : fieldValueList) {\r
685                         try {\r
686                                 if ((listItemValue instanceof StringProperty)\r
687                                                 && oldRefName.equalsIgnoreCase((String)listItemValue.getValue())) {\r
688                                         nFound++;\r
689                                         if(newRefName!=null) {\r
690                                                 fieldValue.setValue(newRefName);\r
691                                         } else {\r
692                                                 // We cannot quit after the first, if we are replacing values.\r
693                                                 // If we are just looking (not replacing), finding one is enough.\r
694                                                 break;\r
695                                         }\r
696                                 }\r
697                         } catch( PropertyException pe ) {}\r
698                 }\r
699         } else {\r
700                 try {\r
701                         if ((fieldValue instanceof StringProperty)\r
702                                         && oldRefName.equalsIgnoreCase((String)fieldValue.getValue())) {\r
703                                         nFound++;\r
704                                 if(newRefName!=null) {\r
705                                         fieldValue.setValue(newRefName);\r
706                                 }\r
707                         }\r
708                 } catch( PropertyException pe ) {}\r
709         }\r
710         return nFound;\r
711     }\r
712      */\r
713 }\r
714 \r