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.DocumentNotFoundException;
\r
58 import org.collectionspace.services.common.document.DocumentUtils;
\r
59 import org.collectionspace.services.common.document.DocumentWrapper;
\r
60 import org.collectionspace.services.common.query.QueryManager;
\r
61 import org.collectionspace.services.common.repository.RepositoryClient;
\r
62 import org.collectionspace.services.nuxeo.client.java.DocHandlerBase;
\r
63 import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;
\r
64 import org.collectionspace.services.common.security.SecurityUtils;
\r
65 import org.collectionspace.services.config.service.ServiceBindingType;
\r
66 import org.collectionspace.services.jaxb.AbstractCommonList;
\r
67 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
\r
69 import com.sun.xml.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;
\r
72 * RefNameServiceUtils is a collection of services utilities related to refName usage.
\r
74 * $LastChangedRevision: $
\r
75 * $LastChangedDate: $
\r
77 public class RefNameServiceUtils {
\r
79 public static class AuthRefConfigInfo {
\r
80 public String getQualifiedDisplayName() {
\r
81 return(Tools.isBlank(schema))?
\r
82 displayName:DocumentUtils.appendSchemaName(schema, displayName);
\r
84 public String getDisplayName() {
\r
87 public void setDisplayName(String displayName) {
\r
88 this.displayName = displayName;
\r
92 public String getSchema() {
\r
95 public void setSchema(String schema) {
\r
96 this.schema = schema;
\r
98 public String getFullPath() {
\r
101 public void setFullPath(String fullPath) {
\r
102 this.fullPath = fullPath;
\r
105 protected String[] pathEls;
\r
106 public AuthRefConfigInfo(AuthRefConfigInfo arci) {
\r
107 this.displayName = arci.displayName;
\r
108 this.schema = arci.schema;
\r
109 this.fullPath = arci.fullPath;
\r
110 this.pathEls = arci.pathEls;
\r
111 // Skip the pathElse check, since we are creatign from another (presumably valid) arci.
\r
114 public AuthRefConfigInfo(String displayName, String schema, String fullPath, String[] pathEls) {
\r
115 this.displayName = displayName;
\r
116 this.schema = schema;
\r
117 this.fullPath = fullPath;
\r
118 this.pathEls = pathEls;
\r
122 // Split a config value string like "intakes_common:collector", or
\r
123 // "collectionobjects_common:contentPeoples|contentPeople"
\r
124 // "collectionobjects_common:assocEventGroupList/*/assocEventPlace"
\r
125 // If has a pipe ('|') second part is a displayLabel, and first is path
\r
126 // Otherwise, entry is a path, and can use the last pathElement as displayName
\r
127 // Should be schema qualified.
\r
128 public AuthRefConfigInfo(String configString) {
\r
129 String[] pair = configString.split("\\|", 2);
\r
131 String displayName, fullPath;
\r
132 if(pair.length == 1) {
\r
133 // no label specifier, so we'll defer getting label
\r
134 fullPath = pair[0];
\r
135 pathEls = pair[0].split("/");
\r
136 displayName = pathEls[pathEls.length-1];
\r
138 fullPath = pair[0];
\r
139 pathEls = pair[0].split("/");
\r
140 displayName = pair[1];
\r
142 String[] schemaSplit = pathEls[0].split(":",2);
\r
144 if(schemaSplit.length==1) { // schema not specified
\r
147 schema = schemaSplit[0];
\r
148 if(pair.length == 1 && pathEls.length == 1) { // simplest case of field in top level schema, no labelll
\r
149 displayName = schemaSplit[1]; // Have to fix up displayName to have no schema
\r
152 this.displayName = displayName;
\r
153 this.schema = schema;
\r
154 this.fullPath = fullPath;
\r
155 this.pathEls = pathEls;
\r
159 protected void checkPathEls() {
\r
160 int len = pathEls.length;
\r
162 throw new InternalError("Bad values in authRef info - caller screwed up:"+fullPath);
\r
163 // Handle case of them putting a leading slash on the path
\r
164 if(len>1 && pathEls[0].endsWith(":")) {
\r
166 String[] newArray = new String[len];
\r
167 newArray[0] = pathEls[0]+pathEls[1];
\r
169 System.arraycopy(pathEls, 2, newArray, 1, len-1);
\r
171 pathEls = newArray;
\r
176 public static class AuthRefInfo extends AuthRefConfigInfo {
\r
177 public Property getProperty() {
\r
180 public void setProperty(Property property) {
\r
181 this.property = property;
\r
184 public AuthRefInfo(String displayName, String schema, String fullPath, String[] pathEls, Property prop) {
\r
185 super(displayName, schema, fullPath, pathEls);
\r
186 this.property = prop;
\r
188 public AuthRefInfo(AuthRefConfigInfo arci, Property prop) {
\r
190 this.property = prop;
\r
194 private static final Logger logger = LoggerFactory.getLogger(RefNameServiceUtils.class);
\r
196 private static ArrayList<String> refNameServiceTypes = null;
\r
198 public static List<AuthRefConfigInfo> getConfiguredAuthorityRefs(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
\r
199 List<String> authRefFields =
\r
200 ((AbstractServiceContextImpl) ctx).getAllPartsPropertyValues(
\r
201 ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
\r
202 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>(authRefFields.size());
\r
203 for(String spec:authRefFields) {
\r
204 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
\r
205 authRefsInfo.add(arci);
\r
207 return authRefsInfo;
\r
210 public static AuthorityRefDocList getAuthorityRefDocs(
\r
211 RepositoryInstance repoSession,
\r
212 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
213 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
214 List<String> serviceTypes,
\r
216 String refPropName,
\r
217 int pageSize, int pageNum, boolean computeTotal)
\r
218 throws DocumentException, DocumentNotFoundException {
\r
219 AuthorityRefDocList wrapperList = new AuthorityRefDocList();
\r
220 AbstractCommonList commonList = (AbstractCommonList) wrapperList;
\r
221 commonList.setPageNum(pageNum);
\r
222 commonList.setPageSize(pageSize);
\r
223 List<AuthorityRefDocList.AuthorityRefDocItem> list =
\r
224 wrapperList.getAuthorityRefDocItem();
\r
226 Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
\r
227 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
\r
229 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl)repoClient;
\r
231 DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,
\r
232 serviceTypes, refName, refPropName, queriedServiceBindings, authRefFieldsByService, pageSize, pageNum, computeTotal);
\r
234 if (docList == null) { // found no authRef fields - nothing to process
\r
235 return wrapperList;
\r
237 // Set num of items in list. this is useful to our testing framework.
\r
238 commonList.setItemsInPage(docList.size());
\r
239 // set the total result size
\r
240 commonList.setTotalItems(docList.totalSize());
\r
241 // set the fieldsReturned list. Even though this is a fixed schema, app layer treats
\r
242 // this like other abstract common lists
\r
244 <xs:element name="docType" type="xs:string" minOccurs="1" />
\r
245 <xs:element name="docId" type="xs:string" minOccurs="1" />
\r
246 <xs:element name="docNumber" type="xs:string" minOccurs="0" />
\r
247 <xs:element name="docName" type="xs:string" minOccurs="0" />
\r
248 <xs:element name="sourceField" type="xs:string" minOccurs="1" />
\r
249 <xs:element name="uri" type="xs:anyURI" minOccurs="1" />
\r
250 <xs:element name="updatedAt" type="xs:string" minOccurs="1" />
\r
251 <xs:element name="workflowState" type="xs:string" minOccurs="1" />
\r
253 String fieldList = "docType|docId|docNumber|docName|sourceField|uri|updatedAt|workflowState";
\r
254 commonList.setFieldsReturned(fieldList);
\r
256 int nRefsFound = processRefObjsDocList(docList, refName, queriedServiceBindings, authRefFieldsByService,
\r
258 if(logger.isDebugEnabled() && (nRefsFound < docList.size())) {
\r
259 logger.debug("Internal curiosity: got fewer matches of refs than # docs matched...");
\r
261 } catch (Exception e) {
\r
262 logger.error("Could not retrieve the Nuxeo repository", e);
\r
263 wrapperList = null;
\r
266 return wrapperList;
\r
269 private static ArrayList<String> getRefNameServiceTypes() {
\r
270 if(refNameServiceTypes == null) {
\r
271 refNameServiceTypes = new ArrayList<String>();
\r
272 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_AUTHORITY);
\r
273 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_OBJECT);
\r
274 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_PROCEDURE);
\r
276 return refNameServiceTypes;
\r
279 // Seems like a good value - no real data to set this well.
\r
280 private static final int N_OBJS_TO_UPDATE_PER_LOOP = 100;
\r
282 public static int updateAuthorityRefDocs(
\r
283 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
284 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
285 RepositoryInstance repoSession,
\r
288 String refPropName ) throws Exception {
\r
289 Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
\r
290 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
\r
291 int nRefsFound = 0;
\r
292 if(!(repoClient instanceof RepositoryJavaClientImpl)) {
\r
293 throw new InternalError("updateAuthorityRefDocs() called with unknown repoClient type!");
\r
296 final int pageSize = N_OBJS_TO_UPDATE_PER_LOOP;
\r
297 int pageNumProcessed = 1;
\r
298 while(true) { // Keep looping until we find all the refs.
\r
299 logger.debug("updateAuthorityRefDocs working on page: "+pageNumProcessed);
\r
300 // Note that we always ask the Repo for the first page, since each page we process
\r
301 // should not be found in successive searches. Slightly inefficient, but more
\r
302 // reliable (stateless).
\r
303 DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,
\r
304 getRefNameServiceTypes(), oldRefName, refPropName,
\r
305 queriedServiceBindings, authRefFieldsByService, pageSize, 0, false);
\r
307 if((docList == null) // found no authRef fields - nothing to do
\r
308 || (docList.size() == 0)) { // No more to handle
\r
309 logger.debug("updateAuthorityRefDocs no more results");
\r
312 logger.debug("updateAuthorityRefDocs curr page result list size: "+docList.size());
\r
313 int nRefsFoundThisPage = processRefObjsDocList(docList, oldRefName, queriedServiceBindings, authRefFieldsByService,
\r
315 if(nRefsFoundThisPage>0) {
\r
316 ((RepositoryJavaClientImpl)repoClient).saveDocListWithoutHandlerProcessing(ctx, repoSession, docList, true);
\r
317 nRefsFound += nRefsFoundThisPage;
\r
319 pageNumProcessed++;
\r
320 if(docList.size()<pageSize) {
\r
321 logger.debug("updateAuthorityRefDocs: assuming no more results, as nResults < pageSize");
\r
325 } catch (Exception e) {
\r
326 logger.error("Internal error updating the AuthorityRefDocs: " + e.getLocalizedMessage());
\r
327 logger.debug(Tools.errorToString(e, true));
\r
330 logger.debug("updateAuthorityRefDocs replaced a total of: "+nRefsFound);
\r
334 private static DocumentModelList findAuthorityRefDocs(
\r
335 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
336 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
337 RepositoryInstance repoSession,
\r
338 List<String> serviceTypes,
\r
340 String refPropName,
\r
341 Map<String, ServiceBindingType> queriedServiceBindings,
\r
342 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
\r
343 int pageSize, int pageNum, boolean computeTotal) throws DocumentException, DocumentNotFoundException {
\r
345 // Get the service bindings for this tenant
\r
346 TenantBindingConfigReaderImpl tReader =
\r
347 ServiceMain.getInstance().getTenantBindingConfigReader();
\r
348 // We need to get all the procedures, authorities, and objects.
\r
349 List<ServiceBindingType> servicebindings = tReader.getServiceBindingsByType(ctx.getTenantId(), serviceTypes);
\r
350 if (servicebindings == null || servicebindings.isEmpty()) {
\r
351 logger.error("RefNameServiceUtils.getAuthorityRefDocs: No services bindings found, cannot proceed!");
\r
354 // Filter the list for current user rights
\r
355 servicebindings = SecurityUtils.getReadableServiceBindingsForCurrentUser(servicebindings);
\r
357 ArrayList<String> docTypes = new ArrayList<String>();
\r
359 String query = computeWhereClauseForAuthorityRefDocs(refName, refPropName, docTypes, servicebindings,
\r
360 queriedServiceBindings, authRefFieldsByService );
\r
361 if (query == null) { // found no authRef fields - nothing to query
\r
364 // Now we have to issue the search
\r
365 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl)repoClient;
\r
366 DocumentWrapper<DocumentModelList> docListWrapper = nuxeoRepoClient.findDocs(ctx, repoSession,
\r
367 docTypes, query, pageSize, pageNum, computeTotal);
\r
368 // Now we gather the info for each document into the list and return
\r
369 DocumentModelList docList = docListWrapper.getWrappedObject();
\r
373 private static final boolean READY_FOR_COMPLEX_QUERY = true;
\r
375 private static String computeWhereClauseForAuthorityRefDocs(
\r
377 String refPropName,
\r
378 ArrayList<String> docTypes,
\r
379 List<ServiceBindingType> servicebindings,
\r
380 Map<String, ServiceBindingType> queriedServiceBindings,
\r
381 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService ) {
\r
383 boolean fFirst = true;
\r
384 List<String> authRefFieldPaths;
\r
385 for (ServiceBindingType sb : servicebindings) {
\r
386 // Gets the property names for each part, qualified with the part label (which
\r
387 // is also the table name, the way that the repository works).
\r
388 authRefFieldPaths =
\r
389 ServiceBindingUtils.getAllPartsPropertyValues(sb,
\r
390 refPropName, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
\r
391 if (authRefFieldPaths.isEmpty()) {
\r
394 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>();
\r
395 for(String spec:authRefFieldPaths) {
\r
396 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
\r
397 authRefsInfo.add(arci);
\r
400 String docType = sb.getObject().getName();
\r
401 queriedServiceBindings.put(docType, sb);
\r
402 authRefFieldsByService.put(docType, authRefsInfo);
\r
403 docTypes.add(docType);
\r
406 if (fFirst) { // found no authRef fields - nothing to query
\r
409 // We used to build a complete matches query, but that was too complex.
\r
410 // Just build a keyword query based upon some key pieces - the urn syntax elements and the shortID
\r
411 // Note that this will also match the Item itself, but that will get filtered out when
\r
412 // we compute actual matches.
\r
413 AuthorityTermInfo authTermInfo = RefNameUtils.parseAuthorityTermInfo(refName);
\r
415 String keywords = RefNameUtils.URN_PREFIX
\r
416 + " AND " + (authTermInfo.inAuthority.name!=null?
\r
417 authTermInfo.inAuthority.name:authTermInfo.inAuthority.csid)
\r
418 + " AND " + (authTermInfo.name!=null?
\r
419 authTermInfo.name:authTermInfo.csid);
\r
421 String whereClauseStr = QueryManager.createWhereClauseFromKeywords(keywords);
\r
423 if (logger.isTraceEnabled()) {
\r
424 logger.trace("The 'where' clause to find refObjs is: ", whereClauseStr);
\r
427 return whereClauseStr;
\r
431 * Runs through the list of found docs, processing them.
\r
432 * If list is non-null, then processing means gather the info for items.
\r
433 * If list is null, and newRefName is non-null, then processing means replacing and updating.
\r
434 * If processing/updating, this must be called in teh context of an open session, and caller
\r
435 * must release Session after calling this.
\r
438 private static int processRefObjsDocList(
\r
439 DocumentModelList docList,
\r
441 Map<String, ServiceBindingType> queriedServiceBindings,
\r
442 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
\r
443 List<AuthorityRefDocList.AuthorityRefDocItem> list,
\r
444 String newAuthorityRefName) {
\r
445 Iterator<DocumentModel> iter = docList.iterator();
\r
446 int nRefsFoundTotal = 0;
\r
447 while (iter.hasNext()) {
\r
448 DocumentModel docModel = iter.next();
\r
449 AuthorityRefDocList.AuthorityRefDocItem ilistItem;
\r
451 String docType = docModel.getDocumentType().getName();
\r
452 docType = ServiceBindingUtils.getUnqualifiedTenantDocType(docType);
\r
453 ServiceBindingType sb = queriedServiceBindings.get(docType);
\r
455 throw new RuntimeException(
\r
456 "getAuthorityRefDocs: No Service Binding for docType: " + docType);
\r
458 String serviceContextPath = "/" + sb.getName().toLowerCase() + "/";
\r
460 if(list == null) { // no list - should be update refName case.
\r
461 if(newAuthorityRefName==null) {
\r
462 throw new InternalError("processRefObjsDocList() called with neither an itemList nor a new RefName!");
\r
465 } else { // Have a list - refObjs case
\r
466 if(newAuthorityRefName!=null) {
\r
467 throw new InternalError("processRefObjsDocList() called with both an itemList and a new RefName!");
\r
469 ilistItem = new AuthorityRefDocList.AuthorityRefDocItem();
\r
470 String csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
\r
471 ilistItem.setDocId(csid);
\r
472 ilistItem.setUri(serviceContextPath + csid);
\r
474 ilistItem.setWorkflowState(docModel.getCurrentLifeCycleState());
\r
475 ilistItem.setUpdatedAt(DocHandlerBase.getUpdatedAtAsString(docModel));
\r
476 } catch(Exception e) {
\r
477 logger.error("Error getting core values for doc ["+csid+"]: "+e.getLocalizedMessage());
\r
479 // The id and URI are the same on all doctypes
\r
480 ilistItem.setDocType(docType);
\r
481 ilistItem.setDocNumber(
\r
482 ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NUMBER_PROP, docModel));
\r
483 ilistItem.setDocName(
\r
484 ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NAME_PROP, docModel));
\r
486 // Now, we have to loop over the authRefFieldsByService to figure
\r
487 // out which field(s) matched this.
\r
488 List<AuthRefConfigInfo> matchingAuthRefFields = authRefFieldsByService.get(docType);
\r
489 if (matchingAuthRefFields == null || matchingAuthRefFields.isEmpty()) {
\r
490 throw new RuntimeException(
\r
491 "getAuthorityRefDocs: internal logic error: can't fetch authRefFields for DocType.");
\r
493 //String authRefAncestorField = "";
\r
494 //String authRefDescendantField = "";
\r
495 //String sourceField = "";
\r
496 int nRefsFoundInDoc = 0;
\r
498 ArrayList<RefNameServiceUtils.AuthRefInfo> foundProps
\r
499 = new ArrayList<RefNameServiceUtils.AuthRefInfo>();
\r
501 findAuthRefPropertiesInDoc(docModel, matchingAuthRefFields, refName, foundProps);
\r
502 for(RefNameServiceUtils.AuthRefInfo ari:foundProps) {
\r
503 if(ilistItem != null) {
\r
504 if(nRefsFoundInDoc == 0) { // First one?
\r
505 ilistItem.setSourceField(ari.getQualifiedDisplayName());
\r
506 } else { // duplicates from one object
\r
507 ilistItem = cloneAuthRefDocItem(ilistItem, ari.getQualifiedDisplayName());
\r
509 list.add(ilistItem);
\r
510 } else { // update refName case
\r
511 Property propToUpdate = ari.getProperty();
\r
512 propToUpdate.setValue(newAuthorityRefName);
\r
516 } catch (ClientException ce) {
\r
517 throw new RuntimeException(
\r
518 "getAuthorityRefDocs: Problem fetching values from repo: " + ce.getLocalizedMessage());
\r
520 if (nRefsFoundInDoc == 0) {
\r
522 "getAuthorityRefDocs: Result: "
\r
523 + docType + " [" + NuxeoUtils.getCsid(docModel)
\r
524 + "] does not reference ["
\r
527 nRefsFoundTotal += nRefsFoundInDoc;
\r
529 return nRefsFoundTotal;
\r
532 private static AuthorityRefDocList.AuthorityRefDocItem cloneAuthRefDocItem(
\r
533 AuthorityRefDocList.AuthorityRefDocItem ilistItem, String sourceField) {
\r
534 AuthorityRefDocList.AuthorityRefDocItem newlistItem = new AuthorityRefDocList.AuthorityRefDocItem();
\r
535 newlistItem.setDocId(ilistItem.getDocId());
\r
536 newlistItem.setDocName(ilistItem.getDocName());
\r
537 newlistItem.setDocNumber(ilistItem.getDocNumber());
\r
538 newlistItem.setDocType(ilistItem.getDocType());
\r
539 newlistItem.setUri(ilistItem.getUri());
\r
540 newlistItem.setSourceField(sourceField);
\r
541 return newlistItem;
\r
544 public static List<AuthRefInfo> findAuthRefPropertiesInDoc(
\r
545 DocumentModel docModel,
\r
546 List<AuthRefConfigInfo> authRefFieldInfo,
\r
547 String refNameToMatch,
\r
548 List<AuthRefInfo> foundProps
\r
550 // Assume that authRefFieldInfo is keyed by the field name (possibly mapped for UI)
\r
551 // and the values are elPaths to the field, where intervening group structures in
\r
552 // lists of complex structures are replaced with "*". Thus, valid paths include
\r
553 // the following (note that the ServiceBindingUtils prepend schema names to configured values):
\r
554 // "schemaname:fieldname"
\r
555 // "schemaname:scalarlistname"
\r
556 // "schemaname:complexfieldname/fieldname"
\r
557 // "schemaname:complexlistname/*/fieldname"
\r
558 // "schemaname:complexlistname/*/scalarlistname"
\r
559 // "schemaname:complexlistname/*/complexfieldname/fieldname"
\r
560 // "schemaname:complexlistname/*/complexlistname/*/fieldname"
\r
562 for (AuthRefConfigInfo arci : authRefFieldInfo) {
\r
564 // Get first property and work down as needed.
\r
565 Property prop = docModel.getProperty(arci.pathEls[0]);
\r
566 findAuthRefPropertiesInProperty(foundProps, prop, arci, 0, refNameToMatch);
\r
567 } catch(Exception e) {
\r
568 logger.error("Problem fetching property: "+arci.pathEls[0]);
\r
574 public static List<AuthRefInfo> findAuthRefPropertiesInProperty(
\r
575 List<AuthRefInfo> foundProps,
\r
577 AuthRefConfigInfo arci,
\r
578 int pathStartIndex, // Supports recursion and we work down the path
\r
579 String refNameToMatch
\r
581 if (pathStartIndex >= arci.pathEls.length) {
\r
582 throw new ArrayIndexOutOfBoundsException("Index = "+pathStartIndex+" for path: "
\r
583 +arci.pathEls.toString());
\r
585 AuthRefInfo ari = null;
\r
586 if (prop == null) {
\r
590 if (prop instanceof StringProperty) { // scalar string
\r
591 addARIifMatches(refNameToMatch, arci, prop, foundProps);
\r
592 } else if(prop instanceof List) {
\r
593 List<Property> propList = (List<Property>)prop;
\r
594 // run through list. Must either be list of Strings, or Complex
\r
595 for (Property listItemProp : propList) {
\r
596 if(listItemProp instanceof StringProperty) {
\r
597 if(arci.pathEls.length-pathStartIndex != 1) {
\r
598 logger.error("Configuration for authRefs does not match schema structure: "
\r
599 +arci.pathEls.toString());
\r
602 addARIifMatches(refNameToMatch, arci, listItemProp, foundProps);
\r
604 } else if(listItemProp.isComplex()) {
\r
605 // Just recurse to handle this. Note that since this is a list of complex,
\r
606 // which should look like listName/*/... we add 2 to the path start index
\r
607 findAuthRefPropertiesInProperty(foundProps, listItemProp, arci,
\r
608 pathStartIndex+2, refNameToMatch);
\r
610 logger.error("Configuration for authRefs does not match schema structure: "
\r
611 +arci.pathEls.toString());
\r
615 } else if(prop.isComplex()) {
\r
616 String localPropName = arci.pathEls[pathStartIndex];
\r
618 Property localProp = prop.get(localPropName);
\r
619 // Now just recurse, pushing down the path 1 step
\r
620 findAuthRefPropertiesInProperty(foundProps, localProp, arci,
\r
621 pathStartIndex, refNameToMatch);
\r
622 } catch(PropertyNotFoundException pnfe) {
\r
623 logger.error("Could not find property: ["+localPropName+"] in path: "+
\r
624 arci.getFullPath());
\r
625 // Fall through - ari will be null and we will continue...
\r
628 logger.error("Configuration for authRefs does not match schema structure: "
\r
629 +arci.pathEls.toString());
\r
633 foundProps.add(ari); //FIXME: REM - This is dead code. 'ari' is never touched after being initalized to null. Why?
\r
639 private static void addARIifMatches(
\r
640 String refNameToMatch,
\r
641 AuthRefConfigInfo arci,
\r
643 List<AuthRefInfo> foundProps) {
\r
644 // Need to either match a passed refName
\r
645 // OR have no refName to match but be non-empty
\r
647 String value = (String)prop.getValue();
\r
648 if(((refNameToMatch!=null) && refNameToMatch.equals(value))
\r
649 || ((refNameToMatch==null) && Tools.notBlank(value))) {
\r
651 logger.debug("Found a match on property: "+prop.getPath()+" with value: ["+value+"]");
\r
652 AuthRefInfo ari = new AuthRefInfo(arci, prop);
\r
653 foundProps.add(ari);
\r
655 } catch(PropertyException pe) {
\r
656 logger.debug("PropertyException on: "+prop.getPath()+pe.getLocalizedMessage());
\r
661 * Identifies whether the refName was found in the supplied field.
\r
662 * If passed a new RefName, will set that into fields in which the old one was found.
\r
666 * * Repeatable scalar fields (aka multi-valued fields)
\r
668 * Does not work for:
\r
669 * * Structured fields (complexTypes)
\r
670 * * Repeatable structured fields (repeatable complexTypes)
\r
671 private static int refNameFoundInField(String oldRefName, Property fieldValue, String newRefName) {
\r
673 if (fieldValue instanceof List) {
\r
674 List<Property> fieldValueList = (List) fieldValue;
\r
675 for (Property listItemValue : fieldValueList) {
\r
677 if ((listItemValue instanceof StringProperty)
\r
678 && oldRefName.equalsIgnoreCase((String)listItemValue.getValue())) {
\r
680 if(newRefName!=null) {
\r
681 fieldValue.setValue(newRefName);
\r
683 // We cannot quit after the first, if we are replacing values.
\r
684 // If we are just looking (not replacing), finding one is enough.
\r
688 } catch( PropertyException pe ) {}
\r
692 if ((fieldValue instanceof StringProperty)
\r
693 && oldRefName.equalsIgnoreCase((String)fieldValue.getValue())) {
\r
695 if(newRefName!=null) {
\r
696 fieldValue.setValue(newRefName);
\r
699 } catch( PropertyException pe ) {}
\r