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 }