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<String, String> translateFieldConversions(String fieldConversionsString) { 223 Map<String, String> 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 }