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