Coverage Report - org.kuali.rice.kns.lookup.LookupUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
LookupUtils
0%
0/367
0%
0/230
4.512
 
 1  
 /*
 2  
  * Copyright 2006-2007 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kns.lookup;
 17  
 
 18  
 import java.sql.Date;
 19  
 import java.sql.Timestamp;
 20  
 import java.text.ParseException;
 21  
 import java.util.ArrayList;
 22  
 import java.util.Calendar;
 23  
 import java.util.Collection;
 24  
 import java.util.Comparator;
 25  
 import java.util.HashMap;
 26  
 import java.util.HashSet;
 27  
 import java.util.Iterator;
 28  
 import java.util.List;
 29  
 import java.util.Map;
 30  
 import java.util.Set;
 31  
 import java.util.StringTokenizer;
 32  
 
 33  
 import org.apache.commons.lang.StringUtils;
 34  
 import org.apache.ojb.broker.query.Criteria;
 35  
 import org.kuali.rice.core.database.platform.DatabasePlatform;
 36  
 import org.kuali.rice.core.service.EncryptionService;
 37  
 import org.kuali.rice.kns.bo.BusinessObject;
 38  
 import org.kuali.rice.kns.bo.BusinessObjectRelationship;
 39  
 import org.kuali.rice.kns.datadictionary.RelationshipDefinition;
 40  
 import org.kuali.rice.kns.datadictionary.control.ControlDefinition;
 41  
 import org.kuali.rice.kns.exception.ClassNotPersistableException;
 42  
 import org.kuali.rice.kns.exception.UnknownBusinessClassAttributeException;
 43  
 import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
 44  
 import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
 45  
 import org.kuali.rice.kns.service.DataDictionaryService;
 46  
 import org.kuali.rice.kns.service.DateTimeService;
 47  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 48  
 import org.kuali.rice.kns.service.KualiConfigurationService;
 49  
 import org.kuali.rice.kns.service.PersistenceStructureService;
 50  
 import org.kuali.rice.kns.util.KNSConstants;
 51  
 import org.kuali.rice.kns.util.KNSPropertyConstants;
 52  
 import org.kuali.rice.kns.util.ObjectUtils;
 53  
 import org.kuali.rice.kns.web.comparator.NullValueComparator;
 54  
 import org.kuali.rice.kns.web.ui.Field;
 55  
 import org.kuali.rice.kns.web.ui.ResultRow;
 56  
 
 57  
 /**
 58  
  * This is a static utility class for Lookup related utilities and helper methods.
 59  
  *
 60  
  *
 61  
  */
 62  
 public class LookupUtils {
 63  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupUtils.class);
 64  
 
 65  
     private static DataDictionaryService dataDictionaryService;
 66  
     private static PersistenceStructureService persistenceStructureService;
 67  
     private static BusinessObjectDictionaryService businessObjectDictionaryService;
 68  
     private static BusinessObjectMetaDataService businessObjectMetaDataService;
 69  
     private static KualiConfigurationService kualiConfigurationService;
 70  
     private static DateTimeService dateTimeService;
 71  
 
 72  0
     public LookupUtils() {
 73  
         // default constructor for Spring to call to start up initialization process
 74  0
     }
 75  
 
 76  
     public void setBusinessObjectDictionaryService(BusinessObjectDictionaryService businessObjectDictionaryService) {
 77  0
         LookupUtils.businessObjectDictionaryService = businessObjectDictionaryService;
 78  0
     }
 79  
 
 80  
     public void setDataDictionaryService(DataDictionaryService ddService) {
 81  0
         LookupUtils.dataDictionaryService = ddService;
 82  0
     }
 83  
 
 84  
     public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
 85  0
         LookupUtils.persistenceStructureService = persistenceStructureService;
 86  0
     }
 87  
 
 88  
     public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) {
 89  0
         LookupUtils.kualiConfigurationService = kualiConfigurationService;
 90  0
     }
 91  
 
 92  
     public void setDateTimeService(DateTimeService dateTimeService) {
 93  0
                 LookupUtils.dateTimeService = dateTimeService;
 94  0
         }
 95  
 
 96  
         /**
 97  
      * Sets the businessObjectMetaDataService attribute value.
 98  
      * @param businessObjectMetaDataService The businessObjectMetaDataService to set.
 99  
      */
 100  
     public void setBusinessObjectMetaDataService(BusinessObjectMetaDataService businessObjectMetaDataService) {
 101  0
         LookupUtils.businessObjectMetaDataService = businessObjectMetaDataService;
 102  0
     }
 103  
 
 104  
     /**
 105  
      *
 106  
      * This method uses the DataDictionary to determine whether to force uppercase the value, and if it should, then it does the
 107  
      * uppercase, and returns the upper-cased value.
 108  
      *
 109  
      * @param boClass Parent BO class that the fieldName is a member of.
 110  
      * @param fieldName Name of the field to be forced to uppercase.
 111  
      * @param fieldValue Value of the field that may be uppercased.
 112  
      * @return The correctly uppercased fieldValue if it should be uppercased, otherwise fieldValue is returned unchanged.
 113  
      *
 114  
      */
 115  
     public static String forceUppercase(Class boClass, String fieldName, String fieldValue) {
 116  
 
 117  
         // short-circuit to exit if there isnt enough information to do the forceUppercase
 118  0
         if (StringUtils.isBlank(fieldValue)) {
 119  0
             return fieldValue;
 120  
         }
 121  
 
 122  
         // parameter validation
 123  0
         if (boClass == null) {
 124  0
             throw new IllegalArgumentException("Parameter boClass passed in with null value.");
 125  
         }
 126  0
         else if (!BusinessObject.class.isAssignableFrom(boClass)) {
 127  0
             throw new IllegalArgumentException("Parameter boClass value passed in [" + boClass.getName() + "] " + "was not a descendent of BusinessObject.");
 128  
         }
 129  0
         if (StringUtils.isBlank(fieldName)) {
 130  0
             throw new IllegalArgumentException("Parameter fieldName passed in with empty value.");
 131  
         }
 132  
 
 133  0
         if (!dataDictionaryService.isAttributeDefined(boClass, fieldName)) {
 134  0
             return fieldValue;
 135  
         }
 136  
 
 137  
 
 138  0
         boolean forceUpperCase = false;
 139  
         try {
 140  0
             forceUpperCase = dataDictionaryService.getAttributeForceUppercase(boClass, fieldName).booleanValue();
 141  
         }
 142  0
         catch (UnknownBusinessClassAttributeException ubae) {
 143  
             // do nothing, dont alter the fieldValue
 144  0
         }
 145  0
         if (forceUpperCase && !fieldValue.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) {
 146  0
             return fieldValue.toUpperCase();
 147  
         }
 148  0
         return fieldValue;
 149  
     }
 150  
 
 151  
     /**
 152  
      *
 153  
      * This method uses the DataDictionary to determine whether to force uppercase the values, and if it should, then it does the
 154  
      * uppercase, and returns the upper-cased Map of fieldname/fieldValue pairs.
 155  
      *
 156  
      * @param boClass Parent BO class that the fieldName is a member of.
 157  
      * @param fieldValues A Map<String,String> where the key is the fieldName and the value is the fieldValue.
 158  
      * @return The same Map is returned, with the appropriate values uppercased (if any).
 159  
      *
 160  
      */
 161  
     public static Map<String, String> forceUppercase(Class boClass, Map<String, String> fieldValues) {
 162  0
         if (boClass == null) {
 163  0
             throw new IllegalArgumentException("Parameter boClass passed in with null value.");
 164  
         }
 165  0
         else if (!BusinessObject.class.isAssignableFrom(boClass)) {
 166  0
             throw new IllegalArgumentException("Parameter boClass value passed in [" + boClass.getName() + "] " + "was not a descendent of BusinessObject.");
 167  
         }
 168  0
         if (fieldValues == null) {
 169  0
             throw new IllegalArgumentException("Parameter fieldValues passed in with null value.");
 170  
         }
 171  
 
 172  0
         for (String fieldName : fieldValues.keySet()) {
 173  0
             fieldValues.put(fieldName, LookupUtils.forceUppercase(boClass, fieldName, (String) fieldValues.get(fieldName)));
 174  
         }
 175  0
         return fieldValues;
 176  
     }
 177  
 
 178  
 
 179  
     /**
 180  
      * @deprecated use {@link #applySearchResultsLimit(Class, Criteria, DatabasePlatform)} instead
 181  
      */
 182  
     public static void applySearchResultsLimit(Criteria criteria, DatabasePlatform platform) {
 183  0
         Integer limit = getApplicationSearchResultsLimit();
 184  0
         if (limit != null) {
 185  0
             platform.applyLimit(limit, criteria);
 186  
         }
 187  0
     }
 188  
 
 189  
     /**
 190  
      * This method applies the search results limit to the search criteria for this BO
 191  
      *
 192  
      * @param businessObjectClass BO class to search on / get limit for
 193  
      * @param criteria search criteria
 194  
      * @param platform database platform
 195  
      */
 196  
     public static void applySearchResultsLimit(Class businessObjectClass, Criteria criteria, DatabasePlatform platform) {
 197  0
         Integer limit = getSearchResultsLimit(businessObjectClass);
 198  0
         if (limit != null) {
 199  0
             platform.applyLimit(limit, criteria);
 200  
         }
 201  0
     }
 202  
 
 203  
     /**
 204  
      * This method applies the search results limit to the search criteria for this BO (JPA)
 205  
      *
 206  
      * @param businessObjectClass BO class to search on / get limit for
 207  
      * @param criteria search criteria
 208  
      */
 209  
     public static void applySearchResultsLimit(Class businessObjectClass, org.kuali.rice.core.jpa.criteria.Criteria criteria) {
 210  0
         Integer limit = getSearchResultsLimit(businessObjectClass);
 211  0
         if (limit != null) {
 212  0
                 criteria.setSearchLimit(limit);
 213  
                 }
 214  0
     }
 215  
 
 216  
     /**
 217  
      * This method parses and returns the lookup result set limit, checking first for the limit
 218  
      * for the BO being looked up, and then the global application limit if there isn't a limit
 219  
      * specific to this BO.
 220  
      *
 221  
      * @param businessObjectClass BO class to search on / get limit for.  If the passed in type is not of type
 222  
      * {@link BusinessObject}, then the application-wide default limit is used.
 223  
      * @return result set limit (or null if there isn't one)
 224  
      */
 225  
     public static Integer getSearchResultsLimit(Class businessObjectClass) {
 226  0
         Integer limit = null;
 227  0
         if (BusinessObject.class.isAssignableFrom(businessObjectClass)) {
 228  0
             limit = getBusinessObjectSearchResultsLimit(businessObjectClass);
 229  
         }
 230  0
         if (limit == null) {
 231  0
             limit = getApplicationSearchResultsLimit();
 232  
         }
 233  0
         return limit;
 234  
     }
 235  
 
 236  
     /**
 237  
      *
 238  
      */
 239  
     private static Integer getApplicationSearchResultsLimit() {
 240  0
         String limitString = KNSServiceLocator.getParameterService().getParameterValue(KNSConstants.KNS_NAMESPACE, KNSConstants.DetailTypes.LOOKUP_PARM_DETAIL_TYPE, KNSConstants.SystemGroupParameterNames.LOOKUP_RESULTS_LIMIT);
 241  0
         if (limitString != null) {
 242  0
             return Integer.valueOf(limitString);
 243  
         }
 244  0
         return null;
 245  
     }
 246  
 
 247  
     /**
 248  
      * This method parses and returns the lookup result set limit for the passed in BO (if one exists)
 249  
      *
 250  
      * @param businessObjectClass
 251  
      * @return result set limit for this BO (or null if the BO doesn't have a limit)
 252  
      */
 253  
     private static Integer getBusinessObjectSearchResultsLimit(Class businessObjectClass) {
 254  0
         return businessObjectDictionaryService.getLookupResultSetLimit(businessObjectClass);
 255  
     }
 256  
 
 257  
     /**
 258  
      * This method the maximum rows per page in a multiple value lookup
 259  
      *
 260  
      * @see org.kuali.KNSConstants.SystemGroupParameterNames#MULTIPLE_VALUE_LOOKUP_RESULTS_PER_PAGE
 261  
      * @return
 262  
      */
 263  
     public static Integer getApplicationMaximumSearchResulsPerPageForMultipleValueLookups() {
 264  0
         String limitString = KNSServiceLocator.getParameterService().getParameterValue(KNSConstants.KNS_NAMESPACE, KNSConstants.DetailTypes.LOOKUP_PARM_DETAIL_TYPE, KNSConstants.SystemGroupParameterNames.MULTIPLE_VALUE_LOOKUP_RESULTS_PER_PAGE);
 265  0
         if (limitString != null) {
 266  0
             return Integer.valueOf(limitString);
 267  
         }
 268  0
         return null;
 269  
     }
 270  
 
 271  
     /**
 272  
      * This makes a , delimited String list of fields separated by a , into a List of target --> lookup readOnlyFieldsList.
 273  
      *
 274  
      * @param readOnlyFields
 275  
      * @return the List representation of the readOnlyFields  String provided.
 276  
      */
 277  
     public static List<String> translateReadOnlyFieldsToList(String readOnlyFieldsString) {
 278  0
         List<String> readOnlyFieldsList = new ArrayList<String>();
 279  0
         if (StringUtils.isNotEmpty(readOnlyFieldsString)) {
 280  0
             if (readOnlyFieldsString.indexOf(",") > 0) {
 281  0
                 StringTokenizer token = new StringTokenizer(readOnlyFieldsString, ",");
 282  0
                 while (token.hasMoreTokens()) {
 283  0
                     String element = token.nextToken();
 284  0
                     readOnlyFieldsList.add(element);
 285  0
                 }
 286  0
             }
 287  
             else {
 288  0
                 readOnlyFieldsList.add(readOnlyFieldsString);
 289  
             }
 290  
         }
 291  0
       return readOnlyFieldsList;
 292  
     }
 293  
 
 294  
     /**
 295  
      * This translates a , delimited list of pairs separated by a : into a Map of target --> lookup field conversions.
 296  
      *
 297  
      * @param conversionFields
 298  
      * @return the Map representation of the fieldConversions String provided.
 299  
      */
 300  
     public static Map translateFieldConversions(String fieldConversionsString) {
 301  0
         Map fieldConversionsMap = new HashMap();
 302  0
         if (StringUtils.isNotEmpty(fieldConversionsString)) {
 303  0
             if (fieldConversionsString.indexOf(",") > 0) {
 304  0
                 StringTokenizer token = new StringTokenizer(fieldConversionsString, ",");
 305  0
                 while (token.hasMoreTokens()) {
 306  0
                     String element = token.nextToken();
 307  0
                     fieldConversionsMap.put(element.substring(0, element.indexOf(":")), element.substring(element.indexOf(":") + 1));
 308  0
                 }
 309  0
             }
 310  
             else {
 311  0
                 fieldConversionsMap.put(fieldConversionsString.substring(0, fieldConversionsString.indexOf(":")), fieldConversionsString.substring(fieldConversionsString.indexOf(":") + 1));
 312  
             }
 313  
         }
 314  0
         return fieldConversionsMap;
 315  
     }
 316  
 
 317  
     public static Field setFieldQuickfinder(BusinessObject businessObject,
 318  
             String attributeName, Field field, List displayedFieldNames) {
 319  0
         return setFieldQuickfinder( businessObject, (String)null, false, 0, attributeName, field, displayedFieldNames );
 320  
     }
 321  
 
 322  
     public static Field setFieldQuickfinder(BusinessObject businessObject,
 323  
             String attributeName, Field field, List displayedFieldNames, SelectiveReferenceRefresher srr) {
 324  0
         return setFieldQuickfinder( businessObject, (String)null, false, 0, attributeName, field, displayedFieldNames, srr );
 325  
     }
 326  
 
 327  
     /**
 328  
      * Sets a fields quickfinder class and field conversions for an attribute.
 329  
      */
 330  
     public static Field setFieldQuickfinder(BusinessObject businessObject, String collectionName, boolean addLine, int index,
 331  
             String attributeName, Field field, List displayedFieldNames, SelectiveReferenceRefresher srr) {
 332  0
         field = setFieldQuickfinder(businessObject, collectionName, addLine, index, attributeName, field, displayedFieldNames);
 333  0
         if (srr != null) {
 334  0
             String collectionPrefix = "";
 335  0
             if ( collectionName != null ) {
 336  0
                 if (addLine) {
 337  0
                     collectionPrefix = KNSConstants.MAINTENANCE_ADD_PREFIX + collectionName + ".";
 338  
                 }
 339  
                 else {
 340  0
                     collectionPrefix = collectionName + "[" + index + "].";
 341  
                 }
 342  
             }
 343  0
             field.setReferencesToRefresh(convertReferencesToSelectCollectionToString(
 344  
                     srr.getAffectedReferencesFromLookup(businessObject, attributeName, collectionPrefix)));
 345  
         }
 346  0
         return field;
 347  
     }
 348  
 
 349  
     /**
 350  
      * Sets a fields quickfinder class and field conversions for an attribute.
 351  
      */
 352  
     public static Field setFieldQuickfinder(BusinessObject businessObject, String collectionName, boolean addLine, int index,
 353  
                                             String attributeName, Field field, List displayedFieldNames) {
 354  0
         boolean noLookup = false;
 355  0
         if (businessObject == null) {
 356  0
             return field;
 357  
         }
 358  
 
 359  0
         Boolean noLookupField = businessObjectDictionaryService.noLookupFieldLookup(businessObject.getClass(), attributeName);
 360  0
         if (noLookupField != null && noLookupField) {
 361  0
             noLookup = true;
 362  
         }
 363  
 
 364  0
          return setFieldQuickfinder(businessObject, collectionName, addLine, index, attributeName, field, displayedFieldNames, noLookup);
 365  
 
 366  
     }
 367  
 
 368  
     public static Field setFieldQuickfinder(BusinessObject businessObject, String collectionName, boolean addLine, int index, String attributeName, Field field, List displayedFieldNames, boolean noLookupField)
 369  
     {
 370  0
          if (businessObject == null) {
 371  0
             return field;
 372  
         }
 373  
 
 374  0
         if (noLookupField) {
 375  0
             return field;
 376  
         }
 377  0
         BusinessObjectRelationship relationship = null;
 378  0
         if ( LOG.isDebugEnabled() ) {
 379  0
             LOG.debug( "setFieldQuickfinder("+businessObject.getClass().getName()+","+attributeName+","+field+","+displayedFieldNames+")" );
 380  
         }
 381  
 
 382  0
         relationship = businessObjectMetaDataService.getBusinessObjectRelationship(businessObject, businessObject.getClass(), attributeName, "", false);
 383  
 
 384  0
         String collectionPrefix = "";
 385  0
         if ( collectionName != null ) {
 386  0
             if (addLine) {
 387  0
                 collectionPrefix = KNSConstants.MAINTENANCE_ADD_PREFIX + collectionName + ".";
 388  
             }
 389  
             else {
 390  0
                 collectionPrefix = collectionName + "[" + index + "].";
 391  
             }
 392  
         }
 393  
 
 394  0
         if (relationship == null) {
 395  0
             Class c = ObjectUtils.getPropertyType(businessObject, attributeName, persistenceStructureService);
 396  
 
 397  0
             if(c!=null) {
 398  0
                 if (attributeName.contains(".")) {
 399  0
                     attributeName = StringUtils.substringBeforeLast( attributeName, "." );
 400  
                 }
 401  
 
 402  0
                 RelationshipDefinition ddReference = businessObjectMetaDataService.getBusinessObjectRelationshipDefinition(businessObject, attributeName);
 403  0
                 relationship = businessObjectMetaDataService.getBusinessObjectRelationship(ddReference, businessObject, businessObject.getClass(), attributeName, "", false);
 404  0
                 if(relationship!=null) {
 405  0
                     field.setQuickFinderClassNameImpl(relationship.getRelatedClass().getName());
 406  0
                     field.setFieldConversions(generateFieldConversions( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, null));
 407  0
                     field.setLookupParameters(generateLookupParameters( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, null));
 408  0
                     field.setBaseLookupUrl(LookupUtils.getBaseLookupUrl(false));
 409  0
                     field.setImageSrc(businessObjectDictionaryService.getSearchIconOverride(businessObject.getClass()));
 410  
                 }
 411  
             }
 412  
 
 413  0
             return field;
 414  
         }
 415  0
         if (ObjectUtils.isNestedAttribute(attributeName)) {
 416  
             //first determine the prefix and the attribute we are referring to
 417  0
             String nestedAttributePrefix = StringUtils.substringBeforeLast(attributeName, ".");
 418  
 
 419  0
             field.setQuickFinderClassNameImpl(relationship.getRelatedClass().getName());
 420  0
             field.setFieldConversions( generateFieldConversions( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, nestedAttributePrefix ) );
 421  0
             field.setLookupParameters( generateLookupParameters( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, nestedAttributePrefix ) );
 422  0
             field.setBaseLookupUrl(LookupUtils.getBaseLookupUrl(false));
 423  0
         } else {
 424  0
             field.setQuickFinderClassNameImpl(relationship.getRelatedClass().getName());
 425  0
             field.setFieldConversions( generateFieldConversions( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, null ) );
 426  0
             field.setLookupParameters( generateLookupParameters( businessObject, collectionPrefix, relationship, field.getPropertyPrefix(), displayedFieldNames, null ) );
 427  0
             field.setBaseLookupUrl(LookupUtils.getBaseLookupUrl(false));
 428  
         }
 429  0
         field.setImageSrc(businessObjectDictionaryService.getSearchIconOverride(businessObject.getClass()));
 430  
 
 431  0
         return field;
 432  
     }
 433  
 
 434  0
     private static String BASE_LOOKUP_ACTION_URL = null;
 435  0
     private static String BASE_MULTIPLE_VALUE_LOOKUP_ACTION_URL = null;
 436  0
     private static String BASE_INQUIRY_ACTION_URL = null;
 437  
     
 438  
     public static String getBaseLookupUrl(boolean isMultipleValue) {
 439  0
             if ( isMultipleValue ) {
 440  0
                     if ( BASE_MULTIPLE_VALUE_LOOKUP_ACTION_URL == null ) {
 441  0
                             String lookupUrl = KNSServiceLocator.getKualiConfigurationService().getPropertyString(KNSConstants.APPLICATION_URL_KEY);
 442  0
                             if (!lookupUrl.endsWith("/")) {
 443  0
                                     lookupUrl = lookupUrl + "/";
 444  
                             }
 445  0
                                 lookupUrl += "kr/" + KNSConstants.MULTIPLE_VALUE_LOOKUP_ACTION;
 446  0
                                 BASE_MULTIPLE_VALUE_LOOKUP_ACTION_URL = lookupUrl;
 447  
                     }
 448  0
                     return BASE_MULTIPLE_VALUE_LOOKUP_ACTION_URL;
 449  
             } else {
 450  0
                     if ( BASE_LOOKUP_ACTION_URL == null ) {
 451  0
                             String lookupUrl = KNSServiceLocator.getKualiConfigurationService().getPropertyString(KNSConstants.APPLICATION_URL_KEY);
 452  0
                             if (!lookupUrl.endsWith("/")) {
 453  0
                                     lookupUrl = lookupUrl + "/";
 454  
                             }
 455  0
                                 lookupUrl += "kr/" + KNSConstants.LOOKUP_ACTION;
 456  0
                                 BASE_LOOKUP_ACTION_URL = lookupUrl;
 457  
                     }
 458  0
                     return BASE_LOOKUP_ACTION_URL;
 459  
             }
 460  
     }
 461  
 
 462  
     public static String getBaseInquiryUrl() {
 463  0
             if ( BASE_INQUIRY_ACTION_URL == null ) {
 464  0
                     StringBuffer inquiryUrl = new StringBuffer( 
 465  
                                     KNSServiceLocator.getKualiConfigurationService().getPropertyString(KNSConstants.APPLICATION_URL_KEY) );
 466  0
                         if (inquiryUrl.charAt(inquiryUrl.length()-1) != '/' ) {
 467  0
                                 inquiryUrl.append( '/' );
 468  
                         }
 469  0
                         inquiryUrl.append("kr/");
 470  0
                         inquiryUrl.append( KNSConstants.INQUIRY_ACTION );
 471  0
                         BASE_INQUIRY_ACTION_URL = inquiryUrl.toString();
 472  
             }
 473  0
             return BASE_INQUIRY_ACTION_URL;
 474  
     }
 475  
 
 476  
     public static String transformLookupUrlToMultiple(String lookupUrl) {
 477  0
             return lookupUrl.replace("kr/" + KNSConstants.LOOKUP_ACTION, "kr/" + KNSConstants.MULTIPLE_VALUE_LOOKUP_ACTION);
 478  
     }
 479  
 
 480  
     /**
 481  
      * 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
 482  
      * 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
 483  
      * the lookup first.
 484  
      *
 485  
      * For this method to work properly, it must be called after setFieldQuickfinder
 486  
      * //TODO: chb: that should not be the case -- the relationship object the two rely upon should be established outside of the lookup/quickfinder code
 487  
      *
 488  
      *
 489  
      * @param field
 490  
      */
 491  
     private static void setFieldDirectInquiry(Field field) {
 492  0
         if (StringUtils.isNotBlank(field.getFieldConversions())) {
 493  0
             boolean directInquiriesEnabled = KNSServiceLocator.getParameterService().getIndicatorParameter(KNSConstants.KNS_NAMESPACE, KNSConstants.DetailTypes.ALL_DETAIL_TYPE, KNSConstants.SystemGroupParameterNames.ENABLE_DIRECT_INQUIRIES_IND);
 494  0
             if (directInquiriesEnabled) {
 495  0
                 if (StringUtils.isNotBlank(field.getFieldConversions())) {
 496  0
                     String fieldConversions = field.getFieldConversions();
 497  0
                     String newInquiryParameters = KNSConstants.EMPTY_STRING;
 498  0
                     String[] conversions = StringUtils.split(fieldConversions, KNSConstants.FIELD_CONVERSIONS_SEPARATOR);
 499  
 
 500  0
                     for (int l = 0; l < conversions.length; l++) {
 501  0
                         String conversion = conversions[l];
 502  
                         //String[] conversionPair = StringUtils.split(conversion, KNSConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
 503  0
                         String[] conversionPair = StringUtils.split(conversion, KNSConstants.FIELD_CONVERSION_PAIR_SEPARATOR, 2);
 504  0
                         String conversionFrom = conversionPair[0];
 505  0
                         String conversionTo = conversionPair[1];
 506  0
                         newInquiryParameters += (conversionTo + KNSConstants.FIELD_CONVERSION_PAIR_SEPARATOR + conversionFrom);
 507  
 
 508  0
                         if (l < conversions.length - 1) {
 509  0
                             newInquiryParameters += KNSConstants.FIELD_CONVERSIONS_SEPARATOR;
 510  
                         }
 511  
                     }
 512  
 
 513  0
                     field.setInquiryParameters(newInquiryParameters);
 514  
                 }
 515  
             }
 516  0
             field.setFieldDirectInquiryEnabled(directInquiriesEnabled);
 517  0
         }
 518  
         else {
 519  0
             field.setFieldDirectInquiryEnabled(false);
 520  
         }
 521  0
     }
 522  
 
 523  
     /**
 524  
      *
 525  
      * @param field
 526  
      * @return the altered Field object
 527  
      */
 528  
     public static Field setFieldDirectInquiry(BusinessObject businessObject, String attributeName, Field field)
 529  
     {
 530  0
                 if (businessObject == null)
 531  
                 {
 532  0
             return field;
 533  
         }
 534  
 
 535  0
         Boolean noDirectInquiry = businessObjectDictionaryService.noDirectInquiryFieldLookup(businessObject.getClass(), attributeName);
 536  
         //check if noDirectInquiry is present and true, but if it's not set in existing data dictionary definitions, don't create a direct inquiry
 537  0
         if (noDirectInquiry != null && noDirectInquiry.booleanValue() || noDirectInquiry == null) {
 538  0
             return field;
 539  
         }
 540  
 
 541  0
         setFieldDirectInquiry(field);
 542  
 
 543  0
         return field;
 544  
     }
 545  
 
 546  0
     private static Map<Class,Map<String,Map>> referencesForForeignKey = new HashMap<Class, Map<String,Map>>();
 547  
 
 548  
     public static Map getPrimitiveReference(BusinessObject businessObject, String attributeName) {
 549  0
         Map chosenReferenceByKeySize = new HashMap();
 550  0
         Map chosenReferenceByFieldName = new HashMap();
 551  
 
 552  0
         Map referenceClasses = null;
 553  
 
 554  
         try {
 555  
             // add special caching of these relationships since the Spring caching is so expensive
 556  0
             Map<String,Map> propMap = referencesForForeignKey.get(businessObject.getClass());
 557  0
             if ( propMap == null ) {
 558  0
                 propMap = new HashMap<String, Map>();
 559  0
                 referencesForForeignKey.put(businessObject.getClass(), propMap);
 560  
             }
 561  0
             if ( propMap.containsKey(attributeName) ) {
 562  0
                 referenceClasses = propMap.get( attributeName );
 563  
             } else {
 564  
                     //KFSMI-709: Make Inquiry Framework use BusinessObjectMetadataService instead of just PersistenceStructureService
 565  0
                     referenceClasses = businessObjectMetaDataService.getReferencesForForeignKey(businessObject, attributeName);
 566  0
                     if(referenceClasses==null || referenceClasses.isEmpty()) {
 567  0
                         if ( persistenceStructureService.isPersistable(businessObject.getClass()) ) {
 568  0
                             referenceClasses = persistenceStructureService.getReferencesForForeignKey(businessObject.getClass(), attributeName);
 569  
                         }
 570  
                     }
 571  0
                 propMap.put(attributeName, referenceClasses);
 572  
             }
 573  0
         } catch ( ClassNotPersistableException ex ) {
 574  
             // do nothing, there is no quickfinder
 575  0
             Map<String,Map> propMap = referencesForForeignKey.get(businessObject.getClass());
 576  0
             propMap.put(attributeName, null);
 577  0
         }
 578  
 
 579  
         // if field is not fk to any reference class, return field object w no quickfinder
 580  0
         if (referenceClasses == null || referenceClasses.isEmpty()) {
 581  0
             return chosenReferenceByKeySize;
 582  
         }
 583  
 
 584  
         /*
 585  
          * if field is fk to more than one reference, take the class with the least # of pk fields, this should give the correct
 586  
          * grain for the attribute
 587  
          */
 588  0
         int minKeys = Integer.MAX_VALUE;
 589  0
         for (Iterator iter = referenceClasses.keySet().iterator(); iter.hasNext();) {
 590  0
             String attr = (String) iter.next();
 591  0
             Class clazz = (Class) referenceClasses.get(attr);
 592  0
             List pkNames = businessObjectMetaDataService.listPrimaryKeyFieldNames(clazz);
 593  
 
 594  
             // Compare based on key size.
 595  0
             if (pkNames.size() < minKeys) {
 596  0
                 minKeys = pkNames.size();
 597  0
                 chosenReferenceByKeySize.clear();
 598  0
                 chosenReferenceByKeySize.put(attr, clazz);
 599  
             }
 600  
 
 601  
             // Compare based on field name.
 602  0
             if (attributeName.startsWith(attr)) {
 603  0
                 chosenReferenceByFieldName.clear();
 604  0
                 chosenReferenceByFieldName.put(attr, clazz);
 605  
             }
 606  0
         }
 607  
 
 608  
         // If a compatible key was found based on field names, prefer it, otherwise use choice by key size.
 609  0
         return chosenReferenceByFieldName.isEmpty() ? chosenReferenceByKeySize : chosenReferenceByFieldName;
 610  
     }
 611  
 
 612  
     /**
 613  
      *
 614  
      * This method walks through the nested attribute and finds the last business object in the chain and returns it (excluding the
 615  
      * last parameter which is the actual attribute)
 616  
      *
 617  
      * @param attributeName
 618  
      * @return
 619  
      */
 620  
     public static BusinessObject getNestedBusinessObject(BusinessObject bo, String attributeName) {
 621  0
         String[] nestedAttributes = StringUtils.split(attributeName, ".");
 622  
 
 623  0
         BusinessObject childBO = null;
 624  0
         String attributeRefName = "";
 625  0
         Class clazz = null;
 626  0
         if (nestedAttributes.length > 1) {
 627  0
             String attributeStringSoFar = "";
 628  0
             for (int i = 0; i < nestedAttributes.length - 1; i++) {
 629  
                 // we need to build a string of the attribute names depending on which iteration we're in.
 630  
                 // so if the original attributeName string we're using is "a.b.c.d.e", then first iteration would use
 631  
                 // "a", 2nd "a.b", 3rd "a.b.c", etc.
 632  0
                 if (i != 0) {
 633  0
                     attributeStringSoFar = attributeStringSoFar + ".";
 634  
                 }
 635  0
                 attributeStringSoFar = attributeStringSoFar + nestedAttributes[i];
 636  
 
 637  0
                 clazz = ObjectUtils.getPropertyType( bo, attributeStringSoFar, persistenceStructureService );
 638  
 
 639  0
                 if (clazz != null && BusinessObject.class.isAssignableFrom(clazz)) {
 640  
                     try {
 641  0
                             childBO = (BusinessObject) ObjectUtils.createNewObjectFromClass(clazz);
 642  
                     }
 643  0
                     catch (Exception e) {
 644  0
                         return null;
 645  0
                     }
 646  
                 }
 647  
             }
 648  
         }
 649  0
         return childBO;
 650  
     }
 651  
 
 652  
     public static Class getNestedReferenceClass(BusinessObject businessObject, String attributeName) {
 653  0
         BusinessObject bo = getNestedBusinessObject(businessObject, attributeName);
 654  0
         return null == bo ? null : bo.getClass();
 655  
     }
 656  
 
 657  
     private static String generateFieldConversions(BusinessObject businessObject, String collectionName, BusinessObjectRelationship relationship, String propertyPrefix, List displayedFieldNames, String nestedObjectPrefix) {
 658  0
         String fieldConversions = "";
 659  
 
 660  0
         if ( LOG.isDebugEnabled() ) {
 661  0
             LOG.debug( "generateFieldConversions(" + businessObject.getClass().getName() + "," + collectionName + ",\n" + relationship + "\n," + propertyPrefix + "," + displayedFieldNames + "," + nestedObjectPrefix + ")" );
 662  
         }
 663  
 
 664  
         // get the references for the given property
 665  0
         for ( Map.Entry<String,String> entry : relationship.getParentToChildReferences().entrySet() ) {
 666  0
             String fromField = entry.getValue();
 667  0
             String toField = entry.getKey();
 668  
 
 669  
             // find the displayed to field mapping
 670  0
             if (!displayedFieldNames.contains(toField)) {
 671  0
                 toField = translateToDisplayedField(businessObject.getClass(), toField, displayedFieldNames);
 672  
             }
 673  
 
 674  0
             if (StringUtils.isNotBlank(fieldConversions)) {
 675  0
                 fieldConversions += ",";
 676  
             }
 677  
 
 678  0
             if ( StringUtils.isNotEmpty( propertyPrefix ) ) {
 679  0
                 toField = propertyPrefix + "." + toField;
 680  
             }
 681  
 
 682  0
             if ( StringUtils.isNotEmpty( collectionName ) ) {
 683  0
                 toField = collectionName + toField;
 684  
             }
 685  
 
 686  0
             fieldConversions += fromField + ":" + toField;
 687  0
         }
 688  
 
 689  0
         return fieldConversions;
 690  
     }
 691  
 
 692  
     private static String generateLookupParameters(BusinessObject businessObject, String collectionName, BusinessObjectRelationship relationship, String propertyPrefix, List displayedFieldNames, String nestedObjectPrefix) {
 693  
 
 694  0
         String lookupParameters = "";
 695  
 
 696  0
         List displayedQFFieldNames = businessObjectDictionaryService.getLookupFieldNames(relationship.getRelatedClass());
 697  0
         for ( Map.Entry<String,String> entry : relationship.getParentToChildReferences().entrySet() ) {
 698  0
             String fromField = entry.getKey();
 699  0
             String toField = entry.getValue();
 700  
 
 701  0
             if ( relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey().equals( fromField ) ) {
 702  
                 // find the displayed from field mapping
 703  0
                 if (!displayedFieldNames.contains(fromField)) {
 704  0
                     fromField = translateToDisplayedField(businessObject.getClass(), fromField, displayedFieldNames);
 705  
                 }
 706  
 
 707  
                 // translate to field
 708  0
                 if (displayedQFFieldNames != null && !displayedQFFieldNames.contains(toField)) {
 709  0
                     toField = translateToDisplayedField(relationship.getRelatedClass(), toField, displayedQFFieldNames);
 710  
                 }
 711  
 
 712  0
                 if (StringUtils.isNotBlank(lookupParameters)) {
 713  0
                     lookupParameters += ",";
 714  
                 }
 715  
 
 716  0
                 if (propertyPrefix != null && !propertyPrefix.equals("")) {
 717  0
                     fromField = propertyPrefix + "." + fromField;
 718  
                 }
 719  
 
 720  0
                 if ( StringUtils.isNotEmpty( collectionName ) ) {
 721  0
                     fromField = collectionName + fromField;
 722  
                 }
 723  
 
 724  0
                 lookupParameters += fromField + ":" + toField;
 725  
             }
 726  0
         }
 727  
 
 728  0
         return lookupParameters;
 729  
     }
 730  
 
 731  
 
 732  
     private static String translateToDisplayedField(Class businessObjectClass, String fieldName, List displayedFieldNames) {        
 733  0
         if ( persistenceStructureService.isPersistable(businessObjectClass) ) {
 734  0
             Map nestedFkMap = persistenceStructureService.getNestedForeignKeyMap(businessObjectClass);
 735  
 
 736  
             // translate to primitive fk if nested
 737  
             /*
 738  
              * if (ObjectUtils.isNestedAttribute(fieldName) && nestedFkMap.containsKey(fieldName)) { fieldName = (String)
 739  
              * nestedFkMap.get(fieldName); }
 740  
              */
 741  
 
 742  0
             if (!displayedFieldNames.contains(fieldName)) {
 743  0
                 for (Iterator iterator = displayedFieldNames.iterator(); iterator.hasNext();) {
 744  0
                     String dispField = (String) iterator.next();
 745  
 
 746  0
                     if (nestedFkMap.containsKey(dispField) && nestedFkMap.get(dispField).equals(fieldName)) {
 747  0
                         fieldName = dispField;
 748  
                     }
 749  0
                 }
 750  
             }
 751  
         }
 752  
 
 753  0
         return fieldName;
 754  
     }
 755  
 
 756  
     public static String convertReferencesToSelectCollectionToString(Collection<String> referencesToRefresh) {
 757  0
         StringBuilder buf = new StringBuilder();
 758  0
         for (String reference : referencesToRefresh) {
 759  0
             buf.append(reference).append(KNSConstants.REFERENCES_TO_REFRESH_SEPARATOR);
 760  
         }
 761  0
         if (!referencesToRefresh.isEmpty()) {
 762  
             // we appended one too many separators, remove it
 763  0
             buf.delete(buf.length() - KNSConstants.REFERENCES_TO_REFRESH_SEPARATOR.length(), buf.length());
 764  
         }
 765  0
         return buf.toString();
 766  
     }
 767  
 
 768  
     public static String convertSetOfObjectIdsToString(Set<String> objectIds) {
 769  0
         if (objectIds.isEmpty()) {
 770  0
             return "";
 771  
         }
 772  0
         StringBuilder buf = new StringBuilder();
 773  0
         for (String objectId : objectIds) {
 774  0
             if (objectId.contains(KNSConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR)) {
 775  0
                 throw new RuntimeException("object ID " + objectId + " contains the selected obj ID separator");
 776  
             }
 777  0
             buf.append(objectId).append(KNSConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR);
 778  
         }
 779  
         // added one extra separator, remove it
 780  0
         buf.delete(buf.length() - KNSConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR.length(), buf.length());
 781  
 
 782  0
         return buf.toString();
 783  
     }
 784  
 
 785  
     public static Set<String> convertStringOfObjectIdsToSet(String objectIdsString) {
 786  0
         Set<String> set = new HashSet<String>();
 787  
 
 788  0
         if (StringUtils.isNotBlank(objectIdsString)) {
 789  0
             String[] objectIds = StringUtils.splitByWholeSeparator(objectIdsString, KNSConstants.MULTIPLE_VALUE_LOOKUP_OBJ_IDS_SEPARATOR);
 790  0
             for (String objectId : objectIds) {
 791  0
                 set.add(objectId);
 792  
             }
 793  
         }
 794  0
         return set;
 795  
     }
 796  
 
 797  
     /**
 798  
      * Given a list of results from a lookup, determines the best comparator to use on the String values of each of these columns
 799  
      *
 800  
      * 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,
 801  
      * so we gotta go thru the whole list and determine the best comparator to use
 802  
      *
 803  
      * @param resultsTable
 804  
      * @param column
 805  
      * @return
 806  
      */
 807  
     public static Comparator findBestValueComparatorForColumn(List<ResultRow> resultTable, int column) {
 808  
         // BIG HACK
 809  0
         Comparator comp = NullValueComparator.getInstance();
 810  0
         for (ResultRow row : resultTable) {
 811  0
             Comparator tempComp = row.getColumns().get(column).getValueComparator();
 812  0
             if (tempComp != null && !NullValueComparator.class.equals(tempComp.getClass())) {
 813  0
                 return tempComp;
 814  
             }
 815  0
         }
 816  0
         return comp;
 817  
     }
 818  
 
 819  
     /**
 820  
      * Given 3 sets of object IDs: the set of selected object IDs before rendering the current page,
 821  
      * the set of object IDs rendered on the page, and the set of object IDs selected on the page, computes
 822  
      * the total set of selected object IDs.
 823  
      *
 824  
      * Instead of storing it in a set, returns it in a map with the selected object ID as both the key and value
 825  
      * @param previouslySelectedObjectIds
 826  
      * @param displayedObjectIds
 827  
      * @param selectedObjectIds
 828  
      * @return
 829  
      */
 830  
     public static Map<String, String> generateCompositeSelectedObjectIds(Set<String> previouslySelectedObjectIds, Set<String> displayedObjectIds, Set<String> selectedObjectIds) {
 831  0
         Map<String, String> tempMap = new HashMap<String, String>();
 832  
         // Equivalent to the set operation:
 833  
         // (P - D) union C, where - is the set difference operator
 834  
         // 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
 835  
         // since HTML does not pass a value for non-selected dcheckboxes
 836  
 
 837  
         // first build a map w/ all the previouslySelectedObjectIds as keys
 838  0
         for (String previouslySelectedObjectId : previouslySelectedObjectIds) {
 839  0
             tempMap.put(previouslySelectedObjectId, previouslySelectedObjectId);
 840  
         }
 841  
         // then remove all the displayed elements (any selected displayed elements will be added back in the next loop)
 842  0
         for (String displayedObjectId : displayedObjectIds) {
 843  0
             tempMap.remove(displayedObjectId);
 844  
         }
 845  
         // put back the selected IDs
 846  0
         for (String selectedObjectId : selectedObjectIds) {
 847  0
             tempMap.put(selectedObjectId, selectedObjectId);
 848  
         }
 849  0
         return tempMap;
 850  
     }
 851  
 
 852  
     /**
 853  
      * Removes fields identified in the data dictionary as hidden from the lookup field values.
 854  
      * (This will remove Universal User ID and Person name from search requests when a user ID is entered.)
 855  
      *
 856  
      * @param fieldValues
 857  
      */
 858  
     public static void removeHiddenCriteriaFields( Class businessObjectClass, Map fieldValues ) {
 859  0
         List<String> lookupFieldAttributeList = businessObjectMetaDataService.getLookupableFieldNames(businessObjectClass);
 860  0
         if (lookupFieldAttributeList != null) {
 861  0
             for (Iterator iter = lookupFieldAttributeList.iterator(); iter.hasNext();) {
 862  0
                 String attributeName = (String) iter.next();
 863  0
                 if (fieldValues.containsKey(attributeName)) {
 864  0
                     ControlDefinition controlDef = dataDictionaryService.getAttributeControlDefinition(businessObjectClass, attributeName);
 865  0
                     if (controlDef != null && controlDef.isHidden() ) {
 866  0
                         fieldValues.remove(attributeName);
 867  
                     }
 868  
                 }
 869  0
             }
 870  
         }
 871  0
         }
 872  
 
 873  
     /**
 874  
      * Determines what Timestamp should be used for active queries on effective dated records. Determination made as
 875  
      * follows:
 876  
      * <ul>
 877  
      *   <li>Use activeAsOfDate value from search values Map if value is not empty</li>
 878  
      *   <li>If search value given, try to convert to sql date, if conversion fails, try to convert to Timestamp</li>
 879  
      *   <li>If search value empty, use current Date</li>
 880  
      *   <li>If Timestamp value not given, create Timestamp from given Date setting the time as 1 second before midnight
 881  
      * </ul>
 882  
      * 
 883  
      * @param searchValues - Map containing search key/value pairs
 884  
      * @return Timestamp to be used for active criteria
 885  
      */
 886  
         public static Timestamp getActiveDateTimestampForCriteria(Map searchValues) {
 887  0
                 Date activeDate = dateTimeService.getCurrentSqlDate();
 888  0
                 Timestamp activeTimestamp = null;
 889  0
                 if (searchValues.containsKey(KNSPropertyConstants.ACTIVE_AS_OF_DATE)) {
 890  0
                         String activeAsOfDate = (String) searchValues.get(KNSPropertyConstants.ACTIVE_AS_OF_DATE);
 891  0
                         if (StringUtils.isNotBlank(activeAsOfDate)) {
 892  
                                 try {
 893  0
                                         activeDate = dateTimeService.convertToSqlDate(ObjectUtils.clean(activeAsOfDate));
 894  0
                                 } catch (ParseException e) {
 895  
                                         // try to parse as timestamp
 896  
                                         try {
 897  0
                                                 activeTimestamp = dateTimeService.convertToSqlTimestamp(ObjectUtils.clean(activeAsOfDate));
 898  0
                                         } catch (ParseException e1) {
 899  0
                                                 throw new RuntimeException("Unable to convert date: " + ObjectUtils.clean(activeAsOfDate));
 900  0
                                         }
 901  0
                                 }
 902  
                         }
 903  
                 }
 904  
 
 905  
                 // if timestamp not given set to 1 second before midnight on the given date
 906  0
                 if (activeTimestamp == null) {
 907  0
                         Calendar cal = Calendar.getInstance();
 908  0
                         cal.setTime(activeDate);
 909  0
                         cal.set(Calendar.HOUR, cal.getMaximum(Calendar.HOUR));
 910  0
                         cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE));
 911  0
                         cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND));
 912  
 
 913  0
                         activeTimestamp = new Timestamp(cal.getTime().getTime());
 914  
                 }
 915  
 
 916  0
                 return activeTimestamp;
 917  
         }
 918  
 }