001: /*
002: * Copyright 2006-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.lookup;
017:
018: import java.util.ArrayList;
019: import java.util.Collection;
020: import java.util.Comparator;
021: import java.util.HashMap;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.Set;
027: import java.util.StringTokenizer;
028:
029: import org.apache.commons.lang.StringUtils;
030: import org.apache.ojb.broker.query.Criteria;
031: import org.kuali.RiceConstants;
032: import org.kuali.core.bo.BusinessObject;
033: import org.kuali.core.bo.BusinessObjectRelationship;
034: import org.kuali.core.bo.PersistableBusinessObject;
035: import org.kuali.core.datadictionary.RelationshipDefinition;
036: import org.kuali.core.datadictionary.control.ControlDefinition;
037: import org.kuali.core.dbplatform.KualiDBPlatform;
038: import org.kuali.core.exceptions.ClassNotPersistableException;
039: import org.kuali.core.exceptions.UnknownBusinessClassAttributeException;
040: import org.kuali.core.service.BusinessObjectDictionaryService;
041: import org.kuali.core.service.BusinessObjectMetaDataService;
042: import org.kuali.core.service.DataDictionaryService;
043: import org.kuali.core.service.PersistenceStructureService;
044: import org.kuali.core.util.ObjectUtils;
045: import org.kuali.core.web.comparator.NullValueComparator;
046: import org.kuali.core.web.ui.Field;
047: import org.kuali.core.web.ui.ResultRow;
048: import org.kuali.rice.KNSServiceLocator;
049:
050: /**
051: * This is a static utility class for Lookup related utilities and helper methods.
052: *
053: *
054: */
055: public class LookupUtils {
056: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
057: .getLogger(LookupUtils.class);
058:
059: private static DataDictionaryService dataDictionaryService;
060: private static PersistenceStructureService persistenceStructureService;
061: private static BusinessObjectDictionaryService businessObjectDictionaryService;
062: private static BusinessObjectMetaDataService businessObjectMetaDataService;
063:
064: public LookupUtils() {
065: // default constructor for Spring to call to start up initialization process
066: }
067:
068: public void setBusinessObjectDictionaryService(
069: BusinessObjectDictionaryService businessObjectDictionaryService) {
070: LookupUtils.businessObjectDictionaryService = businessObjectDictionaryService;
071: }
072:
073: public void setDataDictionaryService(DataDictionaryService ddService) {
074: LookupUtils.dataDictionaryService = ddService;
075: }
076:
077: public void setPersistenceStructureService(
078: PersistenceStructureService persistenceStructureService) {
079: LookupUtils.persistenceStructureService = persistenceStructureService;
080: }
081:
082: /**
083: * Sets the businessObjectMetaDataService attribute value.
084: * @param businessObjectMetaDataService The businessObjectMetaDataService to set.
085: */
086: public void setBusinessObjectMetaDataService(
087: BusinessObjectMetaDataService businessObjectMetaDataService) {
088: LookupUtils.businessObjectMetaDataService = businessObjectMetaDataService;
089: }
090:
091: /**
092: *
093: * This method uses the DataDictionary to determine whether to force uppercase the value, and if it should, then it does the
094: * uppercase, and returns the upper-cased value.
095: *
096: * @param boClass Parent BO class that the fieldName is a member of.
097: * @param fieldName Name of the field to be forced to uppercase.
098: * @param fieldValue Value of the field that may be uppercased.
099: * @return The correctly uppercased fieldValue if it should be uppercased, otherwise fieldValue is returned unchanged.
100: *
101: */
102: public static String forceUppercase(Class boClass,
103: String fieldName, String fieldValue) {
104:
105: // short-circuit to exit if there isnt enough information to do the forceUppercase
106: if (StringUtils.isBlank(fieldValue)) {
107: return fieldValue;
108: }
109:
110: // parameter validation
111: if (boClass == null) {
112: throw new IllegalArgumentException(
113: "Parameter boClass passed in with null value.");
114: } else if (!BusinessObject.class.isAssignableFrom(boClass)) {
115: throw new IllegalArgumentException(
116: "Parameter boClass value passed in ["
117: + boClass.getName() + "] "
118: + "was not a descendent of BusinessObject.");
119: }
120: if (StringUtils.isBlank(fieldName)) {
121: throw new IllegalArgumentException(
122: "Parameter fieldName passed in with empty value.");
123: }
124:
125: if (!dataDictionaryService.isAttributeDefined(boClass,
126: fieldName)) {
127: return fieldValue;
128: }
129:
130: boolean forceUpperCase = false;
131: try {
132: forceUpperCase = dataDictionaryService
133: .getAttributeForceUppercase(boClass, fieldName)
134: .booleanValue();
135: } catch (UnknownBusinessClassAttributeException ubae) {
136: // do nothing, dont alter the fieldValue
137: }
138: if (forceUpperCase) {
139: return fieldValue.toUpperCase();
140: }
141: return fieldValue;
142: }
143:
144: /**
145: *
146: * This method uses the DataDictionary to determine whether to force uppercase the values, and if it should, then it does the
147: * uppercase, and returns the upper-cased Map of fieldname/fieldValue pairs.
148: *
149: * @param boClass Parent BO class that the fieldName is a member of.
150: * @param fieldValues A Map<String,String> where the key is the fieldName and the value is the fieldValue.
151: * @return The same Map is returned, with the appropriate values uppercased (if any).
152: *
153: */
154: public static Map<String, String> forceUppercase(Class boClass,
155: Map<String, String> fieldValues) {
156: if (boClass == null) {
157: throw new IllegalArgumentException(
158: "Parameter boClass passed in with null value.");
159: } else if (!PersistableBusinessObject.class
160: .isAssignableFrom(boClass)) {
161: throw new IllegalArgumentException(
162: "Parameter boClass value passed in ["
163: + boClass.getName() + "] "
164: + "was not a descendent of BusinessObject.");
165: }
166: if (fieldValues == null) {
167: throw new IllegalArgumentException(
168: "Parameter fieldValues passed in with null value.");
169: }
170:
171: for (String fieldName : fieldValues.keySet()) {
172: fieldValues.put(fieldName, LookupUtils.forceUppercase(
173: boClass, fieldName, (String) fieldValues
174: .get(fieldName)));
175: }
176: return fieldValues;
177: }
178:
179: public static void applySearchResultsLimit(Criteria criteria,
180: KualiDBPlatform platform) {
181: Integer limit = getApplicationSearchResultsLimit();
182: if (limit != null) {
183: platform.applyLimit(limit, criteria);
184: }
185: }
186:
187: public static Integer getApplicationSearchResultsLimit() {
188: String limitString = KNSServiceLocator
189: .getKualiConfigurationService()
190: .getParameterValue(
191: RiceConstants.KNS_NAMESPACE,
192: RiceConstants.DetailTypes.LOOKUP_PARM_DETAIL_TYPE,
193: RiceConstants.SystemGroupParameterNames.LOOKUP_RESULTS_LIMIT);
194: if (limitString != null) {
195: return Integer.valueOf(limitString);
196: }
197: return null;
198: }
199:
200: /**
201: * This method the maximum rows per page in a multiple value lookup
202: *
203: * @see org.kuali.RiceConstants.SystemGroupParameterNames#MULTIPLE_VALUE_LOOKUP_RESULTS_PER_PAGE
204: * @return
205: */
206: public static Integer getApplicationMaximumSearchResulsPerPageForMultipleValueLookups() {
207: String limitString = KNSServiceLocator
208: .getKualiConfigurationService()
209: .getParameterValue(
210: RiceConstants.KNS_NAMESPACE,
211: RiceConstants.DetailTypes.LOOKUP_PARM_DETAIL_TYPE,
212: RiceConstants.SystemGroupParameterNames.MULTIPLE_VALUE_LOOKUP_RESULTS_PER_PAGE);
213: if (limitString != null) {
214: return Integer.valueOf(limitString);
215: }
216: return null;
217: }
218:
219: /**
220: * This makes a , delimited String list of fields separated by a , into a List of target --> lookup readOnlyFieldsList.
221: *
222: * @param readOnlyFields
223: * @return the List representation of the readOnlyFields String provided.
224: */
225: public static List<String> translateReadOnlyFieldsToList(
226: String readOnlyFieldsString) {
227: List<String> readOnlyFieldsList = new ArrayList<String>();
228: if (StringUtils.isNotEmpty(readOnlyFieldsString)) {
229: if (readOnlyFieldsString.indexOf(",") > 0) {
230: StringTokenizer token = new StringTokenizer(
231: readOnlyFieldsString, ",");
232: while (token.hasMoreTokens()) {
233: String element = token.nextToken();
234: readOnlyFieldsList.add(element);
235: }
236: } else {
237: readOnlyFieldsList.add(readOnlyFieldsString);
238: }
239: }
240: return readOnlyFieldsList;
241: }
242:
243: /**
244: * This translates a , delimited list of pairs separated by a : into a Map of target --> lookup field conversions.
245: *
246: * @param conversionFields
247: * @return the Map representation of the fieldConversions String provided.
248: */
249: public static Map translateFieldConversions(
250: String fieldConversionsString) {
251: Map fieldConversionsMap = new HashMap();
252: if (StringUtils.isNotEmpty(fieldConversionsString)) {
253: if (fieldConversionsString.indexOf(",") > 0) {
254: StringTokenizer token = new StringTokenizer(
255: fieldConversionsString, ",");
256: while (token.hasMoreTokens()) {
257: String element = token.nextToken();
258: fieldConversionsMap.put(element.substring(0,
259: element.indexOf(":")), element
260: .substring(element.indexOf(":") + 1));
261: }
262: } else {
263: fieldConversionsMap.put(fieldConversionsString
264: .substring(0, fieldConversionsString
265: .indexOf(":")),
266: fieldConversionsString
267: .substring(fieldConversionsString
268: .indexOf(":") + 1));
269: }
270: }
271: return fieldConversionsMap;
272: }
273:
274: public static Field setFieldQuickfinder(
275: BusinessObject businessObject, String attributeName,
276: Field field, List displayedFieldNames) {
277: return setFieldQuickfinder(businessObject, (String) null,
278: false, 0, attributeName, field, displayedFieldNames);
279: }
280:
281: public static Field setFieldQuickfinder(
282: BusinessObject businessObject, String attributeName,
283: Field field, List displayedFieldNames,
284: SelectiveReferenceRefresher srr) {
285: return setFieldQuickfinder(businessObject, (String) null,
286: false, 0, attributeName, field, displayedFieldNames,
287: srr);
288: }
289:
290: /**
291: * Sets a fields quickfinder class and field conversions for an attribute.
292: */
293: public static Field setFieldQuickfinder(
294: BusinessObject businessObject, String collectionName,
295: boolean addLine, int index, String attributeName,
296: Field field, List displayedFieldNames,
297: SelectiveReferenceRefresher srr) {
298: field = setFieldQuickfinder(businessObject, collectionName,
299: addLine, index, attributeName, field,
300: displayedFieldNames);
301: if (srr != null) {
302: String collectionPrefix = "";
303: if (collectionName != null) {
304: if (addLine) {
305: collectionPrefix = RiceConstants.MAINTENANCE_ADD_PREFIX
306: + collectionName + ".";
307: } else {
308: collectionPrefix = collectionName + "[" + index
309: + "].";
310: }
311: }
312: field
313: .setReferencesToRefresh(convertReferencesToSelectCollectionToString(srr
314: .getAffectedReferencesFromLookup(
315: businessObject, attributeName,
316: collectionPrefix)));
317: }
318: return field;
319: }
320:
321: /**
322: * Sets a fields quickfinder class and field conversions for an attribute.
323: */
324: public static Field setFieldQuickfinder(
325: BusinessObject businessObject, String collectionName,
326: boolean addLine, int index, String attributeName,
327: Field field, List displayedFieldNames) {
328: if (businessObject == null) {
329: return field;
330: }
331:
332: Boolean noLookupField = businessObjectDictionaryService
333: .noLookupFieldLookup(businessObject.getClass(),
334: attributeName);
335: if (noLookupField != null && noLookupField.booleanValue()) {
336: return field;
337: }
338:
339: BusinessObjectRelationship relationship = null;
340: if (LOG.isDebugEnabled()) {
341: LOG.debug("setFieldQuickfinder("
342: + businessObject.getClass().getName() + ","
343: + attributeName + "," + field + ","
344: + displayedFieldNames + ")");
345: }
346:
347: relationship = businessObjectMetaDataService
348: .getBusinessObjectRelationship(businessObject,
349: businessObject.getClass(), attributeName, "",
350: false);
351:
352: String collectionPrefix = "";
353: if (collectionName != null) {
354: if (addLine) {
355: collectionPrefix = RiceConstants.MAINTENANCE_ADD_PREFIX
356: + collectionName + ".";
357: } else {
358: collectionPrefix = collectionName + "[" + index + "].";
359: }
360: }
361:
362: if (relationship == null) {
363: Class c = ObjectUtils.getPropertyType(businessObject,
364: attributeName, persistenceStructureService);
365:
366: if (c != null) {
367: if (attributeName.contains(".")) {
368: attributeName = StringUtils.substringBeforeLast(
369: attributeName, ".");
370: }
371:
372: RelationshipDefinition ddReference = businessObjectMetaDataService
373: .getBusinessObjectRelationshipDefinition(
374: businessObject, attributeName);
375: relationship = businessObjectMetaDataService
376: .getBusinessObjectRelationship(ddReference,
377: businessObject, businessObject
378: .getClass(), attributeName, "",
379: false);
380: if (relationship != null) {
381: field.setQuickFinderClassNameImpl(relationship
382: .getRelatedClass().getName());
383: field.setFieldConversions(generateFieldConversions(
384: businessObject, collectionPrefix,
385: relationship, field.getPropertyPrefix(),
386: displayedFieldNames, null));
387: field.setLookupParameters(generateLookupParameters(
388: businessObject, collectionPrefix,
389: relationship, field.getPropertyPrefix(),
390: displayedFieldNames, null));
391: }
392: }
393:
394: return field;
395: }
396: if (ObjectUtils.isNestedAttribute(attributeName)) {
397: //first determine the prefix and the attribute we are referring to
398: String nestedAttributePrefix = StringUtils
399: .substringBeforeLast(attributeName, ".");
400: PersistableBusinessObject childBO = (PersistableBusinessObject) getNestedBusinessObject(
401: businessObject, attributeName);
402:
403: field.setQuickFinderClassNameImpl(relationship
404: .getRelatedClass().getName());
405: field.setFieldConversions(generateFieldConversions(
406: businessObject, collectionPrefix, relationship,
407: field.getPropertyPrefix(), displayedFieldNames,
408: nestedAttributePrefix));
409: field.setLookupParameters(generateLookupParameters(
410: businessObject, collectionPrefix, relationship,
411: field.getPropertyPrefix(), displayedFieldNames,
412: nestedAttributePrefix));
413: } else {
414: field.setQuickFinderClassNameImpl(relationship
415: .getRelatedClass().getName());
416: field.setFieldConversions(generateFieldConversions(
417: businessObject, collectionPrefix, relationship,
418: field.getPropertyPrefix(), displayedFieldNames,
419: null));
420: field.setLookupParameters(generateLookupParameters(
421: businessObject, collectionPrefix, relationship,
422: field.getPropertyPrefix(), displayedFieldNames,
423: null));
424: }
425:
426: return field;
427: }
428:
429: public static Map getPrimitiveReference(
430: BusinessObject businessObject, String attributeName) {
431: Map chosenReferenceByKeySize = new HashMap();
432: Map chosenReferenceByFieldName = new HashMap();
433:
434: Map referenceClasses = null;
435:
436: try {
437: referenceClasses = persistenceStructureService
438: .getReferencesForForeignKey(businessObject
439: .getClass(), attributeName);
440: } catch (ClassNotPersistableException ex) {
441: // do nothing, there is no quickfinder
442: }
443:
444: // if field is not fk to any reference class, return field object w no quickfinder
445: if (referenceClasses == null || referenceClasses.isEmpty()) {
446: return chosenReferenceByKeySize;
447: }
448:
449: /*
450: * if field is fk to more than one reference, take the class with the least # of pk fields, this should give the correct
451: * grain for the attribute
452: */
453: int minKeys = Integer.MAX_VALUE;
454: for (Iterator iter = referenceClasses.keySet().iterator(); iter
455: .hasNext();) {
456: String attr = (String) iter.next();
457: Class clazz = (Class) referenceClasses.get(attr);
458: List pkNames = persistenceStructureService
459: .listPrimaryKeyFieldNames(clazz);
460:
461: // Compare based on key size.
462: if (pkNames.size() < minKeys) {
463: minKeys = pkNames.size();
464: chosenReferenceByKeySize.clear();
465: chosenReferenceByKeySize.put(attr, clazz);
466: }
467:
468: // Compare based on field name.
469: if (attributeName.startsWith(attr)) {
470: chosenReferenceByFieldName.clear();
471: chosenReferenceByFieldName.put(attr, clazz);
472: }
473: }
474:
475: // If a compatible key was found based on field names, prefer it, otherwise use choice by key size.
476: return chosenReferenceByFieldName.isEmpty() ? chosenReferenceByKeySize
477: : chosenReferenceByFieldName;
478: }
479:
480: /**
481: *
482: * This method walks through the nested attribute and finds the last business object in the chain and returns it (excluding the
483: * last parameter which is the actual attribute)
484: *
485: * @param attributeName
486: * @return
487: */
488: public static BusinessObject getNestedBusinessObject(
489: BusinessObject bo, String attributeName) {
490: String[] nestedAttributes = StringUtils.split(attributeName,
491: ".");
492:
493: BusinessObject childBO = null;
494: String attributeRefName = "";
495: Class clazz = null;
496: if (nestedAttributes.length > 1) {
497: String attributeStringSoFar = "";
498: for (int i = 0; i < nestedAttributes.length - 1; i++) {
499: // we need to build a string of the attribute names depending on which iteration we're in.
500: // so if the original attributeName string we're using is "a.b.c.d.e", then first iteration would use
501: // "a", 2nd "a.b", 3rd "a.b.c", etc.
502: if (i != 0) {
503: attributeStringSoFar = attributeStringSoFar + ".";
504: }
505: attributeStringSoFar = attributeStringSoFar
506: + nestedAttributes[i];
507:
508: clazz = ObjectUtils.getPropertyType(bo,
509: attributeStringSoFar,
510: persistenceStructureService);
511:
512: if (clazz != null
513: && BusinessObject.class.isAssignableFrom(clazz)) {
514: try {
515: childBO = (BusinessObject) clazz.newInstance();
516: } catch (Exception e) {
517: return null;
518: }
519: }
520: }
521: }
522: return childBO;
523: }
524:
525: public static Class getNestedReferenceClass(
526: BusinessObject businessObject, String attributeName) {
527: BusinessObject bo = getNestedBusinessObject(businessObject,
528: attributeName);
529: return null == bo ? null : bo.getClass();
530: }
531:
532: private static String generateFieldConversions(
533: BusinessObject businessObject, String collectionName,
534: BusinessObjectRelationship relationship,
535: String propertyPrefix, List displayedFieldNames,
536: String nestedObjectPrefix) {
537: String fieldConversions = "";
538:
539: if (LOG.isDebugEnabled()) {
540: LOG.debug("generateFieldConversions("
541: + businessObject.getClass().getName() + ","
542: + collectionName + ",\n" + relationship + "\n,"
543: + propertyPrefix + "," + displayedFieldNames + ","
544: + nestedObjectPrefix + ")");
545: }
546:
547: // get the references for the given property
548: for (Map.Entry<String, String> entry : relationship
549: .getParentToChildReferences().entrySet()) {
550: String fromField = entry.getValue();
551: String toField = entry.getKey();
552:
553: // find the displayed to field mapping
554: if (!displayedFieldNames.contains(toField)) {
555: toField = translateToDisplayedField(businessObject
556: .getClass(), toField, displayedFieldNames);
557: }
558:
559: if (StringUtils.isNotBlank(fieldConversions)) {
560: fieldConversions += ",";
561: }
562:
563: if (StringUtils.isNotEmpty(propertyPrefix)) {
564: toField = propertyPrefix + "." + toField;
565: }
566:
567: if (StringUtils.isNotEmpty(collectionName)) {
568: toField = collectionName + toField;
569: }
570:
571: fieldConversions += fromField + ":" + toField;
572: }
573:
574: return fieldConversions;
575: }
576:
577: private static String generateLookupParameters(
578: BusinessObject businessObject, String collectionName,
579: BusinessObjectRelationship relationship,
580: String propertyPrefix, List displayedFieldNames,
581: String nestedObjectPrefix) {
582:
583: String lookupParameters = "";
584:
585: List displayedQFFieldNames = businessObjectDictionaryService
586: .getLookupFieldNames(relationship.getRelatedClass());
587: for (Map.Entry<String, String> entry : relationship
588: .getParentToChildReferences().entrySet()) {
589: String fromField = entry.getKey();
590: String toField = entry.getValue();
591:
592: if (relationship.getUserVisibleIdentifierKey() == null
593: || relationship.getUserVisibleIdentifierKey()
594: .equals(fromField)) {
595: // find the displayed from field mapping
596: if (!displayedFieldNames.contains(fromField)) {
597: fromField = translateToDisplayedField(
598: businessObject.getClass(), fromField,
599: displayedFieldNames);
600: }
601:
602: // translate to field
603: if (displayedQFFieldNames != null
604: && !displayedQFFieldNames.contains(toField)) {
605: toField = translateToDisplayedField(relationship
606: .getRelatedClass(), toField,
607: displayedQFFieldNames);
608: }
609:
610: if (StringUtils.isNotBlank(lookupParameters)) {
611: lookupParameters += ",";
612: }
613:
614: if (propertyPrefix != null
615: && !propertyPrefix.equals("")) {
616: fromField = propertyPrefix + "." + fromField;
617: }
618:
619: if (StringUtils.isNotEmpty(collectionName)) {
620: fromField = collectionName + fromField;
621: }
622:
623: lookupParameters += fromField + ":" + toField;
624: }
625: }
626:
627: return lookupParameters;
628: }
629:
630: private static String translateToDisplayedField(
631: Class businessObjectClass, String fieldName,
632: List displayedFieldNames) {
633: if (PersistableBusinessObject.class
634: .isAssignableFrom(businessObjectClass)) {
635: Map nestedFkMap = persistenceStructureService
636: .getNestedForeignKeyMap(businessObjectClass);
637:
638: // translate to primitive fk if nested
639: /*
640: * if (ObjectUtils.isNestedAttribute(fieldName) && nestedFkMap.containsKey(fieldName)) { fieldName = (String)
641: * nestedFkMap.get(fieldName); }
642: */
643:
644: if (!displayedFieldNames.contains(fieldName)) {
645: for (Iterator iterator = displayedFieldNames.iterator(); iterator
646: .hasNext();) {
647: String dispField = (String) iterator.next();
648:
649: if (nestedFkMap.containsKey(dispField)
650: && nestedFkMap.get(dispField).equals(
651: fieldName)) {
652: fieldName = dispField;
653: }
654: }
655: }
656: }
657:
658: return fieldName;
659: }
660:
661: public static String convertReferencesToSelectCollectionToString(
662: Collection<String> referencesToRefresh) {
663: StringBuilder buf = new StringBuilder();
664: for (String reference : referencesToRefresh) {
665: buf.append(reference).append(
666: RiceConstants.REFERENCES_TO_REFRESH_SEPARATOR);
667: }
668: if (!referencesToRefresh.isEmpty()) {
669: // we appended one too many separators, remove it
670: buf.delete(buf.length()
671: - RiceConstants.REFERENCES_TO_REFRESH_SEPARATOR
672: .length(), buf.length());
673: }
674: return buf.toString();
675: }
676:
677: public static String convertSetOfObjectIdsToString(
678: Set<String> objectIds) {
679: if (objectIds.isEmpty()) {
680: return "";
681: }
682: StringBuilder buf = new StringBuilder();
683: for (String objectId : objectIds) {
684: if (objectId
685: .contains(RiceConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR)) {
686: throw new RuntimeException("object ID " + objectId
687: + " contains the selected obj ID separator");
688: }
689: buf
690: .append(objectId)
691: .append(
692: RiceConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR);
693: }
694: // added one extra separator, remove it
695: buf.delete(buf.length()
696: - RiceConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR
697: .length(), buf.length());
698:
699: return buf.toString();
700: }
701:
702: public static Set<String> convertStringOfObjectIdsToSet(
703: String objectIdsString) {
704: Set<String> set = new HashSet<String>();
705:
706: if (StringUtils.isNotBlank(objectIdsString)) {
707: String[] objectIds = StringUtils
708: .splitByWholeSeparator(
709: objectIdsString,
710: RiceConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR);
711: for (String objectId : objectIds) {
712: set.add(objectId);
713: }
714: }
715: return set;
716: }
717:
718: /**
719: * Given a list of results from a lookup, determines the best comparator to use on the String values of each of these columns
720: *
721: * This method exists because each cell (represented by the Column object) lists the comparator that should be used within it based on the property value class,
722: * so we gotta go thru the whole list and determine the best comparator to use
723: *
724: * @param resultsTable
725: * @param column
726: * @return
727: */
728: public static Comparator findBestValueComparatorForColumn(
729: List<ResultRow> resultTable, int column) {
730: // BIG HACK
731: Comparator comp = NullValueComparator.getInstance();
732: for (ResultRow row : resultTable) {
733: Comparator tempComp = row.getColumns().get(column)
734: .getValueComparator();
735: if (tempComp != null
736: && !NullValueComparator.class.equals(tempComp
737: .getClass())) {
738: return tempComp;
739: }
740: }
741: return comp;
742: }
743:
744: /**
745: * Given 3 sets of object IDs: the set of selected object IDs before rendering the current page,
746: * the set of object IDs rendered on the page, and the set of object IDs selected on the page, computes
747: * the total set of selected object IDs.
748: *
749: * Instead of storing it in a set, returns it in a map with the selected object ID as both the key and value
750: * @param previouslySelectedObjectIds
751: * @param displayedObjectIds
752: * @param selectedObjectIds
753: * @return
754: */
755: public static Map<String, String> generateCompositeSelectedObjectIds(
756: Set<String> previouslySelectedObjectIds,
757: Set<String> displayedObjectIds,
758: Set<String> selectedObjectIds) {
759: Map<String, String> tempMap = new HashMap<String, String>();
760: // Equivalent to the set operation:
761: // (P - D) union C, where - is the set difference operator
762: // P is the list of object IDs previously passed in, D is the set of displayed object IDs, and C is the set of checked obj IDs
763: // since HTML does not pass a value for non-selected dcheckboxes
764:
765: // first build a map w/ all the previouslySelectedObjectIds as keys
766: for (String previouslySelectedObjectId : previouslySelectedObjectIds) {
767: tempMap.put(previouslySelectedObjectId,
768: previouslySelectedObjectId);
769: }
770: // then remove all the displayed elements (any selected displayed elements will be added back in the next loop)
771: for (String displayedObjectId : displayedObjectIds) {
772: tempMap.remove(displayedObjectId);
773: }
774: // put back the selected IDs
775: for (String selectedObjectId : selectedObjectIds) {
776: tempMap.put(selectedObjectId, selectedObjectId);
777: }
778: return tempMap;
779: }
780:
781: /**
782: * Removes fields idenfied in the data dictionary as hidden from the lookup field values.
783: * (This will remove Universal User ID and Person name from search requests when a user ID is entered.)
784: *
785: * @param fieldValues
786: */
787: public static void removeHiddenCriteriaFields(
788: Class businessObjectClass, Map fieldValues) {
789: List<String> lookupFieldAttributeList = businessObjectMetaDataService
790: .getLookupableFieldNames(businessObjectClass);
791: if (lookupFieldAttributeList != null) {
792: for (Iterator iter = lookupFieldAttributeList.iterator(); iter
793: .hasNext();) {
794: String attributeName = (String) iter.next();
795: if (fieldValues.containsKey(attributeName)) {
796: ControlDefinition controlDef = dataDictionaryService
797: .getAttributeControlDefinition(
798: businessObjectClass, attributeName);
799: if (controlDef.isHidden()) {
800: fieldValues.remove(attributeName);
801: }
802: }
803: }
804: }
805: }
806: }
|