001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.core.util;
017:
018: import java.util.Collection;
019: import java.util.HashMap;
020: import java.util.HashSet;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.Set;
025:
026: import org.apache.commons.lang.StringUtils;
027: import org.kuali.RiceConstants;
028: import org.kuali.core.bo.BusinessObject;
029: import org.kuali.core.datadictionary.MaintainableCollectionDefinition;
030: import org.kuali.core.datadictionary.MaintainableFieldDefinition;
031: import org.kuali.core.datadictionary.MaintainableItemDefinition;
032: import org.kuali.core.datadictionary.MaintainableSectionDefinition;
033: import org.kuali.core.lookup.LookupUtils;
034: import org.kuali.core.lookup.SelectiveReferenceRefresher;
035: import org.kuali.core.web.ui.Field;
036: import org.kuali.core.web.ui.Row;
037: import org.kuali.core.web.ui.Section;
038:
039: public class MaintenanceUtils {
040: /**
041: * Returns the field templates defined in the maint dictionary xml files. Field templates are used in multiple value lookups.
042: * When doing a MV lookup on a collection, the returned BOs are not necessarily of the same type as the elements of the
043: * collection. Therefore, a means of mapping between the fields for the 2 BOs are necessary. The template attribute of
044: * <maintainableField>s contained within <maintainableCollection>s tells us this mapping. Example: a
045: * <maintainableField name="collectionAttrib" template="lookupBOAttrib"> definition means that when a list of BOs are
046: * returned, the lookupBOAttrib value of the looked up BO will be placed into the collectionAttrib value of the BO added to the
047: * collection
048: *
049: * @param sections the sections of a document
050: * @param collectionName the name of a collection. May be a nested collection with indices (e.g. collA[1].collB)
051: * @return
052: */
053: public static Map<String, String> generateMultipleValueLookupBOTemplate(
054: List<MaintainableSectionDefinition> sections,
055: String collectionName) {
056: MaintainableCollectionDefinition definition = findMaintainableCollectionDefinition(
057: sections, collectionName);
058: if (definition == null) {
059: return null;
060: }
061: Map<String, String> template = null;
062:
063: String name = definition.getSourceAttributeName();
064: if (name != null) {
065: template = new HashMap<String, String>();
066: for (MaintainableFieldDefinition maintainableField : definition
067: .getMaintainableFields()) {
068: String templateString = maintainableField.getTemplate();
069: if (StringUtils.isNotBlank(templateString)) {
070: template.put(maintainableField.getName(),
071: templateString);
072: }
073: }
074: }
075: return template;
076: }
077:
078: /**
079: * Finds the MaintainableCollectionDefinition corresponding to the given collection name. For example, if the collection name is
080: * "A.B.C", it will attempt to find the MaintainableCollectionDefinition for C that is nested in B that is nested under A. This
081: * may not work correctly if there are duplicate collection definitions within the sections
082: *
083: * @param sections the sections of a maint doc
084: * @param collectionName the name of a collection, relative to the root of the BO being maintained. This value may have index
085: * values (e.g. [1]), but these are ignored.
086: * @return
087: */
088: public static MaintainableCollectionDefinition findMaintainableCollectionDefinition(
089: List<MaintainableSectionDefinition> sections,
090: String collectionName) {
091: String[] collectionNameParts = StringUtils.split(
092: collectionName, ".");
093: for (MaintainableSectionDefinition section : sections) {
094: MaintainableCollectionDefinition collDefinition = findMaintainableCollectionDefinitionHelper(
095: section.getMaintainableItems(),
096: collectionNameParts, 0);
097: if (collDefinition != null) {
098: return collDefinition;
099: }
100: }
101: return null;
102: }
103:
104: private static <E extends MaintainableItemDefinition> MaintainableCollectionDefinition findMaintainableCollectionDefinitionHelper(
105: Collection<E> items, String[] collectionNameParts,
106: int collectionNameIndex) {
107: if (collectionNameParts.length <= collectionNameIndex) {
108: // we've gone too far down the nesting without finding it
109: return null;
110: }
111:
112: // we only care about the coll name, and not the index, since the coll definitions do not include the indexing characters,
113: // i.e. [ and ]
114: String collectionToFind = StringUtils.substringBefore(
115: collectionNameParts[collectionNameIndex], "[");
116: for (MaintainableItemDefinition item : items) {
117: if (item instanceof MaintainableCollectionDefinition) {
118: MaintainableCollectionDefinition collection = (MaintainableCollectionDefinition) item;
119: if (collection.getName().equals(collectionToFind)) {
120: // we found an appropriate coll, now we have to see if we need to recurse even more (more nested collections),
121: // or just return the one we found.
122: if (collectionNameIndex == collectionNameParts.length - 1) {
123: // we're at the last part of the name, so we return
124: return collection;
125: } else {
126: // go deeper
127: return findMaintainableCollectionDefinitionHelper(
128: collection.getMaintainableCollections(),
129: collectionNameParts,
130: collectionNameIndex + 1);
131: }
132: }
133: }
134: }
135: return null;
136: }
137:
138: /**
139: * Checks to see if there has been an override lookup declared for the maintenance field. If so, the override will be used for
140: * the quickfinder and lookup utils will not be called. If no override was given, LookupUtils.setFieldQuickfinder will be called
141: * to set the system generated quickfinder based on the attributes relationship to the parent business object.
142: *
143: * @return Field with quickfinder set if one was found
144: */
145: public static final Field setFieldQuickfinder(
146: BusinessObject businessObject, String attributeName,
147: MaintainableFieldDefinition maintainableFieldDefinition,
148: Field field, List displayedFieldNames,
149: SelectiveReferenceRefresher srr) {
150: if (maintainableFieldDefinition.getOverrideLookupClass() != null
151: && StringUtils.isNotBlank(maintainableFieldDefinition
152: .getOverrideFieldConversions())) {
153: field
154: .setQuickFinderClassNameImpl(maintainableFieldDefinition
155: .getOverrideLookupClass().getName());
156: field.setFieldConversions(maintainableFieldDefinition
157: .getOverrideFieldConversions());
158: return field;
159: }
160:
161: return LookupUtils.setFieldQuickfinder(businessObject,
162: attributeName, field, displayedFieldNames, srr);
163: }
164:
165: /**
166: * Given a section, returns a comma delimited string of all fields, representing the error keys that exist for a section
167: *
168: * @param section a section
169: * @return
170: */
171: public static String generateErrorKeyForSection(Section section) {
172: Set<String> fieldPropertyNames = new HashSet<String>();
173: addRowsToErrorKeySet(section.getRows(), fieldPropertyNames);
174:
175: StringBuilder buf = new StringBuilder();
176: Iterator<String> nameIter = fieldPropertyNames.iterator();
177: while (nameIter.hasNext()) {
178: buf.append(nameIter.next());
179: if (nameIter.hasNext()) {
180: buf.append(",");
181: }
182: }
183:
184: if (section.getContainedCollectionNames() != null
185: && section.getContainedCollectionNames().size() > 0) {
186: buf.append(",");
187:
188: Iterator<String> collectionIter = section
189: .getContainedCollectionNames().iterator();
190: while (collectionIter.hasNext()) {
191: buf.append(RiceConstants.MAINTENANCE_NEW_MAINTAINABLE
192: + collectionIter.next());
193: if (collectionIter.hasNext()) {
194: buf.append(",");
195: }
196: }
197: }
198:
199: return buf.toString();
200: }
201:
202: /**
203: * 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
204: * with Constants.MAINTENANCE_NEW_MAINTAINABLE
205: *
206: * @see RiceConstants#MAINTENANCE_NEW_MAINTAINABLE
207: * @param listOfRows
208: * @param errorKeys
209: */
210: protected static void addRowsToErrorKeySet(List<Row> listOfRows,
211: Set<String> errorKeys) {
212: if (listOfRows == null) {
213: return;
214: }
215: for (Row row : listOfRows) {
216: List<Field> fields = row.getFields();
217: if (fields == null) {
218: continue;
219: }
220: for (Field field : fields) {
221: String fieldPropertyName = field.getPropertyName();
222: if (fieldPropertyName != null
223: && fieldPropertyName
224: .startsWith(RiceConstants.MAINTENANCE_NEW_MAINTAINABLE)) {
225: errorKeys.add(field.getPropertyName());
226: }
227: addRowsToErrorKeySet(field.getContainerRows(),
228: errorKeys);
229: }
230: }
231: }
232: }
|