View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kns.util;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.CoreApiServiceLocator;
20  import org.kuali.rice.core.api.config.property.ConfigurationService;
21  import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition;
22  import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition;
23  import org.kuali.rice.kns.datadictionary.MaintainableItemDefinition;
24  import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition;
25  import org.kuali.rice.kns.lookup.LookupUtils;
26  import org.kuali.rice.kns.maintenance.Maintainable;
27  import org.kuali.rice.kns.service.KNSServiceLocator;
28  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
29  import org.kuali.rice.kns.web.ui.Field;
30  import org.kuali.rice.kns.web.ui.Row;
31  import org.kuali.rice.kns.web.ui.Section;
32  import org.kuali.rice.krad.bo.BusinessObject;
33  import org.kuali.rice.krad.datadictionary.AttributeSecurity;
34  import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry;
35  import org.kuali.rice.krad.exception.ValidationException;
36  import org.kuali.rice.kns.lookup.SelectiveReferenceRefresher;
37  import org.kuali.rice.krad.service.DataDictionaryService;
38  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
39  import org.kuali.rice.krad.service.KualiExceptionIncidentService;
40  import org.kuali.rice.krad.service.MaintenanceDocumentService;
41  import org.kuali.rice.krad.util.KRADConstants;
42  import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
43  
44  import java.util.Collection;
45  import java.util.HashMap;
46  import java.util.HashSet;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Map;
50  import java.util.Set;
51  import java.util.StringTokenizer;
52  
53  /**
54   * @deprecated Use {@link org.kuali.rice.krad.maintenance.MaintenanceUtils}.
55   */
56  @Deprecated
57  public final class MaintenanceUtils {
58      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintenanceUtils.class);
59  
60      private static MaintenanceDocumentService maintenanceDocumentService;
61      private static WorkflowDocumentService workflowDocumentService;
62      private static ConfigurationService kualiConfigurationService;
63      private static KualiExceptionIncidentService kualiExceptionIncidentService;
64      private static MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
65      private static DataDictionaryService dataDictionaryService;
66  
67      private MaintenanceUtils() {
68          throw new UnsupportedOperationException("do not call");
69      }
70  
71      /**
72       * Returns the field templates defined in the maint dictionary xml files. Field templates are used in multiple value lookups.
73       * When doing a MV lookup on a collection, the returned BOs are not necessarily of the same type as the elements of the
74       * collection. Therefore, a means of mapping between the fields for the 2 BOs are necessary. The template attribute of
75       * <maintainableField>s contained within <maintainableCollection>s tells us this mapping. Example: a
76       * <maintainableField name="collectionAttrib" template="lookupBOAttrib"> definition means that when a list of BOs are
77       * returned, the lookupBOAttrib value of the looked up BO will be placed into the collectionAttrib value of the BO added to the
78       * collection
79       *
80       * @param sections       the sections of a document
81       * @param collectionName the name of a collection. May be a nested collection with indices (e.g. collA[1].collB)
82       * @return
83       */
84      public static Map<String, String> generateMultipleValueLookupBOTemplate(List<MaintainableSectionDefinition> sections, String collectionName) {
85          MaintainableCollectionDefinition definition = findMaintainableCollectionDefinition(sections, collectionName);
86          if (definition == null) {
87              return null;
88          }
89          Map<String, String> template = null;
90  
91          for (MaintainableFieldDefinition maintainableField : definition.getMaintainableFields()) {
92              String templateString = maintainableField.getTemplate();
93              if (StringUtils.isNotBlank(templateString)) {
94                  if (template == null) {
95                      template = new HashMap<String, String>();
96                  }
97                  template.put(maintainableField.getName(), templateString);
98              }
99          }
100         return template;
101     }
102 
103     /**
104      * Finds the MaintainableCollectionDefinition corresponding to the given collection name. For example, if the collection name is
105      * "A.B.C", it will attempt to find the MaintainableCollectionDefinition for C that is nested in B that is nested under A. This
106      * may not work correctly if there are duplicate collection definitions within the sections
107      *
108      * @param sections       the sections of a maint doc
109      * @param collectionName the name of a collection, relative to the root of the BO being maintained. This value may have index
110      *                       values (e.g. [1]), but these are ignored.
111      * @return
112      */
113     public static MaintainableCollectionDefinition findMaintainableCollectionDefinition(List<MaintainableSectionDefinition> sections, String collectionName) {
114         String[] collectionNameParts = StringUtils.split(collectionName, ".");
115         for (MaintainableSectionDefinition section : sections) {
116             MaintainableCollectionDefinition collDefinition = findMaintainableCollectionDefinitionHelper(section.getMaintainableItems(), collectionNameParts, 0);
117             if (collDefinition != null) {
118                 return collDefinition;
119             }
120         }
121         return null;
122     }
123 
124     private static <E extends MaintainableItemDefinition> MaintainableCollectionDefinition findMaintainableCollectionDefinitionHelper(Collection<E> items, String[] collectionNameParts, int collectionNameIndex) {
125         if (collectionNameParts.length <= collectionNameIndex) {
126             // we've gone too far down the nesting without finding it
127             return null;
128         }
129 
130         // we only care about the coll name, and not the index, since the coll definitions do not include the indexing characters,
131         // i.e. [ and ]
132         String collectionToFind = StringUtils.substringBefore(collectionNameParts[collectionNameIndex], "[");
133         for (MaintainableItemDefinition item : items) {
134             if (item instanceof MaintainableCollectionDefinition) {
135                 MaintainableCollectionDefinition collection = (MaintainableCollectionDefinition) item;
136                 if (collection.getName().equals(collectionToFind)) {
137                     // we found an appropriate coll, now we have to see if we need to recurse even more (more nested collections),
138                     // or just return the one we found.
139                     if (collectionNameIndex == collectionNameParts.length - 1) {
140                         // we're at the last part of the name, so we return
141                         return collection;
142                     } else {
143                         // go deeper
144                         return findMaintainableCollectionDefinitionHelper(collection.getMaintainableCollections(), collectionNameParts, collectionNameIndex + 1);
145                     }
146                 }
147             }
148         }
149         return null;
150     }
151 
152     /**
153      * Checks to see if there has been an override lookup declared for the maintenance field. If so, the override will be used for
154      * the quickfinder and lookup utils will not be called. If no override was given, LookupUtils.setFieldQuickfinder will be called
155      * to set the system generated quickfinder based on the attributes relationship to the parent business object.
156      *
157      * @return Field with quickfinder set if one was found
158      */
159     public static final Field setFieldQuickfinder(BusinessObject businessObject, String attributeName, MaintainableFieldDefinition maintainableFieldDefinition, Field field, List<String> displayedFieldNames, SelectiveReferenceRefresher srr) {
160         if (maintainableFieldDefinition.getOverrideLookupClass() != null && StringUtils.isNotBlank(maintainableFieldDefinition.getOverrideFieldConversions())) {
161             field.setQuickFinderClassNameImpl(maintainableFieldDefinition.getOverrideLookupClass().getName());
162             field.setFieldConversions(maintainableFieldDefinition.getOverrideFieldConversions());
163             field.setBaseLookupUrl(LookupUtils.getBaseLookupUrl(false));
164             field.setReferencesToRefresh(LookupUtils.convertReferencesToSelectCollectionToString(
165                     srr.getAffectedReferencesFromLookup(businessObject, attributeName, "")));
166             return field;
167         }
168         if (maintainableFieldDefinition.isNoLookup()) {
169             return field;
170         }
171         return LookupUtils.setFieldQuickfinder(businessObject, null, false, 0, attributeName, field, displayedFieldNames, maintainableFieldDefinition.isNoLookup());
172     }
173 
174     public static final Field setFieldQuickfinder(BusinessObject businessObject, String collectionName, boolean addLine, int index,
175                                                   String attributeName, Field field, List<String> displayedFieldNames, Maintainable maintainable, MaintainableFieldDefinition maintainableFieldDefinition) {
176         if (maintainableFieldDefinition.getOverrideLookupClass() != null && StringUtils.isNotBlank(maintainableFieldDefinition.getOverrideFieldConversions())) {
177             if (maintainable != null) {
178                 String collectionPrefix = "";
179                 if (collectionName != null) {
180                     if (addLine) {
181                         collectionPrefix = KRADConstants.MAINTENANCE_ADD_PREFIX + collectionName + ".";
182                     } else {
183                         collectionPrefix = collectionName + "[" + index + "].";
184                     }
185                 }
186                 field.setQuickFinderClassNameImpl(maintainableFieldDefinition.getOverrideLookupClass().getName());
187 
188                 String prefixedFieldConversions = prefixFieldConversionsDestinationsWithCollectionPrefix(maintainableFieldDefinition.getOverrideFieldConversions(), collectionPrefix);
189                 field.setFieldConversions(prefixedFieldConversions);
190                 field.setBaseLookupUrl(LookupUtils.getBaseLookupUrl(false));
191                 field.setReferencesToRefresh(LookupUtils.convertReferencesToSelectCollectionToString(
192                         maintainable.getAffectedReferencesFromLookup(businessObject, attributeName, collectionPrefix)));
193             }
194             return field;
195         }
196         if (maintainableFieldDefinition.isNoLookup()) {
197             return field;
198         }
199         return LookupUtils.setFieldQuickfinder(businessObject, collectionName, addLine, index,
200                 attributeName, field, displayedFieldNames, maintainable);
201     }
202 
203     private static String prefixFieldConversionsDestinationsWithCollectionPrefix(String originalFieldConversions, String collectionPrefix) {
204         StringBuilder buf = new StringBuilder();
205         StringTokenizer tok = new StringTokenizer(originalFieldConversions, KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
206         boolean needsSeparator = false;
207         while (tok.hasMoreTokens()) {
208             String conversionPair = tok.nextToken();
209             if (StringUtils.isBlank(conversionPair)) {
210                 continue;
211             }
212 
213             String fromValue = StringUtils.substringBefore(conversionPair, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
214             String toValue = StringUtils.substringAfter(conversionPair, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
215 
216             if (needsSeparator) {
217                 buf.append(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
218             }
219             needsSeparator = true;
220 
221             buf.append(fromValue).append(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR).append(collectionPrefix).append(toValue);
222         }
223         return buf.toString();
224     }
225 
226     public static final void setFieldDirectInquiry(BusinessObject businessObject, String attributeName, MaintainableFieldDefinition maintainableFieldDefinition, Field field, List<String> displayedFieldNames) {
227         LookupUtils.setFieldDirectInquiry(businessObject, attributeName, field);
228     }
229 
230     public static final void setFieldDirectInquiry(BusinessObject businessObject, String collectionName, boolean addLine, int index,
231                                                    String attributeName, Field field, List<String> displayedFieldNames, Maintainable maintainable, MaintainableFieldDefinition maintainableFieldDefinition) {
232         LookupUtils.setFieldDirectInquiry(businessObject, attributeName, field);
233     }
234 
235     /**
236      * Given a section, returns a comma delimited string of all fields, representing the error keys that exist for a section
237      *
238      * @param section a section
239      * @return
240      */
241     public static String generateErrorKeyForSection(Section section) {
242         Set<String> fieldPropertyNames = new HashSet<String>();
243         addRowsToErrorKeySet(section.getRows(), fieldPropertyNames);
244 
245         StringBuilder buf = new StringBuilder();
246         buf.append(section.getSectionId()).append(",");
247 
248         Iterator<String> nameIter = fieldPropertyNames.iterator();
249         while (nameIter.hasNext()) {
250             buf.append(nameIter.next());
251             if (nameIter.hasNext()) {
252                 buf.append(",");
253             }
254         }
255 
256         if (section.getContainedCollectionNames() != null && section.getContainedCollectionNames().size() > 0) {
257             buf.append(",");
258 
259             Iterator<String> collectionIter = section.getContainedCollectionNames().iterator();
260             while (collectionIter.hasNext()) {
261                 buf.append(KRADConstants.MAINTENANCE_NEW_MAINTAINABLE + collectionIter.next());
262                 if (collectionIter.hasNext()) {
263                     buf.append(",");
264                 }
265             }
266         }
267 
268         return buf.toString();
269     }
270 
271     /**
272      * This method recurses through all the fields of the list of rows and adds each field's property name to the set if it starts
273      * with Constants.MAINTENANCE_NEW_MAINTAINABLE
274      *
275      * @param listOfRows
276      * @param errorKeys
277      * @see KRADConstants#MAINTENANCE_NEW_MAINTAINABLE
278      */
279     protected static void addRowsToErrorKeySet(List<Row> listOfRows, Set<String> errorKeys) {
280         if (listOfRows == null) {
281             return;
282         }
283         for (Row row : listOfRows) {
284             List<Field> fields = row.getFields();
285             if (fields == null) {
286                 continue;
287             }
288             for (Field field : fields) {
289                 String fieldPropertyName = field.getPropertyName();
290                 if (fieldPropertyName != null && fieldPropertyName.startsWith(KRADConstants.MAINTENANCE_NEW_MAINTAINABLE)) {
291                     errorKeys.add(field.getPropertyName());
292                 }
293                 addRowsToErrorKeySet(field.getContainerRows(), errorKeys);
294             }
295         }
296     }
297 
298     /**
299      * This method will throw a {@link ValidationException} if there is a valid locking document in existence and throwExceptionIfLocked is true.
300      */
301     public static void checkForLockingDocument(Maintainable maintainable, final boolean throwExceptionIfLocked) {
302         LOG.info("starting checkForLockingDocument (by Maintainable)");
303 
304         // get the docHeaderId of the blocking docs, if any are locked and blocking
305         //String blockingDocId = getMaintenanceDocumentService().getLockingDocumentId(maintainable, null);
306         final String blockingDocId = maintainable.getLockingDocumentId();
307 
308         org.kuali.rice.krad.maintenance.MaintenanceUtils.checkDocumentBlockingDocumentId(blockingDocId, throwExceptionIfLocked);
309     }
310 
311     private static MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
312         if (maintenanceDocumentDictionaryService == null) {
313             maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
314         }
315         return maintenanceDocumentDictionaryService;
316     }
317 
318     private static DataDictionaryService getDataDictionaryService() {
319         if (dataDictionaryService == null) {
320             dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
321         }
322         return dataDictionaryService;
323     }
324 
325     public static Map<String, AttributeSecurity> retrievePropertyPathToAttributeSecurityMappings(String docTypeName) {
326         Map<String, AttributeSecurity> results = new HashMap<String, AttributeSecurity>();
327         MaintenanceDocumentEntry entry = getMaintenanceDocumentDictionaryService().getMaintenanceDocumentEntry(docTypeName);
328         String className = entry.getDataObjectClass().getName();
329 
330         for (MaintainableSectionDefinition section : entry.getMaintainableSections()) {
331             for (MaintainableItemDefinition item : section.getMaintainableItems()) {
332                 if (item instanceof MaintainableFieldDefinition) {
333                     MaintainableFieldDefinition field = (MaintainableFieldDefinition) item;
334                     AttributeSecurity attributeSecurity = getDataDictionaryService().getAttributeSecurity(className, field.getName());
335                     if (attributeSecurity != null) {
336                         results.put(field.getName(), attributeSecurity);
337                     }
338                 } else if (item instanceof MaintainableCollectionDefinition) {
339                     addMaintenanceDocumentCollectionPathToSecurityMappings(results, "", (MaintainableCollectionDefinition) item);
340                 }
341             }
342         }
343         return results;
344     }
345 
346     private static void addMaintenanceDocumentCollectionPathToSecurityMappings(Map<String, AttributeSecurity> mappings, String propertyPathPrefix, MaintainableCollectionDefinition collectionDefinition) {
347         propertyPathPrefix = propertyPathPrefix + collectionDefinition.getName() + ".";
348         String boClassName = collectionDefinition.getBusinessObjectClass().getName();
349         for (MaintainableFieldDefinition field : collectionDefinition.getMaintainableFields()) {
350             AttributeSecurity attributeSecurity = getDataDictionaryService().getAttributeSecurity(boClassName, field.getName());
351             if (attributeSecurity != null) {
352                 mappings.put(propertyPathPrefix + field.getName(), attributeSecurity);
353             }
354         }
355         for (MaintainableCollectionDefinition nestedCollection : collectionDefinition.getMaintainableCollections()) {
356             addMaintenanceDocumentCollectionPathToSecurityMappings(mappings, propertyPathPrefix, nestedCollection);
357         }
358     }
359 
360     private static MaintenanceDocumentService getMaintenanceDocumentService() {
361         if (maintenanceDocumentService == null) {
362             maintenanceDocumentService = KRADServiceLocatorWeb.getMaintenanceDocumentService();
363         }
364         return maintenanceDocumentService;
365     }
366 
367     private static WorkflowDocumentService getWorkflowDocumentService() {
368         if (workflowDocumentService == null) {
369             workflowDocumentService = KRADServiceLocatorWeb.getWorkflowDocumentService();
370         }
371         return workflowDocumentService;
372     }
373 
374     private static ConfigurationService getKualiConfigurationService() {
375         if (kualiConfigurationService == null) {
376             kualiConfigurationService = CoreApiServiceLocator.getKualiConfigurationService();
377         }
378         return kualiConfigurationService;
379     }
380 
381     private static KualiExceptionIncidentService getKualiExceptionIncidentService() {
382         if (kualiExceptionIncidentService == null) {
383             kualiExceptionIncidentService = KRADServiceLocatorWeb.getKualiExceptionIncidentService();
384         }
385         return kualiExceptionIncidentService;
386     }
387 }