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