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