001 /**
002 * Copyright 2005-2012 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.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/ecl2.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.rice.kns.lookup;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.apache.ojb.broker.query.Criteria;
020 import org.kuali.rice.core.api.search.Range;
021 import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
022 import org.kuali.rice.core.api.CoreApiServiceLocator;
023 import org.kuali.rice.core.api.config.property.ConfigurationService;
024 import org.kuali.rice.core.api.datetime.DateTimeService;
025 import org.kuali.rice.core.framework.persistence.platform.DatabasePlatform;
026 import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
027 import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
028 import org.kuali.rice.kns.service.KNSServiceLocator;
029 import org.kuali.rice.kns.util.KNSGlobalVariables;
030 import org.kuali.rice.kns.web.comparator.NullValueComparator;
031 import org.kuali.rice.kns.web.struts.form.KualiForm;
032 import org.kuali.rice.kns.web.struts.form.LookupForm;
033 import org.kuali.rice.kns.web.ui.Field;
034 import org.kuali.rice.kns.web.ui.ResultRow;
035 import org.kuali.rice.krad.bo.BusinessObject;
036 import org.kuali.rice.krad.bo.DataObjectRelationship;
037 import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
038 import org.kuali.rice.krad.datadictionary.control.ControlDefinition;
039 import org.kuali.rice.krad.exception.ClassNotPersistableException;
040 import org.kuali.rice.krad.lookup.SelectiveReferenceRefresher;
041 import org.kuali.rice.krad.service.DataDictionaryService;
042 import org.kuali.rice.krad.service.KRADServiceLocator;
043 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
044 import org.kuali.rice.krad.service.PersistenceStructureService;
045 import org.kuali.rice.krad.util.KRADConstants;
046 import org.kuali.rice.krad.util.ObjectUtils;
047
048 import java.util.ArrayList;
049 import java.util.Collection;
050 import java.util.Comparator;
051 import java.util.HashMap;
052 import java.util.HashSet;
053 import java.util.Iterator;
054 import java.util.List;
055 import java.util.Map;
056 import java.util.Set;
057 import java.util.StringTokenizer;
058
059 /**
060 * Utility class for Lookup related utilities and helper methods.
061 */
062 public class LookupUtils {
063 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupUtils.class);
064
065 public LookupUtils() {
066 // default constructor for Spring to call to start up initialization process
067 }
068
069 /**
070 * Removes fields identified in the data dictionary as hidden from the lookup field values.
071 * (This will remove Universal User ID and Person name from search requests when a user ID is entered.)
072 *
073 * @param fieldValues
074 */
075 public static void removeHiddenCriteriaFields(Class businessObjectClass, Map fieldValues) {
076 List<String> lookupFieldAttributeList =
077 getBusinessObjectMetaDataService().getLookupableFieldNames(businessObjectClass);
078 if (lookupFieldAttributeList != null) {
079 for (Iterator iter = lookupFieldAttributeList.iterator(); iter.hasNext(); ) {
080 String attributeName = (String) iter.next();
081 if (fieldValues.containsKey(attributeName)) {
082 ControlDefinition controlDef = getDataDictionaryService()
083 .getAttributeControlDefinition(businessObjectClass, attributeName);
084 if (controlDef != null && controlDef.isHidden()) {
085 fieldValues.remove(attributeName);
086 }
087 }
088 }
089 }
090 }
091
092 /**
093 * Parses and returns the lookup result set limit, checking first for the limit
094 * for the BO being looked up, and then the global application limit if there isn't a limit
095 * specific to this BO.
096 *
097 * @param businessObjectClass BO class to search on / get limit for. If the passed in type is not of type
098 * {@link org.kuali.rice.krad.bo.BusinessObject}, then the application-wide default limit is used.
099 * @return result set limit (or null if there isn't one)
100 */
101 public static Integer getSearchResultsLimit(Class businessObjectClass) {
102 Integer limit = null;
103 if (BusinessObject.class.isAssignableFrom(businessObjectClass)) {
104 limit = getBusinessObjectSearchResultsLimit(businessObjectClass);
105 }
106 if (limit == null) {
107 limit = getApplicationSearchResultsLimit();
108 }
109 return limit;
110 }
111
112 /**
113 *
114 */
115 public static Integer getApplicationSearchResultsLimit() {
116 String limitString = CoreFrameworkServiceLocator.getParameterService()
117 .getParameterValueAsString(KRADConstants.KNS_NAMESPACE,
118 KRADConstants.DetailTypes.LOOKUP_PARM_DETAIL_TYPE,
119 KRADConstants.SystemGroupParameterNames.LOOKUP_RESULTS_LIMIT);
120 if (limitString != null) {
121 return Integer.valueOf(limitString);
122 }
123 return null;
124 }
125
126 /**
127 * Parses and returns the lookup result set limit for the passed in BO (if one exists)
128 *
129 * @param businessObjectClass
130 * @return result set limit for this BO (or null if the BO doesn't have a limit)
131 */
132 public static Integer getBusinessObjectSearchResultsLimit(Class businessObjectClass) {
133 if (!(isMultipleValueLookup())) {
134 return getBusinessObjectDictionaryService().getLookupResultSetLimit(businessObjectClass);
135 } else {
136 return getBusinessObjectDictionaryService().getMultipleValueLookupResultSetLimit(businessObjectClass);
137 }
138 }
139
140 private static boolean isMultipleValueLookup() {
141 KualiForm kualiForm = KNSGlobalVariables.getKualiForm();
142 if (kualiForm instanceof LookupForm) {
143 LookupForm lookupForm = (LookupForm) kualiForm;
144 return lookupForm.isMultipleValues();
145 } else {
146 return false;
147 }
148 }
149
150 /**
151 * This method applies the search results limit to the search criteria for this BO
152 *
153 * @param businessObjectClass BO class to search on / get limit for
154 * @param criteria search criteria
155 * @param platform database platform
156 */
157 public static void applySearchResultsLimit(Class businessObjectClass, Criteria criteria,
158 DatabasePlatform platform) {
159 Integer limit = getSearchResultsLimit(businessObjectClass);
160 if (limit != null) {
161 platform.applyLimit(limit, criteria);
162 }
163 }
164
165 /**
166 * Applies the search results limit to the search criteria for this BO (JPA)
167 *
168 * @param businessObjectClass BO class to search on / get limit for
169 * @param criteria search criteria
170 */
171 public static void applySearchResultsLimit(Class businessObjectClass,
172 org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria criteria) {
173 Integer limit = getSearchResultsLimit(businessObjectClass);
174 if (limit != null) {
175 criteria.setSearchLimit(limit);
176 }
177 }
178
179 /**
180 * This method the maximum rows per page in a multiple value lookup
181 *
182 * @see org.kuali.KRADConstants.SystemGroupParameterNames#MULTIPLE_VALUE_LOOKUP_RESULTS_PER_PAGE
183 * @return
184 */
185 public static Integer getApplicationMaximumSearchResulsPerPageForMultipleValueLookups() {
186 String limitString = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(KRADConstants.KNS_NAMESPACE, KRADConstants.DetailTypes.LOOKUP_PARM_DETAIL_TYPE, KRADConstants.SystemGroupParameterNames.MULTIPLE_VALUE_LOOKUP_RESULTS_PER_PAGE);
187 if (limitString != null) {
188 return Integer.valueOf(limitString);
189 }
190 return null;
191 }
192
193 /**
194 * This makes a , delimited String list of fields separated by a , into a List of target --> lookup readOnlyFieldsList.
195 *
196 * @param readOnlyFields
197 * @return the List representation of the readOnlyFields String provided.
198 */
199 public static List<String> translateReadOnlyFieldsToList(String readOnlyFieldsString) {
200 List<String> readOnlyFieldsList = new ArrayList<String>();
201 if (StringUtils.isNotEmpty(readOnlyFieldsString)) {
202 if (readOnlyFieldsString.indexOf(",") > 0) {
203 StringTokenizer token = new StringTokenizer(readOnlyFieldsString, ",");
204 while (token.hasMoreTokens()) {
205 String element = token.nextToken();
206 readOnlyFieldsList.add(element);
207 }
208 }
209 else {
210 readOnlyFieldsList.add(readOnlyFieldsString);
211 }
212 }
213 return readOnlyFieldsList;
214 }
215
216 /**
217 * This translates a , delimited list of pairs separated by a : into a Map of target --> lookup field conversions.
218 *
219 * @param conversionFields
220 * @return the Map representation of the fieldConversions String provided.
221 */
222 public static Map translateFieldConversions(String fieldConversionsString) {
223 Map fieldConversionsMap = new HashMap();
224 if (StringUtils.isNotEmpty(fieldConversionsString)) {
225 if (fieldConversionsString.indexOf(",") > 0) {
226 StringTokenizer token = new StringTokenizer(fieldConversionsString, ",");
227 while (token.hasMoreTokens()) {
228 String element = token.nextToken();
229 fieldConversionsMap.put(element.substring(0, element.indexOf(":")), element.substring(element.indexOf(":") + 1));
230 }
231 }
232 else {
233 fieldConversionsMap.put(fieldConversionsString.substring(0, fieldConversionsString.indexOf(":")), fieldConversionsString.substring(fieldConversionsString.indexOf(":") + 1));
234 }
235 }
236 return fieldConversionsMap;
237 }
238
239 @Deprecated
240 public static Field setFieldQuickfinder(BusinessObject businessObject,
241 String attributeName, Field field, List displayedFieldNames) {
242 return setFieldQuickfinder( businessObject, (String)null, false, 0, attributeName, field, displayedFieldNames );
243 }
244
245 @Deprecated
246 public static Field setFieldQuickfinder(BusinessObject businessObject,
247 String attributeName, Field field, List displayedFieldNames, SelectiveReferenceRefresher srr) {
248 return setFieldQuickfinder( businessObject, (String)null, false, 0, attributeName, field, displayedFieldNames, srr );
249 }
250
251 /**
252 * Sets a fields quickfinder class and field conversions for an attribute.
253 */
254 @Deprecated
255 public static Field setFieldQuickfinder(BusinessObject businessObject, String collectionName, boolean addLine, int index,
256 String attributeName, Field field, List displayedFieldNames, SelectiveReferenceRefresher srr) {
257 field = setFieldQuickfinder(businessObject, collectionName, addLine, index, attributeName, field, displayedFieldNames);
258 if (srr != null) {
259 String collectionPrefix = "";
260 if ( collectionName != null ) {
261 if (addLine) {
262 collectionPrefix = KRADConstants.MAINTENANCE_ADD_PREFIX + collectionName + ".";
263 }
264 else {
265 collectionPrefix = collectionName + "[" + index + "].";
266 }
267 }
268 field.setReferencesToRefresh(convertReferencesToSelectCollectionToString(
269 srr.getAffectedReferencesFromLookup(businessObject, attributeName, collectionPrefix)));
270 }
271 return field;
272 }
273
274 /**
275 * Sets a fields quickfinder class and field conversions for an attribute.
276 */
277 @Deprecated
278 public static Field setFieldQuickfinder(BusinessObject businessObject, String collectionName, boolean addLine, int index,
279 String attributeName, Field field, List displayedFieldNames) {
280 boolean noLookup = false;
281 if (businessObject == null) {
282 return field;
283 }
284
285 Boolean noLookupField = getBusinessObjectDictionaryService().noLookupFieldLookup(businessObject.getClass(), attributeName);
286 if (noLookupField != null && noLookupField) {
287 noLookup = true;
288 }
289
290 return setFieldQuickfinder(businessObject, collectionName, addLine, index, attributeName, field, displayedFieldNames, noLookup);
291
292 }
293
294 @Deprecated
295 public static Field setFieldQuickfinder(BusinessObject businessObject, String collectionName, boolean addLine, int index, String attributeName, Field field, List displayedFieldNames, boolean noLookupField)
296 {
297 if (businessObject == null) {
298 return field;
299 }
300
301 if (noLookupField) {
302 return field;
303 }
304 DataObjectRelationship relationship = null;
305 if ( LOG.isDebugEnabled() ) {
306 LOG.debug( "setFieldQuickfinder("+businessObject.getClass().getName()+","+attributeName+","+field+","+displayedFieldNames+")" );
307 }
308
309 relationship = getBusinessObjectMetaDataService().getBusinessObjectRelationship(businessObject, businessObject.getClass(), attributeName, "", false);
310
311 String collectionPrefix = "";
312 if ( collectionName != null ) {
313 if (addLine) {
314 collectionPrefix = KRADConstants.MAINTENANCE_ADD_PREFIX + collectionName + ".";
315 }
316 else {
317 collectionPrefix = collectionName + "[" + index + "].";
318 }
319 }
320
321 if (relationship == null) {
322 Class c = ObjectUtils.getPropertyType(businessObject, attributeName, getPersistenceStructureService());
323
324 if(c!=null) {
325 if (attributeName.contains(".")) {
326 attributeName = StringUtils.substringBeforeLast( attributeName, "." );
327 }
328
329 RelationshipDefinition ddReference = getBusinessObjectMetaDataService().getBusinessObjectRelationshipDefinition(businessObject, attributeName);
330 relationship = getBusinessObjectMetaDataService().getBusinessObjectRelationship(ddReference, businessObject, businessObject.getClass(), attributeName, "", false);
331 if(relationship!=null) {
332 field.setQuickFinderClassNameImpl(relationship.getRelatedClass().getName());
333 field.setFieldConversions(generateFieldConversions( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, null));
334 field.setLookupParameters(generateLookupParameters( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, null));
335 field.setBaseLookupUrl(LookupUtils.getBaseLookupUrl(false));
336 field.setImageSrc(getBusinessObjectDictionaryService().getSearchIconOverride(businessObject.getClass()));
337 }
338 }
339
340 return field;
341 }
342 if (ObjectUtils.isNestedAttribute(attributeName)) {
343 //first determine the prefix and the attribute we are referring to
344 String nestedAttributePrefix = StringUtils.substringBeforeLast(attributeName, ".");
345
346 field.setQuickFinderClassNameImpl(relationship.getRelatedClass().getName());
347 field.setFieldConversions( generateFieldConversions( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, nestedAttributePrefix ) );
348 field.setLookupParameters( generateLookupParameters( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, nestedAttributePrefix ) );
349 field.setBaseLookupUrl(LookupUtils.getBaseLookupUrl(false));
350 } else {
351 field.setQuickFinderClassNameImpl(relationship.getRelatedClass().getName());
352 field.setFieldConversions( generateFieldConversions( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, null ) );
353 field.setLookupParameters( generateLookupParameters( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, null ) );
354 field.setBaseLookupUrl(LookupUtils.getBaseLookupUrl(false));
355 }
356 field.setImageSrc(getBusinessObjectDictionaryService().getSearchIconOverride(businessObject.getClass()));
357
358 return field;
359 }
360
361 private static String BASE_LOOKUP_ACTION_URL = null;
362 private static String BASE_MULTIPLE_VALUE_LOOKUP_ACTION_URL = null;
363 private static String BASE_INQUIRY_ACTION_URL = null;
364
365 /**
366 * @see org.kuali.rice.krad.uif.util.LookupInquiryUtils#getBaseLookupUrl()
367 */
368 @Deprecated
369 public static String getBaseLookupUrl(boolean isMultipleValue) {
370 ConfigurationService kualiConfigurationService = KRADServiceLocator.getKualiConfigurationService();
371 if ( isMultipleValue ) {
372 if ( BASE_MULTIPLE_VALUE_LOOKUP_ACTION_URL == null ) {
373 String lookupUrl = kualiConfigurationService.getPropertyValueAsString(KRADConstants.APPLICATION_URL_KEY);
374 if (!lookupUrl.endsWith("/")) {
375 lookupUrl = lookupUrl + "/";
376 }
377 lookupUrl += "kr/" + KRADConstants.MULTIPLE_VALUE_LOOKUP_ACTION;
378 BASE_MULTIPLE_VALUE_LOOKUP_ACTION_URL = lookupUrl;
379 }
380 return BASE_MULTIPLE_VALUE_LOOKUP_ACTION_URL;
381 } else {
382 if ( BASE_LOOKUP_ACTION_URL == null ) {
383 String lookupUrl = kualiConfigurationService.getPropertyValueAsString(KRADConstants.APPLICATION_URL_KEY);
384 if (!lookupUrl.endsWith("/")) {
385 lookupUrl = lookupUrl + "/";
386 }
387 lookupUrl += "kr/" + KRADConstants.LOOKUP_ACTION;
388 BASE_LOOKUP_ACTION_URL = lookupUrl;
389 }
390 return BASE_LOOKUP_ACTION_URL;
391 }
392 }
393
394 @Deprecated
395 public static String getBaseInquiryUrl() {
396 if ( BASE_INQUIRY_ACTION_URL == null ) {
397 StringBuffer inquiryUrl = new StringBuffer(
398 KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
399 KRADConstants.APPLICATION_URL_KEY) );
400 if (inquiryUrl.charAt(inquiryUrl.length()-1) != '/' ) {
401 inquiryUrl.append( '/' );
402 }
403 inquiryUrl.append("kr/");
404 inquiryUrl.append( KRADConstants.INQUIRY_ACTION );
405 BASE_INQUIRY_ACTION_URL = inquiryUrl.toString();
406 }
407 return BASE_INQUIRY_ACTION_URL;
408 }
409
410 public static String transformLookupUrlToMultiple(String lookupUrl) {
411 return lookupUrl.replace("kr/" + KRADConstants.LOOKUP_ACTION, "kr/" + KRADConstants.MULTIPLE_VALUE_LOOKUP_ACTION);
412 }
413
414 /**
415 * Sets whether a field should have direct inquiries enabled. The direct inquiry is the functionality on a page such that if the primary key for
416 * a quickfinder is filled in and the direct inquiry button is pressed, then a new window will popup showing an inquiry page without going through
417 * the lookup first.
418 *
419 * For this method to work properly, it must be called after setFieldQuickfinder
420 * //TODO: chb: that should not be the case -- the relationship object the two rely upon should be established outside of the lookup/quickfinder code
421 *
422 *
423 * @param field
424 */
425 private static void setFieldDirectInquiry(Field field) {
426 if (StringUtils.isNotBlank(field.getFieldConversions())) {
427 boolean directInquiriesEnabled = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(
428 KRADConstants.KNS_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE, KRADConstants.SystemGroupParameterNames.ENABLE_DIRECT_INQUIRIES_IND);
429 if (directInquiriesEnabled) {
430 if (StringUtils.isNotBlank(field.getFieldConversions())) {
431 String fieldConversions = field.getFieldConversions();
432 String newInquiryParameters = KRADConstants.EMPTY_STRING;
433 String[] conversions = StringUtils.split(fieldConversions, KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
434
435 for (int l = 0; l < conversions.length; l++) {
436 String conversion = conversions[l];
437 //String[] conversionPair = StringUtils.split(conversion, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
438 String[] conversionPair = StringUtils.split(conversion, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR, 2);
439 String conversionFrom = conversionPair[0];
440 String conversionTo = conversionPair[1];
441 newInquiryParameters += (conversionTo + KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR + conversionFrom);
442
443 if (l < conversions.length - 1) {
444 newInquiryParameters += KRADConstants.FIELD_CONVERSIONS_SEPARATOR;
445 }
446 }
447
448 field.setInquiryParameters(newInquiryParameters);
449 }
450 }
451 field.setFieldDirectInquiryEnabled(directInquiriesEnabled);
452 }
453 else {
454 field.setFieldDirectInquiryEnabled(false);
455 }
456 }
457
458 /**
459 *
460 * @param field
461 * @return the altered Field object
462 */
463 public static Field setFieldDirectInquiry(BusinessObject businessObject, String attributeName, Field field)
464 {
465 if (businessObject == null)
466 {
467 return field;
468 }
469
470 Boolean noDirectInquiry = getBusinessObjectDictionaryService().noDirectInquiryFieldLookup(businessObject.getClass(), attributeName);
471 //check if noDirectInquiry is present and true, but if it's not set in existing data dictionary definitions, don't create a direct inquiry
472 if (noDirectInquiry != null && noDirectInquiry.booleanValue() || noDirectInquiry == null) {
473 return field;
474 }
475
476 setFieldDirectInquiry(field);
477
478 return field;
479 }
480
481 private static Map<Class,Map<String,Map>> referencesForForeignKey = new HashMap<Class, Map<String,Map>>();
482
483 @Deprecated
484 public static Map getPrimitiveReference(BusinessObject businessObject, String attributeName) {
485 Map chosenReferenceByKeySize = new HashMap();
486 Map chosenReferenceByFieldName = new HashMap();
487
488 Map referenceClasses = null;
489
490 try {
491 // add special caching of these relationships since the Spring caching is so expensive
492 Map<String,Map> propMap = referencesForForeignKey.get(businessObject.getClass());
493 if ( propMap == null ) {
494 propMap = new HashMap<String, Map>();
495 referencesForForeignKey.put(businessObject.getClass(), propMap);
496 }
497 if ( propMap.containsKey(attributeName) ) {
498 referenceClasses = propMap.get( attributeName );
499 } else {
500 //KFSMI-709: Make Inquiry Framework use BusinessObjectMetadataService instead of just PersistenceStructureService
501 referenceClasses = getBusinessObjectMetaDataService().getReferencesForForeignKey(businessObject, attributeName);
502 if(referenceClasses==null || referenceClasses.isEmpty()) {
503 if ( getPersistenceStructureService().isPersistable(businessObject.getClass()) ) {
504 referenceClasses = getPersistenceStructureService().getReferencesForForeignKey(businessObject.getClass(), attributeName);
505 }
506 }
507 propMap.put(attributeName, referenceClasses);
508 }
509 } catch ( ClassNotPersistableException ex ) {
510 // do nothing, there is no quickfinder
511 Map<String,Map> propMap = referencesForForeignKey.get(businessObject.getClass());
512 propMap.put(attributeName, null);
513 }
514
515 // if field is not fk to any reference class, return field object w no quickfinder
516 if (referenceClasses == null || referenceClasses.isEmpty()) {
517 return chosenReferenceByKeySize;
518 }
519
520 /*
521 * if field is fk to more than one reference, take the class with the least # of pk fields, this should give the correct
522 * grain for the attribute
523 */
524 int minKeys = Integer.MAX_VALUE;
525 for (Iterator iter = referenceClasses.keySet().iterator(); iter.hasNext();) {
526 String attr = (String) iter.next();
527 Class clazz = (Class) referenceClasses.get(attr);
528 List pkNames = getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(clazz);
529
530 // Compare based on key size.
531 if (pkNames.size() < minKeys) {
532 minKeys = pkNames.size();
533 chosenReferenceByKeySize.clear();
534 chosenReferenceByKeySize.put(attr, clazz);
535 }
536
537 // Compare based on field name.
538 if (attributeName.startsWith(attr)) {
539 chosenReferenceByFieldName.clear();
540 chosenReferenceByFieldName.put(attr, clazz);
541 }
542 }
543
544 // If a compatible key was found based on field names, prefer it, otherwise use choice by key size.
545 return chosenReferenceByFieldName.isEmpty() ? chosenReferenceByKeySize : chosenReferenceByFieldName;
546 }
547
548 /**
549 *
550 * This method walks through the nested attribute and finds the last business object in the chain and returns it (excluding the
551 * last parameter which is the actual attribute)
552 *
553 * @param attributeName
554 * @return
555 */
556 public static BusinessObject getNestedBusinessObject(BusinessObject bo, String attributeName) {
557 String[] nestedAttributes = StringUtils.split(attributeName, ".");
558
559 BusinessObject childBO = null;
560 String attributeRefName = "";
561 Class clazz = null;
562 if (nestedAttributes.length > 1) {
563 String attributeStringSoFar = "";
564 for (int i = 0; i < nestedAttributes.length - 1; i++) {
565 // we need to build a string of the attribute names depending on which iteration we're in.
566 // so if the original attributeName string we're using is "a.b.c.d.e", then first iteration would use
567 // "a", 2nd "a.b", 3rd "a.b.c", etc.
568 if (i != 0) {
569 attributeStringSoFar = attributeStringSoFar + ".";
570 }
571 attributeStringSoFar = attributeStringSoFar + nestedAttributes[i];
572
573 clazz = ObjectUtils.getPropertyType( bo, attributeStringSoFar, getPersistenceStructureService() );
574
575 if (clazz != null && BusinessObject.class.isAssignableFrom(clazz)) {
576 try {
577 childBO = (BusinessObject) ObjectUtils.createNewObjectFromClass(clazz);
578 }
579 catch (Exception e) {
580 return null;
581 }
582 }
583 }
584 }
585 return childBO;
586 }
587
588 public static Class getNestedReferenceClass(BusinessObject businessObject, String attributeName) {
589 BusinessObject bo = getNestedBusinessObject(businessObject, attributeName);
590 return null == bo ? null : bo.getClass();
591 }
592
593 @Deprecated
594 private static String generateFieldConversions(BusinessObject businessObject, String collectionName, DataObjectRelationship relationship, String propertyPrefix, List displayedFieldNames, String nestedObjectPrefix) {
595 String fieldConversions = "";
596
597 if ( LOG.isDebugEnabled() ) {
598 LOG.debug( "generateFieldConversions(" + businessObject.getClass().getName() + "," + collectionName + ",\n" + relationship + "\n," + propertyPrefix + "," + displayedFieldNames + "," + nestedObjectPrefix + ")" );
599 }
600
601 // get the references for the given property
602 for ( Map.Entry<String,String> entry : relationship.getParentToChildReferences().entrySet() ) {
603 String fromField = entry.getValue();
604 String toField = entry.getKey();
605
606 // find the displayed to field mapping
607 if (!displayedFieldNames.contains(toField)) {
608 toField = translateToDisplayedField(businessObject.getClass(), toField, displayedFieldNames);
609 }
610
611 if (StringUtils.isNotBlank(fieldConversions)) {
612 fieldConversions += ",";
613 }
614
615 if ( StringUtils.isNotEmpty( propertyPrefix ) ) {
616 toField = propertyPrefix + "." + toField;
617 }
618
619 if ( StringUtils.isNotEmpty( collectionName ) ) {
620 toField = collectionName + toField;
621 }
622
623 fieldConversions += fromField + ":" + toField;
624 }
625
626 return fieldConversions;
627 }
628
629 @Deprecated
630 private static String generateLookupParameters(BusinessObject businessObject, String collectionName, DataObjectRelationship relationship, String propertyPrefix, List displayedFieldNames, String nestedObjectPrefix) {
631
632 String lookupParameters = "";
633
634 List displayedQFFieldNames = getBusinessObjectDictionaryService().getLookupFieldNames(relationship.getRelatedClass());
635 for ( Map.Entry<String,String> entry : relationship.getParentToChildReferences().entrySet() ) {
636 String fromField = entry.getKey();
637 String toField = entry.getValue();
638
639 if ( relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey().equals( fromField ) ) {
640 // find the displayed from field mapping
641 if (!displayedFieldNames.contains(fromField)) {
642 fromField = translateToDisplayedField(businessObject.getClass(), fromField, displayedFieldNames);
643 }
644
645 // translate to field
646 if (displayedQFFieldNames != null && !displayedQFFieldNames.contains(toField)) {
647 toField = translateToDisplayedField(relationship.getRelatedClass(), toField, displayedQFFieldNames);
648 }
649
650 if (StringUtils.isNotBlank(lookupParameters)) {
651 lookupParameters += ",";
652 }
653
654 if (propertyPrefix != null && !propertyPrefix.equals("")) {
655 fromField = propertyPrefix + "." + fromField;
656 }
657
658 if ( StringUtils.isNotEmpty( collectionName ) ) {
659 fromField = collectionName + fromField;
660 }
661
662 lookupParameters += fromField + ":" + toField;
663 }
664 }
665
666 return lookupParameters;
667 }
668
669 @Deprecated
670 private static String translateToDisplayedField(Class businessObjectClass, String fieldName, List displayedFieldNames) {
671 if ( getPersistenceStructureService().isPersistable(businessObjectClass) ) {
672 Map nestedFkMap = getPersistenceStructureService().getNestedForeignKeyMap(businessObjectClass);
673
674 // translate to primitive fk if nested
675 /*
676 * if (ObjectUtils.isNestedAttribute(fieldName) && nestedFkMap.containsKey(fieldName)) { fieldName = (String)
677 * nestedFkMap.get(fieldName); }
678 */
679
680 if (!displayedFieldNames.contains(fieldName)) {
681 for (Iterator iterator = displayedFieldNames.iterator(); iterator.hasNext();) {
682 String dispField = (String) iterator.next();
683
684 if (nestedFkMap.containsKey(dispField) && nestedFkMap.get(dispField).equals(fieldName)) {
685 fieldName = dispField;
686 }
687 }
688 }
689 }
690
691 return fieldName;
692 }
693
694 public static String convertReferencesToSelectCollectionToString(Collection<String> referencesToRefresh) {
695 StringBuilder buf = new StringBuilder();
696 for (String reference : referencesToRefresh) {
697 buf.append(reference).append(KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR);
698 }
699 if (!referencesToRefresh.isEmpty()) {
700 // we appended one too many separators, remove it
701 buf.delete(buf.length() - KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR.length(), buf.length());
702 }
703 return buf.toString();
704 }
705
706 public static String convertSetOfObjectIdsToString(Set<String> objectIds) {
707 if (objectIds.isEmpty()) {
708 return "";
709 }
710 StringBuilder buf = new StringBuilder();
711 for (String objectId : objectIds) {
712 if (objectId.contains(KRADConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR)) {
713 throw new RuntimeException("object ID " + objectId + " contains the selected obj ID separator");
714 }
715 buf.append(objectId).append(KRADConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR);
716 }
717 // added one extra separator, remove it
718 buf.delete(buf.length() - KRADConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR.length(), buf.length());
719
720 return buf.toString();
721 }
722
723 public static Set<String> convertStringOfObjectIdsToSet(String objectIdsString) {
724 Set<String> set = new HashSet<String>();
725
726 if (StringUtils.isNotBlank(objectIdsString)) {
727 String[] objectIds = StringUtils.splitByWholeSeparator(objectIdsString, KRADConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR);
728 for (String objectId : objectIds) {
729 set.add(objectId);
730 }
731 }
732 return set;
733 }
734
735 /**
736 * Given a list of results from a lookup, determines the best comparator to use on the String values of each of these columns
737 *
738 * 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,
739 * so we gotta go thru the whole list and determine the best comparator to use
740 *
741 * @param resultsTable
742 * @param column
743 * @return
744 */
745 public static Comparator findBestValueComparatorForColumn(List<ResultRow> resultTable, int column) {
746 // BIG HACK
747 Comparator comp = NullValueComparator.getInstance();
748 for (ResultRow row : resultTable) {
749 Comparator tempComp = row.getColumns().get(column).getValueComparator();
750 if (tempComp != null && !NullValueComparator.class.equals(tempComp.getClass())) {
751 return tempComp;
752 }
753 }
754 return comp;
755 }
756 /**
757 * Changes ranged search fields like from/to dates into the range operators the lookupable dao expects
758 * ("..",">" etc) this method modifies the passed in map and returns a list containing only the modified fields
759 *
760 * This method does not handle document searchable attributes. This is handled in a second pass by the docsearch-specific
761 * DocumentSearchCriteriaTranslator
762 */
763 public static Map<String, String> preProcessRangeFields(Map<String, String> lookupFormFields) {
764 Map<String, String> fieldsToUpdate = new HashMap<String, String>();
765 Set<String> fieldsForLookup = lookupFormFields.keySet();
766 for (String propName : fieldsForLookup) {
767 if (propName.startsWith(KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX)) {
768 String rangedLowerBoundValue = lookupFormFields.get(propName);
769 String rangedFieldName = StringUtils.remove(propName, KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX);
770 String rangedValue = lookupFormFields.get(rangedFieldName);
771
772 Range range = new Range();
773 // defaults for general lookup/search
774 range.setLowerBoundInclusive(true);
775 range.setUpperBoundInclusive(true);
776 range.setLowerBoundValue(rangedLowerBoundValue);
777 range.setUpperBoundValue(rangedValue);
778
779 String expr = range.toString();
780 if (StringUtils.isEmpty(expr)) {
781 expr = rangedValue;
782 }
783
784 fieldsToUpdate.put(rangedFieldName, expr);
785 }
786 }
787 //update lookup values from found ranged values to update
788 Set<String> keysToUpdate = fieldsToUpdate.keySet();
789 for (String updateKey : keysToUpdate) {
790 lookupFormFields.put(updateKey, fieldsToUpdate.get(updateKey));
791 }
792 return fieldsToUpdate;
793 }
794
795 /**
796 * Given 3 sets of object IDs: the set of selected object IDs before rendering the current page,
797 * the set of object IDs rendered on the page, and the set of object IDs selected on the page, computes
798 * the total set of selected object IDs.
799 *
800 * Instead of storing it in a set, returns it in a map with the selected object ID as both the key and value
801 * @param previouslySelectedObjectIds
802 * @param displayedObjectIds
803 * @param selectedObjectIds
804 * @return
805 */
806 public static Map<String, String> generateCompositeSelectedObjectIds(Set<String> previouslySelectedObjectIds, Set<String> displayedObjectIds, Set<String> selectedObjectIds) {
807 Map<String, String> tempMap = new HashMap<String, String>();
808 // Equivalent to the set operation:
809 // (P - D) union C, where - is the set difference operator
810 // 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
811 // since HTML does not pass a value for non-selected dcheckboxes
812
813 // first build a map w/ all the previouslySelectedObjectIds as keys
814 for (String previouslySelectedObjectId : previouslySelectedObjectIds) {
815 tempMap.put(previouslySelectedObjectId, previouslySelectedObjectId);
816 }
817 // then remove all the displayed elements (any selected displayed elements will be added back in the next loop)
818 for (String displayedObjectId : displayedObjectIds) {
819 tempMap.remove(displayedObjectId);
820 }
821 // put back the selected IDs
822 for (String selectedObjectId : selectedObjectIds) {
823 tempMap.put(selectedObjectId, selectedObjectId);
824 }
825 return tempMap;
826 }
827
828 public static DataDictionaryService getDataDictionaryService() {
829 return KRADServiceLocatorWeb.getDataDictionaryService();
830 }
831
832 public static PersistenceStructureService getPersistenceStructureService() {
833 return KRADServiceLocator.getPersistenceStructureService();
834 }
835
836 public static BusinessObjectDictionaryService getBusinessObjectDictionaryService() {
837 return KNSServiceLocator.getBusinessObjectDictionaryService();
838 }
839
840 public static BusinessObjectMetaDataService getBusinessObjectMetaDataService() {
841 return KNSServiceLocator.getBusinessObjectMetaDataService();
842 }
843
844 public static DateTimeService getDateTimeService() {
845 return CoreApiServiceLocator.getDateTimeService();
846 }
847 }