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
6 * http://www.collectionspace.org
\r
7 * http://wiki.collectionspace.org
\r
9 * Copyright 2009 University of California at Berkeley
\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
14 * You may obtain a copy of the ECL 2.0 License at
\r
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
\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
24 package org.collectionspace.services.common.vocabulary;
\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
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
44 import org.collectionspace.services.client.PoxPayloadIn;
\r
45 import org.collectionspace.services.client.PoxPayloadOut;
\r
46 import org.collectionspace.services.common.ServiceMain;
\r
47 import org.collectionspace.services.common.context.ServiceContext;
\r
48 import org.collectionspace.services.common.context.AbstractServiceContextImpl;
\r
49 import org.collectionspace.services.common.api.RefNameUtils;
\r
50 import org.collectionspace.services.common.api.Tools;
\r
51 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
\r
52 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
\r
53 import org.collectionspace.services.common.authorityref.AuthorityRefList;
\r
54 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
\r
55 import org.collectionspace.services.common.context.ServiceBindingUtils;
\r
56 import org.collectionspace.services.common.document.DocumentException;
\r
57 import org.collectionspace.services.common.document.DocumentFilter;
\r
58 import org.collectionspace.services.common.document.DocumentNotFoundException;
\r
59 import org.collectionspace.services.common.document.DocumentUtils;
\r
60 import org.collectionspace.services.common.document.DocumentWrapper;
\r
61 import org.collectionspace.services.common.query.QueryManager;
\r
62 import org.collectionspace.services.common.repository.RepositoryClient;
\r
63 import org.collectionspace.services.nuxeo.client.java.DocHandlerBase;
\r
64 import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;
\r
65 import org.collectionspace.services.common.security.SecurityUtils;
\r
66 import org.collectionspace.services.config.service.ServiceBindingType;
\r
67 import org.collectionspace.services.jaxb.AbstractCommonList;
\r
68 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
\r
70 import com.sun.xml.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;
\r
73 * RefNameServiceUtils is a collection of services utilities related to refName usage.
\r
75 * $LastChangedRevision: $
\r
76 * $LastChangedDate: $
\r
78 public class RefNameServiceUtils {
\r
80 public static class AuthRefConfigInfo {
\r
81 public String getQualifiedDisplayName() {
\r
82 return(Tools.isBlank(schema))?
\r
83 displayName:DocumentUtils.appendSchemaName(schema, displayName);
\r
85 public String getDisplayName() {
\r
88 public void setDisplayName(String displayName) {
\r
89 this.displayName = displayName;
\r
93 public String getSchema() {
\r
96 public void setSchema(String schema) {
\r
97 this.schema = schema;
\r
99 public String getFullPath() {
\r
102 public void setFullPath(String fullPath) {
\r
103 this.fullPath = fullPath;
\r
106 protected String[] pathEls;
\r
107 public AuthRefConfigInfo(AuthRefConfigInfo arci) {
\r
108 this.displayName = arci.displayName;
\r
109 this.schema = arci.schema;
\r
110 this.fullPath = arci.fullPath;
\r
111 this.pathEls = arci.pathEls;
\r
112 // Skip the pathElse check, since we are creatign from another (presumably valid) arci.
\r
115 public AuthRefConfigInfo(String displayName, String schema, String fullPath, String[] pathEls) {
\r
116 this.displayName = displayName;
\r
117 this.schema = schema;
\r
118 this.fullPath = fullPath;
\r
119 this.pathEls = pathEls;
\r
123 // Split a config value string like "intakes_common:collector", or
\r
124 // "collectionobjects_common:contentPeoples|contentPeople"
\r
125 // "collectionobjects_common:assocEventGroupList/*/assocEventPlace"
\r
126 // If has a pipe ('|') second part is a displayLabel, and first is path
\r
127 // Otherwise, entry is a path, and can use the last pathElement as displayName
\r
128 // Should be schema qualified.
\r
129 public AuthRefConfigInfo(String configString) {
\r
130 String[] pair = configString.split("\\|", 2);
\r
132 String displayName, fullPath;
\r
133 if(pair.length == 1) {
\r
134 // no label specifier, so we'll defer getting label
\r
135 fullPath = pair[0];
\r
136 pathEls = pair[0].split("/");
\r
137 displayName = pathEls[pathEls.length-1];
\r
139 fullPath = pair[0];
\r
140 pathEls = pair[0].split("/");
\r
141 displayName = pair[1];
\r
143 String[] schemaSplit = pathEls[0].split(":",2);
\r
145 if(schemaSplit.length==1) { // schema not specified
\r
148 schema = schemaSplit[0];
\r
149 if(pair.length == 1 && pathEls.length == 1) { // simplest case of field in top level schema, no labelll
\r
150 displayName = schemaSplit[1]; // Have to fix up displayName to have no schema
\r
153 this.displayName = displayName;
\r
154 this.schema = schema;
\r
155 this.fullPath = fullPath;
\r
156 this.pathEls = pathEls;
\r
160 protected void checkPathEls() {
\r
161 int len = pathEls.length;
\r
163 throw new InternalError("Bad values in authRef info - caller screwed up:"+fullPath);
\r
164 // Handle case of them putting a leading slash on the path
\r
165 if(len>1 && pathEls[0].endsWith(":")) {
\r
167 String[] newArray = new String[len];
\r
168 newArray[0] = pathEls[0]+pathEls[1];
\r
170 System.arraycopy(pathEls, 2, newArray, 1, len-1);
\r
172 pathEls = newArray;
\r
177 public static class AuthRefInfo extends AuthRefConfigInfo {
\r
178 public Property getProperty() {
\r
181 public void setProperty(Property property) {
\r
182 this.property = property;
\r
185 public AuthRefInfo(String displayName, String schema, String fullPath, String[] pathEls, Property prop) {
\r
186 super(displayName, schema, fullPath, pathEls);
\r
187 this.property = prop;
\r
189 public AuthRefInfo(AuthRefConfigInfo arci, Property prop) {
\r
191 this.property = prop;
\r
195 private static final Logger logger = LoggerFactory.getLogger(RefNameServiceUtils.class);
\r
197 private static ArrayList<String> refNameServiceTypes = null;
\r
199 public static List<AuthRefConfigInfo> getConfiguredAuthorityRefs(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
\r
200 List<String> authRefFields =
\r
201 ((AbstractServiceContextImpl) ctx).getAllPartsPropertyValues(
\r
202 ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
\r
203 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>(authRefFields.size());
\r
204 for(String spec:authRefFields) {
\r
205 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
\r
206 authRefsInfo.add(arci);
\r
208 return authRefsInfo;
\r
211 public static AuthorityRefDocList getAuthorityRefDocs(
\r
212 RepositoryInstance repoSession,
\r
213 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
214 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
215 List<String> serviceTypes,
\r
217 String refPropName,
\r
218 DocumentFilter filter, boolean computeTotal)
\r
219 throws DocumentException, DocumentNotFoundException {
\r
220 AuthorityRefDocList wrapperList = new AuthorityRefDocList();
\r
221 AbstractCommonList commonList = (AbstractCommonList) wrapperList;
\r
222 int pageNum = filter.getStartPage();
\r
223 int pageSize = filter.getPageSize();
\r
224 commonList.setPageNum(pageNum);
\r
225 commonList.setPageSize(pageSize);
\r
226 List<AuthorityRefDocList.AuthorityRefDocItem> list =
\r
227 wrapperList.getAuthorityRefDocItem();
\r
229 Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
\r
230 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
\r
232 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl)repoClient;
\r
234 DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,
\r
235 serviceTypes, refName, refPropName, queriedServiceBindings, authRefFieldsByService,
\r
236 filter.getWhereClause(), pageSize, pageNum, computeTotal);
\r
238 if (docList == null) { // found no authRef fields - nothing to process
\r
239 return wrapperList;
\r
241 // Set num of items in list. this is useful to our testing framework.
\r
242 commonList.setItemsInPage(docList.size());
\r
243 // set the total result size
\r
244 commonList.setTotalItems(docList.totalSize());
\r
245 // set the fieldsReturned list. Even though this is a fixed schema, app layer treats
\r
246 // this like other abstract common lists
\r
248 <xs:element name="docType" type="xs:string" minOccurs="1" />
\r
249 <xs:element name="docId" type="xs:string" minOccurs="1" />
\r
250 <xs:element name="docNumber" type="xs:string" minOccurs="0" />
\r
251 <xs:element name="docName" type="xs:string" minOccurs="0" />
\r
252 <xs:element name="sourceField" type="xs:string" minOccurs="1" />
\r
253 <xs:element name="uri" type="xs:anyURI" minOccurs="1" />
\r
254 <xs:element name="updatedAt" type="xs:string" minOccurs="1" />
\r
255 <xs:element name="workflowState" type="xs:string" minOccurs="1" />
\r
257 String fieldList = "docType|docId|docNumber|docName|sourceField|uri|updatedAt|workflowState";
\r
258 commonList.setFieldsReturned(fieldList);
\r
260 int nRefsFound = processRefObjsDocList(docList, refName, queriedServiceBindings, authRefFieldsByService,
\r
262 if(logger.isDebugEnabled() && (nRefsFound < docList.size())) {
\r
263 logger.debug("Internal curiosity: got fewer matches of refs than # docs matched...");
\r
265 } catch (Exception e) {
\r
266 logger.error("Could not retrieve the Nuxeo repository", e);
\r
267 wrapperList = null;
\r
270 return wrapperList;
\r
273 private static ArrayList<String> getRefNameServiceTypes() {
\r
274 if(refNameServiceTypes == null) {
\r
275 refNameServiceTypes = new ArrayList<String>();
\r
276 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_AUTHORITY);
\r
277 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_OBJECT);
\r
278 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_PROCEDURE);
\r
280 return refNameServiceTypes;
\r
283 // Seems like a good value - no real data to set this well.
\r
284 private static final int N_OBJS_TO_UPDATE_PER_LOOP = 100;
\r
286 public static int updateAuthorityRefDocs(
\r
287 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
288 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
289 RepositoryInstance repoSession,
\r
292 String refPropName ) throws Exception {
\r
293 Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
\r
294 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
\r
295 int nRefsFound = 0;
\r
296 if(!(repoClient instanceof RepositoryJavaClientImpl)) {
\r
297 throw new InternalError("updateAuthorityRefDocs() called with unknown repoClient type!");
\r
300 final int pageSize = N_OBJS_TO_UPDATE_PER_LOOP;
\r
301 int pageNumProcessed = 1;
\r
302 while(true) { // Keep looping until we find all the refs.
\r
303 logger.debug("updateAuthorityRefDocs working on page: "+pageNumProcessed);
\r
304 // Note that we always ask the Repo for the first page, since each page we process
\r
305 // should not be found in successive searches. Slightly inefficient, but more
\r
306 // reliable (stateless).
\r
307 DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,
\r
308 getRefNameServiceTypes(), oldRefName, refPropName,
\r
309 queriedServiceBindings, authRefFieldsByService, null, pageSize, 0, false);
\r
311 if((docList == null) // found no authRef fields - nothing to do
\r
312 || (docList.size() == 0)) { // No more to handle
\r
313 logger.debug("updateAuthorityRefDocs no more results");
\r
316 logger.debug("updateAuthorityRefDocs curr page result list size: "+docList.size());
\r
317 int nRefsFoundThisPage = processRefObjsDocList(docList, oldRefName, queriedServiceBindings, authRefFieldsByService,
\r
319 if(nRefsFoundThisPage>0) {
\r
320 ((RepositoryJavaClientImpl)repoClient).saveDocListWithoutHandlerProcessing(ctx, repoSession, docList, true);
\r
321 nRefsFound += nRefsFoundThisPage;
\r
323 pageNumProcessed++;
\r
324 if(docList.size()<pageSize) {
\r
325 logger.debug("updateAuthorityRefDocs: assuming no more results, as nResults < pageSize");
\r
329 } catch (Exception e) {
\r
330 logger.error("Internal error updating the AuthorityRefDocs: " + e.getLocalizedMessage());
\r
331 logger.debug(Tools.errorToString(e, true));
\r
334 logger.debug("updateAuthorityRefDocs replaced a total of: "+nRefsFound);
\r
338 private static DocumentModelList findAuthorityRefDocs(
\r
339 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
340 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
341 RepositoryInstance repoSession,
\r
342 List<String> serviceTypes,
\r
344 String refPropName,
\r
345 Map<String, ServiceBindingType> queriedServiceBindings,
\r
346 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
\r
347 String whereClauseAdditions,
\r
348 int pageSize, int pageNum, boolean computeTotal) throws DocumentException, DocumentNotFoundException {
\r
350 // Get the service bindings for this tenant
\r
351 TenantBindingConfigReaderImpl tReader =
\r
352 ServiceMain.getInstance().getTenantBindingConfigReader();
\r
353 // We need to get all the procedures, authorities, and objects.
\r
354 List<ServiceBindingType> servicebindings = tReader.getServiceBindingsByType(ctx.getTenantId(), serviceTypes);
\r
355 if (servicebindings == null || servicebindings.isEmpty()) {
\r
356 logger.error("RefNameServiceUtils.getAuthorityRefDocs: No services bindings found, cannot proceed!");
\r
359 // Filter the list for current user rights
\r
360 servicebindings = SecurityUtils.getReadableServiceBindingsForCurrentUser(servicebindings);
\r
362 ArrayList<String> docTypes = new ArrayList<String>();
\r
364 String query = computeWhereClauseForAuthorityRefDocs(refName, refPropName, docTypes, servicebindings,
\r
365 queriedServiceBindings, authRefFieldsByService );
\r
366 if (query == null) { // found no authRef fields - nothing to query
\r
369 // Additional qualifications, like workflow state
\r
370 if(whereClauseAdditions!=null) {
\r
371 query += " AND " + whereClauseAdditions;
\r
373 // Now we have to issue the search
\r
374 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl)repoClient;
\r
375 DocumentWrapper<DocumentModelList> docListWrapper = nuxeoRepoClient.findDocs(ctx, repoSession,
\r
376 docTypes, query, pageSize, pageNum, computeTotal);
\r
377 // Now we gather the info for each document into the list and return
\r
378 DocumentModelList docList = docListWrapper.getWrappedObject();
\r
382 private static final boolean READY_FOR_COMPLEX_QUERY = true;
\r
384 private static String computeWhereClauseForAuthorityRefDocs(
\r
386 String refPropName,
\r
387 ArrayList<String> docTypes,
\r
388 List<ServiceBindingType> servicebindings,
\r
389 Map<String, ServiceBindingType> queriedServiceBindings,
\r
390 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService ) {
\r
392 boolean fFirst = true;
\r
393 List<String> authRefFieldPaths;
\r
394 for (ServiceBindingType sb : servicebindings) {
\r
395 // Gets the property names for each part, qualified with the part label (which
\r
396 // is also the table name, the way that the repository works).
\r
397 authRefFieldPaths =
\r
398 ServiceBindingUtils.getAllPartsPropertyValues(sb,
\r
399 refPropName, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
\r
400 if (authRefFieldPaths.isEmpty()) {
\r
403 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>();
\r
404 for(String spec:authRefFieldPaths) {
\r
405 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
\r
406 authRefsInfo.add(arci);
\r
409 String docType = sb.getObject().getName();
\r
410 queriedServiceBindings.put(docType, sb);
\r
411 authRefFieldsByService.put(docType, authRefsInfo);
\r
412 docTypes.add(docType);
\r
415 if (fFirst) { // found no authRef fields - nothing to query
\r
418 // We used to build a complete matches query, but that was too complex.
\r
419 // Just build a keyword query based upon some key pieces - the urn syntax elements and the shortID
\r
420 // Note that this will also match the Item itself, but that will get filtered out when
\r
421 // we compute actual matches.
\r
422 AuthorityTermInfo authTermInfo = RefNameUtils.parseAuthorityTermInfo(refName);
\r
424 String keywords = RefNameUtils.URN_PREFIX
\r
425 + " AND " + (authTermInfo.inAuthority.name!=null?
\r
426 authTermInfo.inAuthority.name:authTermInfo.inAuthority.csid)
\r
427 + " AND " + (authTermInfo.name!=null?
\r
428 authTermInfo.name:authTermInfo.csid);
\r
430 String whereClauseStr = QueryManager.createWhereClauseFromKeywords(keywords);
\r
432 if (logger.isTraceEnabled()) {
\r
433 logger.trace("The 'where' clause to find refObjs is: ", whereClauseStr);
\r
436 return whereClauseStr;
\r
440 * Runs through the list of found docs, processing them.
\r
441 * If list is non-null, then processing means gather the info for items.
\r
442 * If list is null, and newRefName is non-null, then processing means replacing and updating.
\r
443 * If processing/updating, this must be called in teh context of an open session, and caller
\r
444 * must release Session after calling this.
\r
447 private static int processRefObjsDocList(
\r
448 DocumentModelList docList,
\r
450 Map<String, ServiceBindingType> queriedServiceBindings,
\r
451 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
\r
452 List<AuthorityRefDocList.AuthorityRefDocItem> list,
\r
453 String newAuthorityRefName) {
\r
454 Iterator<DocumentModel> iter = docList.iterator();
\r
455 int nRefsFoundTotal = 0;
\r
456 while (iter.hasNext()) {
\r
457 DocumentModel docModel = iter.next();
\r
458 AuthorityRefDocList.AuthorityRefDocItem ilistItem;
\r
460 String docType = docModel.getDocumentType().getName();
\r
461 docType = ServiceBindingUtils.getUnqualifiedTenantDocType(docType);
\r
462 ServiceBindingType sb = queriedServiceBindings.get(docType);
\r
464 throw new RuntimeException(
\r
465 "getAuthorityRefDocs: No Service Binding for docType: " + docType);
\r
467 String serviceContextPath = "/" + sb.getName().toLowerCase() + "/";
\r
469 if(list == null) { // no list - should be update refName case.
\r
470 if(newAuthorityRefName==null) {
\r
471 throw new InternalError("processRefObjsDocList() called with neither an itemList nor a new RefName!");
\r
474 } else { // Have a list - refObjs case
\r
475 if(newAuthorityRefName!=null) {
\r
476 throw new InternalError("processRefObjsDocList() called with both an itemList and a new RefName!");
\r
478 ilistItem = new AuthorityRefDocList.AuthorityRefDocItem();
\r
479 String csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
\r
480 ilistItem.setDocId(csid);
\r
481 ilistItem.setUri(serviceContextPath + csid);
\r
483 ilistItem.setWorkflowState(docModel.getCurrentLifeCycleState());
\r
484 ilistItem.setUpdatedAt(DocHandlerBase.getUpdatedAtAsString(docModel));
\r
485 } catch(Exception e) {
\r
486 logger.error("Error getting core values for doc ["+csid+"]: "+e.getLocalizedMessage());
\r
488 // The id and URI are the same on all doctypes
\r
489 ilistItem.setDocType(docType);
\r
490 ilistItem.setDocNumber(
\r
491 ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NUMBER_PROP, docModel));
\r
492 ilistItem.setDocName(
\r
493 ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NAME_PROP, docModel));
\r
495 // Now, we have to loop over the authRefFieldsByService to figure
\r
496 // out which field(s) matched this.
\r
497 List<AuthRefConfigInfo> matchingAuthRefFields = authRefFieldsByService.get(docType);
\r
498 if (matchingAuthRefFields == null || matchingAuthRefFields.isEmpty()) {
\r
499 throw new RuntimeException(
\r
500 "getAuthorityRefDocs: internal logic error: can't fetch authRefFields for DocType.");
\r
502 //String authRefAncestorField = "";
\r
503 //String authRefDescendantField = "";
\r
504 //String sourceField = "";
\r
505 int nRefsFoundInDoc = 0;
\r
507 ArrayList<RefNameServiceUtils.AuthRefInfo> foundProps
\r
508 = new ArrayList<RefNameServiceUtils.AuthRefInfo>();
\r
510 findAuthRefPropertiesInDoc(docModel, matchingAuthRefFields, refName, foundProps);
\r
511 for(RefNameServiceUtils.AuthRefInfo ari:foundProps) {
\r
512 if(ilistItem != null) {
\r
513 if(nRefsFoundInDoc == 0) { // First one?
\r
514 ilistItem.setSourceField(ari.getQualifiedDisplayName());
\r
515 } else { // duplicates from one object
\r
516 ilistItem = cloneAuthRefDocItem(ilistItem, ari.getQualifiedDisplayName());
\r
518 list.add(ilistItem);
\r
519 } else { // update refName case
\r
520 Property propToUpdate = ari.getProperty();
\r
521 propToUpdate.setValue(newAuthorityRefName);
\r
525 } catch (ClientException ce) {
\r
526 throw new RuntimeException(
\r
527 "getAuthorityRefDocs: Problem fetching values from repo: " + ce.getLocalizedMessage());
\r
529 if (nRefsFoundInDoc == 0) {
\r
531 "getAuthorityRefDocs: Result: "
\r
532 + docType + " [" + NuxeoUtils.getCsid(docModel)
\r
533 + "] does not reference ["
\r
536 nRefsFoundTotal += nRefsFoundInDoc;
\r
538 return nRefsFoundTotal;
\r
541 private static AuthorityRefDocList.AuthorityRefDocItem cloneAuthRefDocItem(
\r
542 AuthorityRefDocList.AuthorityRefDocItem ilistItem, String sourceField) {
\r
543 AuthorityRefDocList.AuthorityRefDocItem newlistItem = new AuthorityRefDocList.AuthorityRefDocItem();
\r
544 newlistItem.setDocId(ilistItem.getDocId());
\r
545 newlistItem.setDocName(ilistItem.getDocName());
\r
546 newlistItem.setDocNumber(ilistItem.getDocNumber());
\r
547 newlistItem.setDocType(ilistItem.getDocType());
\r
548 newlistItem.setUri(ilistItem.getUri());
\r
549 newlistItem.setSourceField(sourceField);
\r
550 return newlistItem;
\r
553 public static List<AuthRefInfo> findAuthRefPropertiesInDoc(
\r
554 DocumentModel docModel,
\r
555 List<AuthRefConfigInfo> authRefFieldInfo,
\r
556 String refNameToMatch,
\r
557 List<AuthRefInfo> foundProps
\r
559 // Assume that authRefFieldInfo is keyed by the field name (possibly mapped for UI)
\r
560 // and the values are elPaths to the field, where intervening group structures in
\r
561 // lists of complex structures are replaced with "*". Thus, valid paths include
\r
562 // the following (note that the ServiceBindingUtils prepend schema names to configured values):
\r
563 // "schemaname:fieldname"
\r
564 // "schemaname:scalarlistname"
\r
565 // "schemaname:complexfieldname/fieldname"
\r
566 // "schemaname:complexlistname/*/fieldname"
\r
567 // "schemaname:complexlistname/*/scalarlistname"
\r
568 // "schemaname:complexlistname/*/complexfieldname/fieldname"
\r
569 // "schemaname:complexlistname/*/complexlistname/*/fieldname"
\r
571 for (AuthRefConfigInfo arci : authRefFieldInfo) {
\r
573 // Get first property and work down as needed.
\r
574 Property prop = docModel.getProperty(arci.pathEls[0]);
\r
575 findAuthRefPropertiesInProperty(foundProps, prop, arci, 0, refNameToMatch);
\r
576 } catch(Exception e) {
\r
577 logger.error("Problem fetching property: "+arci.pathEls[0]);
\r
583 public static List<AuthRefInfo> findAuthRefPropertiesInProperty(
\r
584 List<AuthRefInfo> foundProps,
\r
586 AuthRefConfigInfo arci,
\r
587 int pathStartIndex, // Supports recursion and we work down the path
\r
588 String refNameToMatch
\r
590 if (pathStartIndex >= arci.pathEls.length) {
\r
591 throw new ArrayIndexOutOfBoundsException("Index = "+pathStartIndex+" for path: "
\r
592 +arci.pathEls.toString());
\r
594 AuthRefInfo ari = null;
\r
595 if (prop == null) {
\r
599 if (prop instanceof StringProperty) { // scalar string
\r
600 addARIifMatches(refNameToMatch, arci, prop, foundProps);
\r
601 } else if(prop instanceof List) {
\r
602 List<Property> propList = (List<Property>)prop;
\r
603 // run through list. Must either be list of Strings, or Complex
\r
604 for (Property listItemProp : propList) {
\r
605 if(listItemProp instanceof StringProperty) {
\r
606 if(arci.pathEls.length-pathStartIndex != 1) {
\r
607 logger.error("Configuration for authRefs does not match schema structure: "
\r
608 +arci.pathEls.toString());
\r
611 addARIifMatches(refNameToMatch, arci, listItemProp, foundProps);
\r
613 } else if(listItemProp.isComplex()) {
\r
614 // Just recurse to handle this. Note that since this is a list of complex,
\r
615 // which should look like listName/*/... we add 2 to the path start index
\r
616 findAuthRefPropertiesInProperty(foundProps, listItemProp, arci,
\r
617 pathStartIndex+2, refNameToMatch);
\r
619 logger.error("Configuration for authRefs does not match schema structure: "
\r
620 +arci.pathEls.toString());
\r
624 } else if(prop.isComplex()) {
\r
625 String localPropName = arci.pathEls[pathStartIndex];
\r
627 Property localProp = prop.get(localPropName);
\r
628 // Now just recurse, pushing down the path 1 step
\r
629 findAuthRefPropertiesInProperty(foundProps, localProp, arci,
\r
630 pathStartIndex, refNameToMatch);
\r
631 } catch(PropertyNotFoundException pnfe) {
\r
632 logger.error("Could not find property: ["+localPropName+"] in path: "+
\r
633 arci.getFullPath());
\r
634 // Fall through - ari will be null and we will continue...
\r
637 logger.error("Configuration for authRefs does not match schema structure: "
\r
638 +arci.pathEls.toString());
\r
642 foundProps.add(ari); //FIXME: REM - This is dead code. 'ari' is never touched after being initalized to null. Why?
\r
648 private static void addARIifMatches(
\r
649 String refNameToMatch,
\r
650 AuthRefConfigInfo arci,
\r
652 List<AuthRefInfo> foundProps) {
\r
653 // Need to either match a passed refName
\r
654 // OR have no refName to match but be non-empty
\r
656 String value = (String)prop.getValue();
\r
657 if(((refNameToMatch!=null) && refNameToMatch.equals(value))
\r
658 || ((refNameToMatch==null) && Tools.notBlank(value))) {
\r
660 logger.debug("Found a match on property: "+prop.getPath()+" with value: ["+value+"]");
\r
661 AuthRefInfo ari = new AuthRefInfo(arci, prop);
\r
662 foundProps.add(ari);
\r
664 } catch(PropertyException pe) {
\r
665 logger.debug("PropertyException on: "+prop.getPath()+pe.getLocalizedMessage());
\r
670 * Identifies whether the refName was found in the supplied field.
\r
671 * If passed a new RefName, will set that into fields in which the old one was found.
\r
675 * * Repeatable scalar fields (aka multi-valued fields)
\r
677 * Does not work for:
\r
678 * * Structured fields (complexTypes)
\r
679 * * Repeatable structured fields (repeatable complexTypes)
\r
680 private static int refNameFoundInField(String oldRefName, Property fieldValue, String newRefName) {
\r
682 if (fieldValue instanceof List) {
\r
683 List<Property> fieldValueList = (List) fieldValue;
\r
684 for (Property listItemValue : fieldValueList) {
\r
686 if ((listItemValue instanceof StringProperty)
\r
687 && oldRefName.equalsIgnoreCase((String)listItemValue.getValue())) {
\r
689 if(newRefName!=null) {
\r
690 fieldValue.setValue(newRefName);
\r
692 // We cannot quit after the first, if we are replacing values.
\r
693 // If we are just looking (not replacing), finding one is enough.
\r
697 } catch( PropertyException pe ) {}
\r
701 if ((fieldValue instanceof StringProperty)
\r
702 && oldRefName.equalsIgnoreCase((String)fieldValue.getValue())) {
\r
704 if(newRefName!=null) {
\r
705 fieldValue.setValue(newRefName);
\r
708 } catch( PropertyException pe ) {}
\r