]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
3d07d672aa51924b706a765224e19610219327d9
[tmp/jakarta-migration.git] /
1 /**
2  * This document is a part of the source code and related artifacts for
3  * CollectionSpace, an open source collections management system for museums and
4  * related institutions:
5  *
6  * http://www.collectionspace.org http://wiki.collectionspace.org
7  *
8  * Copyright 2009 University of California at Berkeley
9  *
10  * Licensed under the Educational Community License (ECL), Version 2.0. You may
11  * not use this file except in compliance with this License.
12  *
13  * You may obtain a copy of the ECL 2.0 License at
14  *
15  * https://source.collectionspace.org/collection-space/LICENSE.txt
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
19  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20  * License for the specific language governing permissions and limitations under
21  * the License.
22  */
23 package org.collectionspace.services.common.vocabulary;
24
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.ws.rs.core.Response;
32
33 import org.nuxeo.ecm.core.api.ClientException;
34 import org.nuxeo.ecm.core.api.DocumentModel;
35 import org.nuxeo.ecm.core.api.DocumentModelList;
36 import org.nuxeo.ecm.core.api.model.Property;
37 import org.nuxeo.ecm.core.api.model.PropertyException;
38 import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
39 import org.nuxeo.ecm.core.api.model.impl.primitives.StringProperty;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42 import org.collectionspace.services.client.CollectionSpaceClient;
43 import org.collectionspace.services.client.IQueryManager;
44 import org.collectionspace.services.client.IRelationsManager;
45 import org.collectionspace.services.client.PoxPayloadIn;
46 import org.collectionspace.services.client.PoxPayloadOut;
47 import org.collectionspace.services.common.CSWebApplicationException;
48 import org.collectionspace.services.common.ServiceMain;
49 import org.collectionspace.services.common.StoredValuesUriTemplate;
50 import org.collectionspace.services.common.UriTemplateFactory;
51 import org.collectionspace.services.common.UriTemplateRegistry;
52 import org.collectionspace.services.common.UriTemplateRegistryKey;
53 import org.collectionspace.services.common.context.ServiceContext;
54 import org.collectionspace.services.common.context.AbstractServiceContextImpl;
55 import org.collectionspace.services.common.api.RefNameUtils;
56 import org.collectionspace.services.common.api.Tools;
57 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
58 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
59 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
60 import org.collectionspace.services.common.context.ServiceBindingUtils;
61 import org.collectionspace.services.common.document.DocumentException;
62 import org.collectionspace.services.common.document.DocumentFilter;
63 import org.collectionspace.services.common.document.DocumentNotFoundException;
64 import org.collectionspace.services.common.document.DocumentUtils;
65 import org.collectionspace.services.common.document.DocumentWrapper;
66 import org.collectionspace.services.common.query.QueryManager;
67 import org.collectionspace.services.common.relation.RelationUtils;
68 import org.collectionspace.services.common.repository.RepositoryClient;
69 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
70 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler;
71 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
72 import org.collectionspace.services.common.security.SecurityUtils;
73 import org.collectionspace.services.config.service.ServiceBindingType;
74 import org.collectionspace.services.jaxb.AbstractCommonList;
75 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
76
77 /**
78  * RefNameServiceUtils is a collection of services utilities related to refName
79  * usage.
80  *
81  * $LastChangedRevision: $ $LastChangedDate: $
82  */
83 public class RefNameServiceUtils {
84
85     public static enum SpecifierForm {
86         CSID, URN_NAME // Either a CSID or a short ID
87     };
88
89     public static class Specifier {
90         //
91         // URN statics for things like urn:cspace:name(grover)
92         //
93         final static String URN_PREFIX = "urn:cspace:";
94         final static int URN_PREFIX_LEN = URN_PREFIX.length();
95         final static String URN_PREFIX_NAME = "name(";
96         final static int URN_NAME_PREFIX_LEN = URN_PREFIX_LEN + URN_PREFIX_NAME.length();
97         final static String URN_PREFIX_ID = "id(";
98         final static int URN_ID_PREFIX_LEN = URN_PREFIX_LEN + URN_PREFIX_ID.length();
99         
100         public SpecifierForm form;
101         public String value;
102
103         public Specifier(SpecifierForm form, String value) {
104             this.form = form;
105             this.value = value;
106         }
107         
108         /*
109          *  identifier can be a CSID form like a8ad38ec-1d7d-4bf2-bd31 or a URN form like urn:cspace:name(shortid) or urn:cspace:id(a8ad38ec-1d7d-4bf2-bd31)
110          *
111          */
112         public static Specifier getSpecifier(String identifier) throws CSWebApplicationException {
113                 return getSpecifier(identifier, "NO-OP", "NO-OP");
114         }
115
116         /*
117          *  identifier can be a CSID form like a8ad38ec-1d7d-4bf2-bd31 or a URN form like urn:cspace:name(shortid) or urn:cspace:id(a8ad38ec-1d7d-4bf2-bd31)
118          *
119          */
120         public static Specifier getSpecifier(String identifier, String method, String op) throws CSWebApplicationException {
121                 Specifier result = null;
122
123                 if (identifier != null) {
124                 if (!identifier.startsWith(URN_PREFIX)) {
125                     // We'll assume it is a CSID and complain if it does not match
126                     result = new Specifier(SpecifierForm.CSID, identifier);
127                 } else {
128                     if (identifier.startsWith(URN_PREFIX_NAME, URN_PREFIX_LEN)) {
129                         int closeParen = identifier.indexOf(')', URN_NAME_PREFIX_LEN);
130                         if (closeParen >= 0) {
131                             result = new Specifier(SpecifierForm.URN_NAME,
132                                     identifier.substring(URN_NAME_PREFIX_LEN, closeParen));
133                         }
134                     } else if (identifier.startsWith(URN_PREFIX_ID, URN_PREFIX_LEN)) {
135                         int closeParen = identifier.indexOf(')', URN_ID_PREFIX_LEN);
136                         if (closeParen >= 0) {
137                             result = new Specifier(SpecifierForm.CSID,
138                                     identifier.substring(URN_ID_PREFIX_LEN, closeParen));
139                         }
140                     } else {
141                         logger.error(method + ": bad or missing specifier!");
142                         Response response = Response.status(Response.Status.BAD_REQUEST).entity(
143                                 op + " failed on bad or missing Authority specifier").type(
144                                 "text/plain").build();
145                         throw new CSWebApplicationException(response);
146                     }
147                 }
148             }
149             
150             return result;
151         }
152         
153         /**
154          * Creates a refName in the name / shortIdentifier form.
155          *
156          * @param shortId a shortIdentifier for an authority or one of its terms
157          * @return a refName for that authority or term, in the name / shortIdentifier form.
158          *         If the provided shortIdentifier is null or empty, returns
159          *         the empty string.
160          */
161         public static String createShortIdURNValue(String shortId) {
162                 String result = null;
163                 
164             if (shortId != null || !shortId.trim().isEmpty()) {
165                 result = String.format("urn:cspace:name(%s)", shortId);
166             }
167             
168             return result;
169         }        
170         
171         /**
172          * Returns a URN string identifier -e.g., urn:cspace:name(patrick) or urn:cspace:id(579d18a6-b464-4b11-ba3a)
173          * 
174          * @return
175          * @throws Exception
176          */
177         public String getURNValue() throws Exception {
178                 String result = null;
179                 
180                 if (form == SpecifierForm.CSID) {
181                         result = String.format("urn:cspace:id(%s)", value);
182                 } else if (form == SpecifierForm.URN_NAME) {
183                         result = String.format("urn:cspace:name(%s)", value);
184                 } else {
185                         throw new Exception(String.format("Unknown specifier form '%s'.", form));
186                 }
187                 
188                 return result;
189         }
190     }
191     
192     public static class AuthorityItemSpecifier {
193         private Specifier parentSpecifier;
194         private Specifier itemSpecifier;
195         
196         public AuthorityItemSpecifier(Specifier parentSpecifier, Specifier itemSpecifier) {
197                 this.parentSpecifier = parentSpecifier;
198                 this.itemSpecifier = itemSpecifier;
199         }
200         
201         public AuthorityItemSpecifier(SpecifierForm form, String parentCsidOrShortId, String itemCsidOrShortId) {
202                 this.parentSpecifier = new Specifier(form, parentCsidOrShortId);
203                 this.itemSpecifier = new Specifier(form, itemCsidOrShortId);
204         }       
205         
206         public Specifier getParentSpecifier() {
207                 return this.parentSpecifier;
208         }
209         
210         public Specifier getItemSpecifier() {
211                 return this.itemSpecifier;
212         }
213         
214         @Override
215         public String toString() {
216                 String result = "%s/items/%s";
217                 
218                 try {
219                                 result = String.format(result, this.parentSpecifier.getURNValue(), this.itemSpecifier.getURNValue());
220                         } catch (Exception e) {
221                                 result = "Unknown error trying to get string representation of Specifier.";
222                                 logger.error(result, e);
223                         }
224                 
225                 return result;
226         }
227     }
228
229     public static class AuthRefConfigInfo {
230
231         public String getQualifiedDisplayName() {
232             return (Tools.isBlank(schema))
233                     ? displayName : DocumentUtils.appendSchemaName(schema, displayName);
234         }
235
236         public String getDisplayName() {
237             return displayName;
238         }
239
240         public void setDisplayName(String displayName) {
241             this.displayName = displayName;
242         }
243         String displayName;
244         String schema;
245
246         public String getSchema() {
247             return schema;
248         }
249
250         public void setSchema(String schema) {
251             this.schema = schema;
252         }
253
254         public String getFullPath() {
255             return fullPath;
256         }
257
258         public void setFullPath(String fullPath) {
259             this.fullPath = fullPath;
260         }
261         String fullPath;
262         protected String[] pathEls;
263
264         public AuthRefConfigInfo(AuthRefConfigInfo arci) {
265             this.displayName = arci.displayName;
266             this.schema = arci.schema;
267             this.fullPath = arci.fullPath;
268             this.pathEls = arci.pathEls;
269             // Skip the pathElse check, since we are creatign from another (presumably valid) arci.
270         }
271
272         public AuthRefConfigInfo(String displayName, String schema, String fullPath, String[] pathEls) {
273             this.displayName = displayName;
274             this.schema = schema;
275             this.fullPath = fullPath;
276             this.pathEls = pathEls;
277             checkPathEls();
278         }
279
280         // Split a config value string like "intakes_common:collector", or
281         // "collectionobjects_common:contentPeoples|contentPeople"
282         // "collectionobjects_common:assocEventGroupList/*/assocEventPlace"
283         // If has a pipe ('|') second part is a displayLabel, and first is path
284         // Otherwise, entry is a path, and can use the last pathElement as displayName
285         // Should be schema qualified.
286         public AuthRefConfigInfo(String configString) {
287             String[] pair = configString.split("\\|", 2);
288             String[] pathEls;
289             String displayName, fullPath;
290             if (pair.length == 1) {
291                 // no label specifier, so we'll defer getting label
292                 fullPath = pair[0];
293                 pathEls = pair[0].split("/");
294                 displayName = pathEls[pathEls.length - 1];
295             } else {
296                 fullPath = pair[0];
297                 pathEls = pair[0].split("/");
298                 displayName = pair[1];
299             }
300             String[] schemaSplit = pathEls[0].split(":", 2);
301             String schema;
302             if (schemaSplit.length == 1) {    // schema not specified
303                 schema = null;
304             } else {
305                 schema = schemaSplit[0];
306                 if (pair.length == 1 && pathEls.length == 1) {    // simplest case of field in top level schema, no labelll
307                     displayName = schemaSplit[1];    // Have to fix up displayName to have no schema
308                 }
309             }
310             this.displayName = displayName;
311             this.schema = schema;
312             this.fullPath = fullPath;
313             this.pathEls = pathEls;
314             checkPathEls();
315         }
316
317         protected void checkPathEls() {
318             int len = pathEls.length;
319             if (len < 1) {
320                 throw new InternalError("Bad values in authRef info - caller screwed up:" + fullPath);
321             }
322             // Handle case of them putting a leading slash on the path
323             if (len > 1 && pathEls[0].endsWith(":")) {
324                 len--;
325                 String[] newArray = new String[len];
326                 newArray[0] = pathEls[0] + pathEls[1];
327                 if (len >= 2) {
328                     System.arraycopy(pathEls, 2, newArray, 1, len - 1);
329                 }
330                 pathEls = newArray;
331             }
332         }
333     }
334
335     public static class AuthRefInfo extends AuthRefConfigInfo {
336
337         public Property getProperty() {
338             return property;
339         }
340
341         public void setProperty(Property property) {
342             this.property = property;
343         }
344         Property property;
345
346         public AuthRefInfo(String displayName, String schema, String fullPath, String[] pathEls, Property prop) {
347             super(displayName, schema, fullPath, pathEls);
348             this.property = prop;
349         }
350
351         public AuthRefInfo(AuthRefConfigInfo arci, Property prop) {
352             super(arci);
353             this.property = prop;
354         }
355     }
356     
357     private static final Logger logger = LoggerFactory.getLogger(RefNameServiceUtils.class);
358     private static ArrayList<String> refNameServiceTypes = null;
359
360     public static void updateRefNamesInRelations(
361             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
362             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
363             CoreSessionInterface repoSession,
364             String oldRefName,
365             String newRefName) throws Exception {
366         //
367         // First, look for and update all the places where the refName is the "subject" of the relationship
368         //
369         RelationUtils.updateRefNamesInRelations(ctx, repoClient, repoSession, IRelationsManager.SUBJECT_REFNAME, oldRefName, newRefName);
370         
371         //
372         // Next, look for and update all the places where the refName is the "object" of the relationship
373         //
374         RelationUtils.updateRefNamesInRelations(ctx, repoClient, repoSession, IRelationsManager.OBJECT_REFNAME, oldRefName, newRefName);
375     }
376     
377         public static List<AuthRefConfigInfo> getConfiguredAuthorityRefs(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
378                 List<String> authRefFields = ((AbstractServiceContextImpl) ctx).getAllPartsPropertyValues(
379                                 ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
380                 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>(authRefFields.size());
381                 for (String spec : authRefFields) {
382                         AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
383                         authRefsInfo.add(arci);
384                 }
385                 return authRefsInfo;
386         }
387
388     public static AuthorityRefDocList getAuthorityRefDocs(
389                 CoreSessionInterface repoSession,
390             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
391             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
392             List<String> serviceTypes,
393             String refName,
394             String refPropName, // authRef or termRef, authorities or vocab terms.
395             DocumentFilter filter,
396             boolean useDefaultOrderByClause,
397             boolean computeTotal) throws DocumentException, DocumentNotFoundException {
398         AuthorityRefDocList wrapperList = new AuthorityRefDocList();
399         AbstractCommonList commonList = (AbstractCommonList) wrapperList;
400         int pageNum = filter.getStartPage();
401         int pageSize = filter.getPageSize();
402         
403         List<AuthorityRefDocList.AuthorityRefDocItem> list =
404                 wrapperList.getAuthorityRefDocItem();
405
406         Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
407         Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
408
409         NuxeoRepositoryClientImpl nuxeoRepoClient = (NuxeoRepositoryClientImpl) repoClient;
410         try {
411             // Ignore any provided page size and number query parameters in
412             // the following call, as they pertain to the list of authority
413             // references to be returned, not to the list of documents to be
414             // scanned for those references.
415             
416             // Get a list of possibly referencing documents. This list is
417             // lazily loaded, page by page. Ideally, only one page will
418             // need to be loaded to fill one page of results. Some number
419             // of possibly referencing documents will be false positives,
420             // so use a page size of double the requested page size to
421             // account for those.
422             DocumentModelList docList = findAllAuthorityRefDocs(ctx, 
423                     repoClient, 
424                     repoSession,
425                     serviceTypes, 
426                     refName, 
427                     refPropName, 
428                     queriedServiceBindings, 
429                     authRefFieldsByService,
430                     filter.getWhereClause(), 
431                     null, // orderByClause
432                     2*pageSize,
433                     useDefaultOrderByClause,
434                     computeTotal);
435
436             if (docList == null) { // found no authRef fields - nothing to process
437                 return wrapperList;
438             }
439
440             String fieldList = "docType|docId|docNumber|docName|sourceField|uri|refName|updatedAt|workflowState";  // FIXME: Should not be hard-coded string
441             commonList.setFieldsReturned(fieldList);
442
443             // As a side-effect, the method called below modifies the value of
444             // the 'list' variable, which holds the list of references to
445             // an authority item.
446             //
447             // There can be more than one reference to a particular authority
448             // item within any individual document scanned, so the number of
449             // authority references may potentially exceed the total number
450             // of documents scanned.
451
452             // Strip off displayName and only match the base, so we get references to all 
453             // the NPTs as well as the PT.
454                 String strippedRefName = RefNameUtils.stripAuthorityTermDisplayName(refName);
455                 
456                 // *** Need to pass in pagination info here. 
457             int nRefsFound = processRefObjsDocListForList(docList, ctx.getTenantId(), strippedRefName, 
458                         queriedServiceBindings, authRefFieldsByService, // the actual list size needs to be updated to the size of "list"
459                     list, pageSize, pageNum);
460                 
461             commonList.setPageSize(pageSize);
462             
463             // Values returned in the pagination block above the list items
464             // need to reflect the number of references to authority items
465             // returned, rather than the number of documents originally scanned
466             // to find such references.
467             // This will be an estimate only...
468             commonList.setPageNum(pageNum);
469                 commonList.setTotalItems(nRefsFound);   // Accurate if total was scanned, otherwise, just an estimate
470             commonList.setItemsInPage(list.size());
471             
472             if (logger.isDebugEnabled() && (nRefsFound < docList.size())) {
473                 logger.debug("Internal curiosity: got fewer matches of refs than # docs matched..."); // We found a ref to ourself and have excluded it.
474             }
475         } catch (Exception e) {
476             logger.error("Could not retrieve a list of documents referring to the specified authority item", e);
477             wrapperList = null;
478         }
479
480         return wrapperList;
481     }
482
483     private static ArrayList<String> getRefNameServiceTypes() {
484         if (refNameServiceTypes == null) {
485             refNameServiceTypes = new ArrayList<String>();
486             refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_AUTHORITY);
487             refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_OBJECT);
488             refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_PROCEDURE);
489         }
490         return refNameServiceTypes;
491     }
492     
493     // Seems like a good value - no real data to set this well.
494     // Note: can set this value lower during debugging; e.g. to 3 - ADR 2012-07-10
495     private static final int N_OBJS_TO_UPDATE_PER_LOOP = 100;
496
497     public static int updateAuthorityRefDocs(
498             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
499             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
500             CoreSessionInterface repoSession,
501             String oldRefName,
502             String newRefName,
503             String refPropName) throws Exception {
504         Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
505         Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
506
507         int docsScanned = 0;
508         int nRefsFound = 0;
509         int currentPage = 0;
510         int docsInCurrentPage = 0;
511         final String WHERE_CLAUSE_ADDITIONS_VALUE = null;
512         final String ORDER_BY_VALUE = CollectionSpaceClient.CORE_CREATED_AT  // "collectionspace_core:createdAt";
513                                           + ", " + IQueryManager.NUXEO_UUID; // CSPACE-6333: Add secondary sort on uuid, in case records have the same createdAt timestamp.
514
515         if (repoClient instanceof NuxeoRepositoryClientImpl == false) {
516             throw new InternalError("updateAuthorityRefDocs() called with unknown repoClient type!");
517         }
518         
519         try { // REM - How can we deal with transaction and timeout issues here?
520             final int pageSize = N_OBJS_TO_UPDATE_PER_LOOP;
521             DocumentModelList docList;
522             boolean morePages = true;
523             while (morePages) {
524
525                 docList = findAuthorityRefDocs(ctx, 
526                         repoClient, 
527                         repoSession,
528                         getRefNameServiceTypes(), 
529                         oldRefName, 
530                         refPropName,
531                         queriedServiceBindings, 
532                         authRefFieldsByService, 
533                         WHERE_CLAUSE_ADDITIONS_VALUE, 
534                         ORDER_BY_VALUE,
535                         currentPage,
536                         pageSize,
537                         true,       // useDefaultOrderByClause
538                         false);     // computeTotal
539
540                 if (docList == null) {
541                     logger.debug("updateAuthorityRefDocs: no documents could be found that referenced the old refName");
542                     break;
543                 }
544                 docsInCurrentPage = docList.size();
545                 logger.debug("updateAuthorityRefDocs: current page=" + currentPage + " documents included in page=" + docsInCurrentPage);
546                 if (docsInCurrentPage == 0) {
547                     logger.debug("updateAuthorityRefDocs: no more documents requiring refName updates could be found");
548                     break;
549                 }
550                 if (docsInCurrentPage < pageSize) {
551                     logger.debug("updateAuthorityRefDocs: assuming no more documents requiring refName updates will be found, as docsInCurrentPage < pageSize");
552                     morePages = false;
553                 }
554
555                 // Only match complete refNames - unless and until we decide how to resolve changes
556                 // to NPTs we will defer that and only change PTs or refNames as passed in.
557                 int nRefsFoundThisPage = processRefObjsDocListForUpdate(ctx, docList, ctx.getTenantId(), oldRefName, 
558                                 queriedServiceBindings, authRefFieldsByService, // Perform the refName updates on the list of document models
559                         newRefName);
560                 if (nRefsFoundThisPage > 0) {
561                     ((NuxeoRepositoryClientImpl) repoClient).saveDocListWithoutHandlerProcessing(ctx, repoSession, docList, true); // Flush the document model list out to Nuxeo storage
562                     nRefsFound += nRefsFoundThisPage;
563                 }
564
565                 // FIXME: Per REM, set a limit of num objects - something like
566                 // 1000K objects - and also add a log Warning after some threshold
567                 docsScanned += docsInCurrentPage;
568                 if (morePages) {
569                     currentPage++;
570                 }
571
572             }
573         } catch (Exception e) {
574             logger.error("Internal error updating the AuthorityRefDocs: " + e.getLocalizedMessage());
575             logger.debug(Tools.errorToString(e, true));
576             throw e;
577         }
578         logger.debug("updateAuthorityRefDocs replaced a total of " + nRefsFound + " authority references, within as many as " + docsScanned + " scanned document(s)");
579         return nRefsFound;
580     }
581
582     private static DocumentModelList findAllAuthorityRefDocs(
583             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
584             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
585             CoreSessionInterface repoSession, List<String> serviceTypes,
586             String refName,
587             String refPropName,
588             Map<String, ServiceBindingType> queriedServiceBindings,
589             Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
590             String whereClauseAdditions,
591             String orderByClause,
592             int pageSize,
593             boolean useDefaultOrderByClause,
594             boolean computeTotal) throws DocumentException, DocumentNotFoundException {
595                 
596                 return new LazyAuthorityRefDocList(ctx, 
597                         repoClient, 
598                         repoSession,
599                                 serviceTypes, 
600                                 refName, 
601                                 refPropName, 
602                                 queriedServiceBindings, 
603                                 authRefFieldsByService,
604                                 whereClauseAdditions, 
605                                 orderByClause,
606                                 pageSize, 
607                                 useDefaultOrderByClause, 
608                                 computeTotal);
609     }
610     
611     protected static DocumentModelList findAuthorityRefDocs(
612             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
613             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
614             CoreSessionInterface repoSession, List<String> serviceTypes,
615             String refName,
616             String refPropName,
617             Map<String, ServiceBindingType> queriedServiceBindings,
618             Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
619             String whereClauseAdditions,
620             String orderByClause,
621             int pageNum,
622             int pageSize,
623             boolean useDefaultOrderByClause,
624             boolean computeTotal) throws DocumentException, DocumentNotFoundException {
625
626         // Get the service bindings for this tenant
627         TenantBindingConfigReaderImpl tReader = ServiceMain.getInstance().getTenantBindingConfigReader();
628         
629         // We need to get all the procedures, authorities, and objects.
630         List<ServiceBindingType> servicebindings = tReader.getServiceBindingsByType(ctx.getTenantId(), serviceTypes);
631         if (servicebindings == null || servicebindings.isEmpty()) {
632             logger.error("RefNameServiceUtils.getAuthorityRefDocs: No services bindings found, cannot proceed!");
633             return null;
634         }
635         // Filter the list for current user rights
636         servicebindings = SecurityUtils.getReadableServiceBindingsForCurrentUser(servicebindings);
637
638         ArrayList<String> docTypes = new ArrayList<String>();
639
640         String query = computeWhereClauseForAuthorityRefDocs(refName, refPropName, docTypes, servicebindings, // REM - Side effect that docTypes, authRefFieldsByService, and queriedServiceBindings get set/change.  Any others?
641                 queriedServiceBindings, authRefFieldsByService);
642         if (query == null) { // found no authRef fields - nothing to query
643             return null;
644         }
645         // Additional qualifications, like workflow state
646         if (Tools.notBlank(whereClauseAdditions)) {
647             query += " AND " + whereClauseAdditions;
648         }
649         // Now we have to issue the search
650         NuxeoRepositoryClientImpl nuxeoRepoClient = (NuxeoRepositoryClientImpl) repoClient;
651         DocumentWrapper<DocumentModelList> docListWrapper = nuxeoRepoClient.findDocs(
652                 ctx,
653                 repoSession,
654                 docTypes, 
655                 query, 
656                 orderByClause, 
657                 pageNum, 
658                 pageSize, 
659                 useDefaultOrderByClause, 
660                 computeTotal);
661         // Now we gather the info for each document into the list and return
662         DocumentModelList docList = docListWrapper.getWrappedObject();
663         return docList;
664     }
665     
666     private static final boolean READY_FOR_COMPLEX_QUERY = true;
667
668     private static String computeWhereClauseForAuthorityRefDocs(
669             String refName,
670             String refPropName,
671             ArrayList<String> docTypes,
672             List<ServiceBindingType> servicebindings,
673             Map<String, ServiceBindingType> queriedServiceBindings,
674             Map<String, List<AuthRefConfigInfo>> authRefFieldsByService) {
675
676         boolean fFirst = true;
677         List<String> authRefFieldPaths;
678         for (ServiceBindingType sb : servicebindings) {
679             // Gets the property names for each part, qualified with the part label (which
680             // is also the table name, the way that the repository works).
681             authRefFieldPaths =
682                     ServiceBindingUtils.getAllPartsPropertyValues(sb,
683                     refPropName, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
684             if (authRefFieldPaths.isEmpty()) {
685                 continue;
686             }
687             ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>();
688             for (String spec : authRefFieldPaths) {
689                 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
690                 authRefsInfo.add(arci);
691             }
692
693             String docType = sb.getObject().getName();
694             queriedServiceBindings.put(docType, sb);
695             authRefFieldsByService.put(docType, authRefsInfo);
696             docTypes.add(docType);
697             fFirst = false;
698         }
699         if (fFirst) { // found no authRef fields - nothing to query
700             return null;
701         }
702         // We used to build a complete matches query, but that was too complex.
703         // Just build a keyword query based upon some key pieces - the urn syntax elements and the shortID
704         // Note that this will also match the Item itself, but that will get filtered out when
705         // we compute actual matches.
706         AuthorityTermInfo authTermInfo = RefNameUtils.parseAuthorityTermInfo(refName);
707
708         String keywords = RefNameUtils.URN_PREFIX
709                 + " AND " + (authTermInfo.inAuthority.name != null
710                 ? authTermInfo.inAuthority.name : authTermInfo.inAuthority.csid)
711                 + " AND " + (authTermInfo.name != null
712                 ? authTermInfo.name : authTermInfo.csid); // REM - This seems likely to cause trouble?  We should consider searching for the full refname -excluding the display name suffix?
713
714         String whereClauseStr = QueryManager.createWhereClauseFromKeywords(keywords);
715
716         if (logger.isTraceEnabled()) {
717             logger.trace("The 'where' clause to find refObjs is: ", whereClauseStr);
718         }
719
720         return whereClauseStr;
721     }
722     
723     // TODO there are multiple copies of this that should be put somewhere common.
724         protected static String getRefname(DocumentModel docModel) throws ClientException {
725                 String result = (String)docModel.getProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
726                                 CollectionSpaceClient.COLLECTIONSPACE_CORE_REFNAME);
727                 return result;
728         }
729
730     private static int processRefObjsDocListForUpdate(
731                 ServiceContext ctx,
732             DocumentModelList docList,
733             String tenantId,
734             String refName,
735             Map<String, ServiceBindingType> queriedServiceBindings,
736             Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
737             String newAuthorityRefName) {
738         boolean matchBaseOnly = false;
739         
740         if (ctx.shouldForceUpdateRefnameReferences() == true) {
741                 refName = RefNameUtils.stripAuthorityTermDisplayName(refName);
742                 matchBaseOnly = true;
743         }
744         
745         return processRefObjsDocList(docList, tenantId, refName, matchBaseOnly, queriedServiceBindings,
746                         authRefFieldsByService, null, 0, 0, newAuthorityRefName);
747     }
748                         
749     private static int processRefObjsDocListForList(
750             DocumentModelList docList,
751             String tenantId,
752             String refName,
753             Map<String, ServiceBindingType> queriedServiceBindings,
754             Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
755             List<AuthorityRefDocList.AuthorityRefDocItem> list, 
756             int pageSize, int pageNum) {
757         return processRefObjsDocList(docList, tenantId, refName, true, queriedServiceBindings,
758                         authRefFieldsByService, list, pageSize, pageNum, null);
759     }
760                         
761
762         /*
763      * Runs through the list of found docs, processing them. If list is
764      * non-null, then processing means gather the info for items. If list is
765      * null, and newRefName is non-null, then processing means replacing and
766      * updating. If processing/updating, this must be called in the context of
767      * an open session, and caller must release Session after calling this.
768      *
769      */
770     private static int processRefObjsDocList(
771             DocumentModelList docList,
772             String tenantId,
773             String refName,
774             boolean matchBaseOnly,
775             Map<String, ServiceBindingType> queriedServiceBindings,
776             Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
777             List<AuthorityRefDocList.AuthorityRefDocItem> list,
778             int pageSize, int pageNum,  // Only used when constructing a list.
779             String newAuthorityRefName) {
780         UriTemplateRegistry registry = ServiceMain.getInstance().getUriTemplateRegistry();
781         Iterator<DocumentModel> iter = docList.iterator();
782         int nRefsFoundTotal = 0;
783         boolean foundSelf = false;
784
785         // When paginating results, we have to guess at the total. First guess is the number of docs returned
786         // by the query. However, this returns some false positives, so may be high. 
787         // In addition, we can match multiple fields per doc, so this may be low. Fun, eh?
788         int nDocsReturnedInQuery = (int)docList.totalSize();
789         int nDocsProcessed = 0;
790         int firstItemInPage = pageNum*pageSize;
791         while (iter.hasNext()) {
792             DocumentModel docModel = iter.next();
793             AuthorityRefDocList.AuthorityRefDocItem ilistItem;
794
795             String docType = docModel.getDocumentType().getName(); // REM - This will be a tentant qualified document type
796             docType = ServiceBindingUtils.getUnqualifiedTenantDocType(docType);
797             ServiceBindingType sb = queriedServiceBindings.get(docType);
798             if (sb == null) {
799                 throw new RuntimeException(
800                         "getAuthorityRefDocs: No Service Binding for docType: " + docType);
801             }
802
803             if (list == null) { // no list - should be update refName case.
804                 if (newAuthorityRefName == null) {
805                     throw new InternalError("processRefObjsDocList() called with neither an itemList nor a new RefName!");
806                 }
807                 ilistItem = null;
808                 pageSize = 0;
809                 firstItemInPage = 0;    // Do not paginate if updating, rather than building list
810             } else {    // Have a list - refObjs case
811                 if (newAuthorityRefName != null) {
812                     throw new InternalError("processRefObjsDocList() called with both an itemList and a new RefName!");
813                 }
814                 if(firstItemInPage > 100) {
815                         logger.warn("Processing a large offset (size:{}, num:{}) for refObjs - will be expensive!!!",
816                                                 pageSize, pageNum);
817                 }
818                 // Note that we have to go through check all the fields to determine the actual page start
819                 ilistItem = new AuthorityRefDocList.AuthorityRefDocItem();
820                 String csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
821                 try {
822                         String itemRefName = getRefname(docModel);
823                         ilistItem.setRefName(itemRefName);
824                 } catch (ClientException ce) {
825                     throw new RuntimeException(
826                             "processRefObjsDocList: Problem fetching refName from item Object: " 
827                                         + ce.getLocalizedMessage());
828                 }
829                 ilistItem.setDocId(csid);
830                 String uri = "";
831                 UriTemplateRegistryKey key = new UriTemplateRegistryKey(tenantId, docType);
832                 StoredValuesUriTemplate template = registry.get(key);
833                 if (template != null) {
834                     Map<String, String> additionalValues = new HashMap<String, String>();
835                     if (template.getUriTemplateType() == UriTemplateFactory.RESOURCE) {
836                         additionalValues.put(UriTemplateFactory.IDENTIFIER_VAR, csid);
837                         uri = template.buildUri(additionalValues);
838                     } else if (template.getUriTemplateType() == UriTemplateFactory.ITEM) {
839                         try {
840                             String inAuthorityCsid = (String) NuxeoUtils.getProperyValue(docModel, "inAuthority"); //docModel.getPropertyValue("inAuthority"); // AuthorityItemJAXBSchema.IN_AUTHORITY
841                             additionalValues.put(UriTemplateFactory.IDENTIFIER_VAR, inAuthorityCsid);
842                             additionalValues.put(UriTemplateFactory.ITEM_IDENTIFIER_VAR, csid);
843                             uri = template.buildUri(additionalValues);
844                         } catch (Exception e) {
845                             logger.warn("Could not extract inAuthority property from authority item record: " + e.getMessage());
846                         }
847                     } else if (template.getUriTemplateType() == UriTemplateFactory.CONTACT) {
848                         // FIXME: Generating contact sub-resource URIs requires additional work,
849                         // as a follow-on to CSPACE-5271 - ADR 2012-08-16
850                         // Sets the default (empty string) value for uri, for now
851                     } else {
852                         logger.warn("Unrecognized URI template type = " + template.getUriTemplateType());
853                         // Sets the default (empty string) value for uri
854                     }
855                 } else { // (if template == null)
856                     logger.warn("Could not retrieve URI template from registry via tenant ID "
857                             + tenantId + " and docType " + docType);
858                     // Sets the default (empty string) value for uri
859                 }
860                 ilistItem.setUri(uri);
861                 try {
862                     ilistItem.setWorkflowState(docModel.getCurrentLifeCycleState());
863                     ilistItem.setUpdatedAt(NuxeoDocumentModelHandler.getUpdatedAtAsString(docModel));
864                 } catch (Exception e) {
865                     logger.error("Error getting core values for doc [" + csid + "]: " + e.getLocalizedMessage());
866                 }
867                 ilistItem.setDocType(docType);
868                 ilistItem.setDocNumber(
869                         ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NUMBER_PROP, docModel));
870                 ilistItem.setDocName(
871                         ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NAME_PROP, docModel));
872             }
873             // Now, we have to loop over the authRefFieldsByService to figure out
874             // out which field(s) matched this.
875             List<AuthRefConfigInfo> matchingAuthRefFields = authRefFieldsByService.get(docType);
876             if (matchingAuthRefFields == null || matchingAuthRefFields.isEmpty()) {
877                 throw new RuntimeException(
878                         "getAuthorityRefDocs: internal logic error: can't fetch authRefFields for DocType.");
879             }
880             //String authRefAncestorField = "";
881             //String authRefDescendantField = "";
882             //String sourceField = "";
883
884             ArrayList<RefNameServiceUtils.AuthRefInfo> foundProps = new ArrayList<RefNameServiceUtils.AuthRefInfo>();
885             try {
886                 findAuthRefPropertiesInDoc(docModel, matchingAuthRefFields, refName, matchBaseOnly, foundProps); // REM - side effect that foundProps is set
887                 if(!foundProps.isEmpty()) {
888                     int nRefsFoundInDoc = 0;
889                         for (RefNameServiceUtils.AuthRefInfo ari : foundProps) {
890                                 if (ilistItem != null) {
891                                         // So this is a true positive, and not a false one. We have to consider pagination now.
892                                         if(nRefsFoundTotal >= firstItemInPage) {        // skipped enough already
893                                                 if (nRefsFoundInDoc == 0) {    // First one?
894                                                         ilistItem.setSourceField(ari.getQualifiedDisplayName());
895                                                 } else {    // duplicates from one object
896                                                         ilistItem = cloneAuthRefDocItem(ilistItem, ari.getQualifiedDisplayName());
897                                                 }
898                                                 list.add(ilistItem);
899                                         nRefsFoundInDoc++;      // Only increment if processed, or clone logic above will fail
900                                         }
901                                 } else {    // update refName case
902                                         Property propToUpdate = ari.getProperty();
903                                         propToUpdate.setValue(newAuthorityRefName);
904                                 }
905                                 nRefsFoundTotal++;              // Whether we processed or not, we found - essential to pagination logic
906                         }
907                 } else if(ilistItem != null) {
908                         String docRefName = ilistItem.getRefName();
909                     if (matchBaseOnly?
910                                         (docRefName!=null && docRefName.startsWith(refName))
911                                         :refName.equals(docRefName)) {
912                                 // We found the self for an item
913                                 foundSelf = true;
914                                 logger.debug("getAuthorityRefDocs: Result: "
915                                                                 + docType + " [" + NuxeoUtils.getCsid(docModel)
916                                                                 + "] appears to be self for: ["
917                                                                 + refName + "]");
918                         } else {
919                                 logger.debug("getAuthorityRefDocs: Result: "
920                                                                 + docType + " [" + NuxeoUtils.getCsid(docModel)
921                                                                 + "] does not reference ["
922                                                                 + refName + "]");
923                         }
924                 }
925             } catch (ClientException ce) {
926                 throw new RuntimeException(
927                                 "getAuthorityRefDocs: Problem fetching values from repo: " + ce.getLocalizedMessage());
928             }
929             nDocsProcessed++;
930             // Done processing that doc. Are we done with the whole page?
931             // Note pageSize <=0 means do them all
932             if((pageSize > 0) && ((nRefsFoundTotal-firstItemInPage)>=pageSize)) {
933                 // Quitting early, so we need to estimate the total. Assume one per doc
934                 // for the rest of the docs we matched in the query
935                 int unprocessedDocs = nDocsReturnedInQuery - nDocsProcessed;
936                 if(unprocessedDocs>0) {
937                         // We generally match ourselves in the keyword search. If we already saw ourselves
938                         // then do not try to correct for this. Otherwise, decrement the total.
939                         // Yes, this is fairly goofy, but the whole estimation mechanism is goofy. 
940                         if(!foundSelf)
941                                 unprocessedDocs--;
942                         nRefsFoundTotal += unprocessedDocs;
943                 }
944                 break;
945             }
946         } // close while(iterator)
947         return nRefsFoundTotal;
948     }
949
950     /**
951      * Clone an AuthorityRefDocItem which is a JAX-B generated class.  Be sure we're copying every field defined in the XSD (XML Schema) that is
952      * found here services\jaxb\src\main\resources\authorityrefdocs.xsd
953      * @param ilistItem
954      * @param sourceField
955      * @return
956      */
957     private static AuthorityRefDocList.AuthorityRefDocItem cloneAuthRefDocItem(
958             AuthorityRefDocList.AuthorityRefDocItem ilistItem, String sourceField) {
959         AuthorityRefDocList.AuthorityRefDocItem newlistItem = new AuthorityRefDocList.AuthorityRefDocItem();
960         newlistItem.setDocId(ilistItem.getDocId());
961         newlistItem.setDocName(ilistItem.getDocName());
962         newlistItem.setDocNumber(ilistItem.getDocNumber());
963         newlistItem.setDocType(ilistItem.getDocType());
964         newlistItem.setUri(ilistItem.getUri());
965         newlistItem.setSourceField(sourceField);
966         newlistItem.setRefName(ilistItem.getRefName());
967         newlistItem.setUpdatedAt(ilistItem.getUpdatedAt());
968         newlistItem.setWorkflowState(ilistItem.getWorkflowState());
969         return newlistItem;
970     }
971
972     public static List<AuthRefInfo> findAuthRefPropertiesInDoc(
973             DocumentModel docModel,
974             List<AuthRefConfigInfo> authRefFieldInfoList,
975             String refNameToMatch,
976             List<AuthRefInfo> foundProps) {
977         return findAuthRefPropertiesInDoc(docModel, authRefFieldInfoList, 
978                                                                         refNameToMatch, false, foundProps);
979     }
980     
981     public static List<AuthRefInfo> findAuthRefPropertiesInDoc(
982             DocumentModel docModel,
983             List<AuthRefConfigInfo> authRefFieldInfoList,
984             String refNameToMatch,
985             boolean matchBaseOnly,
986             List<AuthRefInfo> authRefInfoList) {
987         // Assume that authRefFieldInfo is keyed by the field name (possibly mapped for UI)
988         // and the values are elPaths to the field, where intervening group structures in
989         // lists of complex structures are replaced with "*". Thus, valid paths include
990         // the following (note that the ServiceBindingUtils prepend schema names to configured values):
991         // "schemaname:fieldname"
992         // "schemaname:scalarlistname"
993         // "schemaname:complexfieldname/fieldname"
994         // "schemaname:complexlistname/*/fieldname"
995         // "schemaname:complexlistname/*/scalarlistname"
996         // "schemaname:complexlistname/*/complexfieldname/fieldname"
997         // "schemaname:complexlistname/*/complexlistname/*/fieldname"
998         // etc.
999         for (AuthRefConfigInfo arci : authRefFieldInfoList) {
1000             try {
1001                 // Get first property and work down as needed.
1002                 Property prop = docModel.getProperty(arci.pathEls[0]);
1003                 findAuthRefPropertiesInProperty(authRefInfoList, prop, arci, 0, refNameToMatch, matchBaseOnly);
1004             } catch (Exception e) {
1005                 logger.error("Problem fetching property: " + arci.pathEls[0]);
1006             }
1007         }
1008         return authRefInfoList;
1009     }
1010
1011     private static List<AuthRefInfo> findAuthRefPropertiesInProperty(
1012             List<AuthRefInfo> authRefInfoList,
1013             Property prop,
1014             AuthRefConfigInfo arci,
1015             int pathStartIndex, // Supports recursion and we work down the path
1016             String refNameToMatch,
1017             boolean matchBaseOnly ) {
1018         if (pathStartIndex >= arci.pathEls.length) {
1019             throw new ArrayIndexOutOfBoundsException("Index = " + pathStartIndex + " for path: "
1020                     + arci.pathEls.toString());
1021         }
1022         AuthRefInfo ari = null;
1023         if (prop == null) {
1024             return authRefInfoList;
1025         }
1026
1027         if (prop instanceof StringProperty) {    // scalar string
1028             addARIifMatches(refNameToMatch, matchBaseOnly, arci, prop, authRefInfoList); // REM - Side effect that foundProps gets changed/updated
1029         } else if (prop instanceof List) {
1030             List<Property> propList = (List<Property>) prop;
1031             // run through list. Must either be list of Strings, or Complex
1032             for (Property listItemProp : propList) {
1033                 if (listItemProp instanceof StringProperty) {
1034                     if (arci.pathEls.length - pathStartIndex != 1) {
1035                         logger.error("Configuration for authRefs does not match schema structure: "
1036                                 + arci.pathEls.toString());
1037                         break;
1038                     } else {
1039                         addARIifMatches(refNameToMatch, matchBaseOnly, arci, listItemProp, authRefInfoList);
1040                     }
1041                 } else if (listItemProp.isComplex()) {
1042                     // Just recurse to handle this. Note that since this is a list of complex, 
1043                     // which should look like listName/*/... we add 2 to the path start index 
1044                     findAuthRefPropertiesInProperty(authRefInfoList, listItemProp, arci,
1045                             pathStartIndex + 2, refNameToMatch, matchBaseOnly);
1046                 } else {
1047                     logger.error("Configuration for authRefs does not match schema structure: "
1048                             + arci.pathEls.toString());
1049                     break;
1050                 }
1051             }
1052         } else if (prop.isComplex()) {
1053             String localPropName = arci.pathEls[pathStartIndex];
1054             try {
1055                 Property localProp = prop.get(localPropName);
1056                 // Now just recurse, pushing down the path 1 step
1057                 findAuthRefPropertiesInProperty(authRefInfoList, localProp, arci,
1058                         pathStartIndex, refNameToMatch, matchBaseOnly);
1059             } catch (PropertyNotFoundException pnfe) {
1060                 logger.error("Could not find property: [" + localPropName + "] in path: "
1061                         + arci.getFullPath());
1062                 // Fall through - ari will be null and we will continue...
1063             }
1064         } else {
1065             logger.error("Configuration for authRefs does not match schema structure: "
1066                     + arci.pathEls.toString());
1067         }
1068
1069         if (ari != null) {
1070             authRefInfoList.add(ari); //FIXME: REM - This is dead code.  'ari' is never touched after being initalized to null.  Why?
1071         }
1072
1073         return authRefInfoList;
1074     }
1075
1076     private static void addARIifMatches(
1077             String refNameToMatch,
1078             boolean matchBaseOnly,
1079             AuthRefConfigInfo arci,
1080             Property prop,
1081             List<AuthRefInfo> authRefInfoList) {
1082         // Need to either match a passed refName 
1083         // OR have no refName to match but be non-empty
1084         try {
1085             String value = (String) prop.getValue();
1086             if (((refNameToMatch != null) && 
1087                                 (matchBaseOnly?
1088                                         (value!=null && value.startsWith(refNameToMatch))
1089                                         :refNameToMatch.equals(value)))
1090                     || ((refNameToMatch == null) && Tools.notBlank(value))) {
1091                 // Found a match
1092                 logger.debug("Found a match on property: " + prop.getPath() + " with value: [" + value + "]");
1093                 AuthRefInfo ari = new AuthRefInfo(arci, prop);
1094                 authRefInfoList.add(ari);
1095             }
1096         } catch (PropertyException pe) {
1097             logger.debug("PropertyException on: " + prop.getPath() + pe.getLocalizedMessage());
1098         }
1099     }
1100     
1101     public static String buildWhereForAuthByName(String authorityCommonSchemaName, String name) {
1102         return authorityCommonSchemaName
1103                 + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
1104                 + "='" + name + "'";
1105     }
1106
1107     /**
1108      * Build an NXQL query for finding an item by its short ID
1109      * 
1110      * @param authorityItemCommonSchemaName
1111      * @param shortId
1112      * @param parentcsid
1113      * @return
1114      */
1115     public static String buildWhereForAuthItemByName(String authorityItemCommonSchemaName, String shortId, String parentcsid) {
1116         String result = null;
1117         
1118         result = String.format("%s:%s='%s'", authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER, shortId);
1119         //
1120         // Technically, we don't need the parent CSID since the short ID is unique so it can be null
1121         //
1122         if (parentcsid != null) {
1123                 result = String.format("%s AND %s:%s='%s'",
1124                                 result, authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY, parentcsid);
1125         }
1126         
1127         return result;
1128     }    
1129
1130     /*
1131      * Identifies whether the refName was found in the supplied field. If passed
1132      * a new RefName, will set that into fields in which the old one was found.
1133      *
1134      * Only works for: * Scalar fields * Repeatable scalar fields (aka
1135      * multi-valued fields)
1136      *
1137      * Does not work for: * Structured fields (complexTypes) * Repeatable
1138      * structured fields (repeatable complexTypes) private static int
1139      * refNameFoundInField(String oldRefName, Property fieldValue, String
1140      * newRefName) { int nFound = 0; if (fieldValue instanceof List) {
1141      * List<Property> fieldValueList = (List) fieldValue; for (Property
1142      * listItemValue : fieldValueList) { try { if ((listItemValue instanceof
1143      * StringProperty) &&
1144      * oldRefName.equalsIgnoreCase((String)listItemValue.getValue())) {
1145      * nFound++; if(newRefName!=null) { fieldValue.setValue(newRefName); } else
1146      * { // We cannot quit after the first, if we are replacing values. // If we
1147      * are just looking (not replacing), finding one is enough. break; } } }
1148      * catch( PropertyException pe ) {} } } else { try { if ((fieldValue
1149      * instanceof StringProperty) &&
1150      * oldRefName.equalsIgnoreCase((String)fieldValue.getValue())) { nFound++;
1151      * if(newRefName!=null) { fieldValue.setValue(newRefName); } } } catch(
1152      * PropertyException pe ) {} } return nFound; }
1153      */
1154 }