001: /*
002: * Copyright 2006-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.core.lookup;
017:
018: import java.security.GeneralSecurityException;
019: import java.sql.Date;
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.Properties;
026:
027: import org.apache.commons.lang.StringUtils;
028: import org.kuali.RiceConstants;
029: import org.kuali.RiceKeyConstants;
030: import org.kuali.core.bo.BusinessObject;
031: import org.kuali.core.bo.PersistableBusinessObject;
032: import org.kuali.core.datadictionary.mask.Mask;
033: import org.kuali.core.exceptions.ValidationException;
034: import org.kuali.core.inquiry.Inquirable;
035: import org.kuali.core.inquiry.KualiInquirableImpl;
036: import org.kuali.core.service.BusinessObjectDictionaryService;
037: import org.kuali.core.service.BusinessObjectMetaDataService;
038: import org.kuali.core.service.BusinessObjectService;
039: import org.kuali.core.service.DataDictionaryService;
040: import org.kuali.core.service.EncryptionService;
041: import org.kuali.core.service.LookupService;
042: import org.kuali.core.service.MaintenanceDocumentDictionaryService;
043: import org.kuali.core.service.PersistenceStructureService;
044: import org.kuali.core.service.SequenceAccessorService;
045: import org.kuali.core.service.UniversalUserService;
046: import org.kuali.core.util.FieldUtils;
047: import org.kuali.core.util.GlobalVariables;
048: import org.kuali.core.util.ObjectUtils;
049: import org.kuali.core.util.UrlFactory;
050: import org.kuali.core.web.comparator.CellComparatorHelper;
051: import org.kuali.core.web.format.BooleanFormatter;
052: import org.kuali.core.web.format.CollectionFormatter;
053: import org.kuali.core.web.format.DateFormatter;
054: import org.kuali.core.web.format.Formatter;
055: import org.kuali.core.web.struts.form.LookupForm;
056: import org.kuali.core.web.ui.Column;
057: import org.kuali.core.web.ui.ResultRow;
058: import org.kuali.core.web.ui.Row;
059: import org.kuali.rice.KNSServiceLocator;
060:
061: /**
062: * This class declares many of the common spring injected properties, the get/set-ers for them,
063: * and some common util methods that require the injected services
064: */
065: public abstract class AbstractLookupableHelperServiceImpl implements
066: LookupableHelperService {
067:
068: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
069: .getLogger(AbstractLookupableHelperServiceImpl.class);
070:
071: private Class businessObjectClass;
072: private BusinessObjectDictionaryService businessObjectDictionaryService;
073: private BusinessObjectMetaDataService businessObjectMetaDataService;
074: private DataDictionaryService dataDictionaryService;
075: private PersistenceStructureService persistenceStructureService;
076: private EncryptionService encryptionService;
077: private List<String> readOnlyFieldsList;
078: private String backLocation;
079: private String docFormKey;
080: private Map fieldConversions;
081: private LookupService lookupService;
082: private UniversalUserService universalUserService;
083: private List<Row> rows;
084: private String referencesToRefresh;
085: private SequenceAccessorService sequenceAccessorService;
086: private BusinessObjectService businessObjectService;
087: private LookupResultsService lookupResultsService;
088:
089: public AbstractLookupableHelperServiceImpl() {
090: rows = null;
091: }
092:
093: /**
094: * This implementation always returns false.
095: *
096: * @see org.kuali.core.lookup.LookupableHelperService#checkForAdditionalFields(java.util.Map)
097: */
098: public boolean checkForAdditionalFields(Map fieldValues) {
099: return false;
100: }
101:
102: /**
103: * Build a maintenanace url.
104: *
105: * @param bo - business object representing the record for maint.
106: * @param methodToCall - maintenance action
107: * @return
108: */
109: public String getMaintenanceUrl(BusinessObject businessObject,
110: String methodToCall) {
111: // TODO: considering making visibility "protected"
112: Properties parameters = new Properties();
113: parameters.put(RiceConstants.DISPATCH_REQUEST_PARAMETER,
114: methodToCall);
115: parameters.put(RiceConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE,
116: this .businessObjectClass.getName());
117:
118: String encryptedList = "";
119:
120: List pkNames = getPersistenceStructureService()
121: .listPrimaryKeyFieldNames(getBusinessObjectClass());
122: for (Iterator iter = pkNames.iterator(); iter.hasNext();) {
123: String fieldNm = (String) iter.next();
124:
125: Object fieldVal = ObjectUtils.getPropertyValue(
126: businessObject, fieldNm);
127: if (fieldVal == null) {
128: fieldVal = RiceConstants.EMPTY_STRING;
129: }
130: if (fieldVal instanceof java.sql.Date) {
131: String formattedString = "";
132: if (Formatter.findFormatter(fieldVal.getClass()) != null) {
133: Formatter formatter = Formatter
134: .getFormatter(fieldVal.getClass());
135: formattedString = (String) formatter
136: .format(fieldVal);
137: fieldVal = formattedString;
138: }
139: }
140:
141: // Encrypt value if it is a secure field
142: String displayWorkgroup = dataDictionaryService
143: .getAttributeDisplayWorkgroup(businessObject
144: .getClass(), fieldNm);
145: if (StringUtils.isNotBlank(displayWorkgroup)) {
146: try {
147: fieldVal = getEncryptionService().encrypt(fieldVal);
148: } catch (GeneralSecurityException e) {
149: LOG
150: .error(
151: "Exception while trying to encrypted value for inquiry framework.",
152: e);
153: throw new RuntimeException(e);
154: }
155:
156: // add to parameter list so that KualiInquiryAction can identify which parameters are encrypted
157: if (encryptedList.equals("")) {
158: encryptedList = fieldNm;
159: } else {
160: encryptedList = encryptedList
161: + RiceConstants.FIELD_CONVERSIONS_SEPERATOR
162: + fieldNm;
163: }
164: }
165:
166: parameters.put(fieldNm, fieldVal.toString());
167: }
168:
169: // if we did encrypt a value (or values), add the list of those that are encrypted to the parameters
170: if (!encryptedList.equals("")) {
171: parameters.put(RiceConstants.ENCRYPTED_LIST_PREFIX,
172: encryptedList);
173: }
174:
175: // FIXME: either use UrlFactory or hardcode url
176: String url = UrlFactory.parameterizeUrl(
177: RiceConstants.MAINTENANCE_ACTION, parameters);
178: url = "<a href=\"" + url + "\">" + methodToCall + "</a>";
179: return url;
180: }
181:
182: /**
183: * @see org.kuali.core.lookup.LookupableHelperService#getBusinessObjectClass()
184: */
185: public Class getBusinessObjectClass() {
186: return businessObjectClass;
187: }
188:
189: /**
190: * @see org.kuali.core.lookup.LookupableHelperService#setBusinessObjectClass(java.lang.Class)
191: */
192: public void setBusinessObjectClass(Class businessObjectClass) {
193: this .businessObjectClass = businessObjectClass;
194: setRows();
195: }
196:
197: /**
198: * Gets the dataDictionaryService attribute.
199: * @return Returns the dataDictionaryService.
200: */
201: public DataDictionaryService getDataDictionaryService() {
202: return dataDictionaryService;
203: }
204:
205: /**
206: * Sets the dataDictionaryService attribute value.
207: * @param dataDictionaryService The dataDictionaryService to set.
208: */
209: public void setDataDictionaryService(
210: DataDictionaryService dataDictionaryService) {
211: this .dataDictionaryService = dataDictionaryService;
212: }
213:
214: /**
215: * Gets the businessObjectDictionaryService attribute.
216: * @return Returns the businessObjectDictionaryService.
217: */
218: public BusinessObjectDictionaryService getBusinessObjectDictionaryService() {
219: return businessObjectDictionaryService;
220: }
221:
222: /**
223: * Sets the businessObjectDictionaryService attribute value.
224: * @param businessObjectDictionaryService The businessObjectDictionaryService to set.
225: */
226: public void setBusinessObjectDictionaryService(
227: BusinessObjectDictionaryService businessObjectDictionaryService) {
228: this .businessObjectDictionaryService = businessObjectDictionaryService;
229: }
230:
231: /**
232: * Gets the businessObjectMetaDataService attribute.
233: * @return Returns the businessObjectMetaDataService.
234: */
235: public BusinessObjectMetaDataService getBusinessObjectMetaDataService() {
236: return businessObjectMetaDataService;
237: }
238:
239: /**
240: * Sets the businessObjectMetaDataService attribute value.
241: * @param businessObjectMetaDataService The businessObjectMetaDataService to set.
242: */
243: public void setBusinessObjectMetaDataService(
244: BusinessObjectMetaDataService businessObjectMetaDataService) {
245: this .businessObjectMetaDataService = businessObjectMetaDataService;
246: }
247:
248: /**
249: * Gets the persistenceStructureService attribute.
250: * @return Returns the persistenceStructureService.
251: */
252: protected PersistenceStructureService getPersistenceStructureService() {
253: return persistenceStructureService;
254: }
255:
256: /**
257: * Sets the persistenceStructureService attribute value.
258: * @param persistenceStructureService The persistenceStructureService to set.
259: */
260: public void setPersistenceStructureService(
261: PersistenceStructureService persistenceStructureService) {
262: this .persistenceStructureService = persistenceStructureService;
263: }
264:
265: /**
266: * Gets the encryptionService attribute.
267: * @return Returns the encryptionService.
268: */
269: protected EncryptionService getEncryptionService() {
270: return encryptionService;
271: }
272:
273: /**
274: * Sets the encryptionService attribute value.
275: * @param encryptionService The encryptionService to set.
276: */
277: public void setEncryptionService(EncryptionService encryptionService) {
278: this .encryptionService = encryptionService;
279: }
280:
281: /**
282: * Determines if underlying lookup bo has associated maintenance document that allows new or copy maintenance actions.
283: *
284: * @return true if bo has maint doc that allows new or copy actions
285: */
286: public boolean allowsMaintenanceNewOrCopyAction() {
287: boolean allowsNewOrCopy = false;
288:
289: String maintDocTypeName = getMaintenanceDocumentTypeName();
290: if (StringUtils.isNotBlank(maintDocTypeName)) {
291: allowsNewOrCopy = KNSServiceLocator
292: .getMaintenanceDocumentDictionaryService()
293: .getAllowsNewOrCopy(maintDocTypeName);
294: }
295:
296: return allowsNewOrCopy;
297: }
298:
299: /**
300: * @returns links to edit and copy maintenance action for the current maintenance record if the business object
301: * class has an associated maintenance document. Also checks value of allowsNewOrCopy in maintenance document xml
302: * before rendering the copy link.
303: *
304: * @see org.kuali.core.lookup.LookupableHelperService#getActionUrls(org.kuali.core.bo.BusinessObject)
305: */
306: public String getActionUrls(BusinessObject businessObject) {
307: StringBuffer actions = new StringBuffer();
308: if (StringUtils.isNotBlank(getMaintenanceDocumentTypeName())) {
309: actions.append(getMaintenanceUrl(businessObject,
310: RiceConstants.MAINTENANCE_EDIT_METHOD_TO_CALL));
311: }
312:
313: if (allowsMaintenanceNewOrCopyAction()) {
314: actions.append(" ");
315: actions.append(getMaintenanceUrl(businessObject,
316: RiceConstants.MAINTENANCE_COPY_METHOD_TO_CALL));
317: }
318:
319: return actions.toString();
320: }
321:
322: /**
323: * Returns the maintenance document type associated with the business object class or null if one does not
324: * exist.
325: * @return String representing the maintenance document type name
326: */
327: protected String getMaintenanceDocumentTypeName() {
328: MaintenanceDocumentDictionaryService dd = KNSServiceLocator
329: .getMaintenanceDocumentDictionaryService();
330: String maintDocTypeName = dd
331: .getDocumentTypeName(getBusinessObjectClass());
332: return maintDocTypeName;
333: }
334:
335: /**
336: * Gets the readOnlyFieldsList attribute.
337: *
338: * @return Returns the readOnlyFieldsList.
339: */
340: public List<String> getReadOnlyFieldsList() {
341: return readOnlyFieldsList;
342: }
343:
344: /**
345: * Sets the readOnlyFieldsList attribute value.
346: *
347: * @param readOnlyFieldsList The readOnlyFieldsList to set.
348: */
349: public void setReadOnlyFieldsList(List<String> readOnlyFieldsList) {
350: this .readOnlyFieldsList = readOnlyFieldsList;
351: }
352:
353: /**
354: * Returns the inquiry url for a field if one exist.
355: *
356: * @param bo the business object instance to build the urls for
357: * @param propertyName the property which links to an inquirable
358: * @return String url to inquiry
359: */
360: public String getInquiryUrl(BusinessObject bo, String propertyName) {
361: String inquiryUrl = "";
362:
363: if (getBusinessObjectDictionaryService()
364: .noLookupResultFieldInquiry(bo.getClass(), propertyName) != null
365: && !(getBusinessObjectDictionaryService()
366: .noLookupResultFieldInquiry(bo.getClass(),
367: propertyName)).booleanValue()) {
368: Class<Inquirable> inquirableClass = businessObjectDictionaryService
369: .getInquirableClass(bo.getClass());
370: Inquirable inq = null;
371: try {
372: if (inquirableClass != null) {
373: inq = inquirableClass.newInstance();
374: } else {
375: inq = KNSServiceLocator.getKualiInquirable();
376: if (LOG.isDebugEnabled()) {
377: LOG.debug("Default Inquirable Class: "
378: + inq.getClass());
379: }
380: }
381: inquiryUrl = inq.getInquiryUrl(bo, propertyName,
382: (getBusinessObjectDictionaryService()
383: .forceLookupResultFieldInquiry(bo
384: .getClass(), propertyName))
385: .booleanValue());
386: } catch (Exception ex) {
387: LOG
388: .error(
389: "unable to create inquirable to get inquiry URL",
390: ex);
391: }
392: }
393:
394: return inquiryUrl;
395: }
396:
397: /**
398: * Constructs the list of columns for the search results. All properties for the column objects come from the DataDictionary.
399: */
400: public List<Column> getColumns() {
401: List<Column> columns = new ArrayList<Column>();
402: for (String attributeName : getBusinessObjectDictionaryService()
403: .getLookupResultFieldNames(getBusinessObjectClass())) {
404: Column column = new Column();
405: column.setPropertyName(attributeName);
406: String columnTitle = dataDictionaryService
407: .getAttributeLabel(getBusinessObjectClass(),
408: attributeName);
409: if (StringUtils.isBlank(columnTitle)) {
410: columnTitle = dataDictionaryService.getCollectionLabel(
411: getBusinessObjectClass(), attributeName);
412: }
413: column.setColumnTitle(columnTitle);
414: column.setMaxLength(getColumnMaxLength(attributeName));
415:
416: Class formatterClass = dataDictionaryService
417: .getAttributeFormatter(getBusinessObjectClass(),
418: attributeName);
419: if (formatterClass != null) {
420: try {
421: column.setFormatter((Formatter) formatterClass
422: .newInstance());
423: } catch (InstantiationException e) {
424: LOG
425: .error("Unable to get new instance of formatter class: "
426: + formatterClass.getName());
427: throw new RuntimeException(
428: "Unable to get new instance of formatter class: "
429: + formatterClass.getName());
430: } catch (IllegalAccessException e) {
431: LOG
432: .error("Unable to get new instance of formatter class: "
433: + formatterClass.getName());
434: throw new RuntimeException(
435: "Unable to get new instance of formatter class: "
436: + formatterClass.getName());
437: }
438: }
439:
440: columns.add(column);
441: }
442: return columns;
443: }
444:
445: protected int getColumnMaxLength(String attributeName) {
446: Integer fieldDefinedMaxLength = getBusinessObjectDictionaryService()
447: .getLookupResultFieldMaxLength(
448: getBusinessObjectClass(), attributeName);
449: if (fieldDefinedMaxLength == null) {
450: String valueStr = KNSServiceLocator
451: .getKualiConfigurationService()
452: .getParameterValue(
453: RiceConstants.KNS_NAMESPACE,
454: RiceConstants.DetailTypes.LOOKUP_PARM_DETAIL_TYPE,
455: RiceConstants.RESULTS_DEFAULT_MAX_COLUMN_LENGTH);
456: if (valueStr == null) {
457: LOG
458: .error("Lookup field max length parameter not found and field not set with max length value.");
459: }
460: return Integer.parseInt(valueStr);
461: }
462: return fieldDefinedMaxLength.intValue();
463: }
464:
465: /**
466: * @return Returns the backLocation.
467: */
468: public String getBackLocation() {
469: return backLocation;
470: }
471:
472: /**
473: * @param backLocation The backLocation to set.
474: */
475: public void setBackLocation(String backLocation) {
476: this .backLocation = backLocation;
477: }
478:
479: /**
480: * @see org.kuali.core.lookup.LookupableHelperService#getReturnLocation()
481: */
482: public String getReturnLocation() {
483: return backLocation;
484: }
485:
486: /**
487: * @see org.kuali.core.lookup.LookupableHelperService#getReturnUrl(org.kuali.core.bo.BusinessObject, java.util.Map, java.lang.String)
488: */
489: public String getReturnUrl(BusinessObject businessObject,
490: Map fieldConversions, String lookupImpl) {
491: String url = UrlFactory.parameterizeUrl(backLocation,
492: getParameters(businessObject, fieldConversions,
493: lookupImpl));
494: return url;
495: }
496:
497: protected Properties getParameters(BusinessObject bo,
498: Map fieldConversions, String lookupImpl) {
499: Properties parameters = new Properties();
500: parameters.put(RiceConstants.DISPATCH_REQUEST_PARAMETER,
501: RiceConstants.RETURN_METHOD_TO_CALL);
502: parameters.put(RiceConstants.DOC_FORM_KEY, getDocFormKey());
503: parameters.put(RiceConstants.REFRESH_CALLER, lookupImpl);
504: if (getReferencesToRefresh() != null) {
505: parameters.put(RiceConstants.REFERENCES_TO_REFRESH,
506: getReferencesToRefresh());
507: }
508:
509: String encryptedList = "";
510:
511: Iterator returnKeys = getReturnKeys().iterator();
512: while (returnKeys.hasNext()) {
513: String fieldNm = (String) returnKeys.next();
514:
515: Object fieldVal = ObjectUtils.getPropertyValue(bo, fieldNm);
516: if (fieldVal == null) {
517: fieldVal = RiceConstants.EMPTY_STRING;
518: }
519:
520: // Encrypt value if it is a secure field
521: String displayWorkgroup = dataDictionaryService
522: .getAttributeDisplayWorkgroup(bo.getClass(),
523: fieldNm);
524:
525: if (fieldConversions.containsKey(fieldNm)) {
526: fieldNm = (String) fieldConversions.get(fieldNm);
527: }
528:
529: if (StringUtils.isNotBlank(displayWorkgroup)
530: && !GlobalVariables.getUserSession()
531: .getUniversalUser().isMember(
532: displayWorkgroup)) {
533: try {
534: fieldVal = encryptionService.encrypt(fieldVal);
535: } catch (GeneralSecurityException e) {
536: LOG
537: .error(
538: "Exception while trying to encrypted value for inquiry framework.",
539: e);
540: throw new RuntimeException(e);
541: }
542:
543: // add to parameter list so that KualiInquiryAction can identify which parameters are encrypted
544: if (encryptedList.equals("")) {
545: encryptedList = fieldNm;
546: } else {
547: encryptedList = encryptedList
548: + RiceConstants.FIELD_CONVERSIONS_SEPERATOR
549: + fieldNm;
550: }
551: }
552:
553: //need to format date in url
554: if (fieldVal instanceof Date) {
555: DateFormatter dateFormatter = new DateFormatter();
556: fieldVal = dateFormatter.format(fieldVal);
557: }
558:
559: parameters.put(fieldNm, fieldVal.toString());
560: }
561:
562: // if we did encrypt a value (or values), add the list of those that are encrypted to the parameters
563: if (!encryptedList.equals("")) {
564: parameters.put(RiceConstants.ENCRYPTED_LIST_PREFIX,
565: encryptedList);
566: }
567:
568: return parameters;
569: }
570:
571: /**
572: * @return a List of the names of fields which are marked in data dictionary as return fields.
573: */
574: public List getReturnKeys() {
575: List returnKeys;
576: if (fieldConversions != null && !fieldConversions.isEmpty()) {
577: returnKeys = new ArrayList(fieldConversions.keySet());
578: } else {
579: returnKeys = getPersistenceStructureService()
580: .listPrimaryKeyFieldNames(getBusinessObjectClass());
581: }
582:
583: return returnKeys;
584: }
585:
586: /**
587: * Gets the docFormKey attribute.
588: * @return Returns the docFormKey.
589: */
590: public String getDocFormKey() {
591: return docFormKey;
592: }
593:
594: /**
595: * Sets the docFormKey attribute value.
596: * @param docFormKey The docFormKey to set.
597: */
598: public void setDocFormKey(String docFormKey) {
599: this .docFormKey = docFormKey;
600: }
601:
602: /**
603: * @see org.kuali.core.lookup.LookupableHelperService#setFieldConversions(java.util.Map)
604: */
605: public void setFieldConversions(Map fieldConversions) {
606: this .fieldConversions = fieldConversions;
607: }
608:
609: /**
610: * Gets the lookupService attribute.
611: * @return Returns the lookupService.
612: */
613: protected LookupService getLookupService() {
614: return lookupService;
615: }
616:
617: /**
618: * Sets the lookupService attribute value.
619: * @param lookupService The lookupService to set.
620: */
621: public void setLookupService(LookupService lookupService) {
622: this .lookupService = lookupService;
623: }
624:
625: /**
626: * Uses the DD to determine which is the default sort order.
627: *
628: * @return property names that will be used to sort on by default
629: */
630: public List getDefaultSortColumns() {
631: return getBusinessObjectDictionaryService()
632: .getLookupDefaultSortFieldNames(
633: getBusinessObjectClass());
634: }
635:
636: /**
637: * Checks that any required search fields have value.
638: *
639: * @see org.kuali.core.lookup.LookupableHelperService#validateSearchParameters(java.util.Map)
640: */
641: public void validateSearchParameters(Map fieldValues) {
642: List<String> lookupFieldAttributeList = null;
643: if (getBusinessObjectMetaDataService().isLookupable(
644: getBusinessObjectClass())) {
645: lookupFieldAttributeList = getBusinessObjectMetaDataService()
646: .getLookupableFieldNames(getBusinessObjectClass());
647: }
648: if (lookupFieldAttributeList == null) {
649: throw new RuntimeException(
650: "Lookup not defined for business object "
651: + getBusinessObjectClass());
652: }
653: for (Iterator iter = lookupFieldAttributeList.iterator(); iter
654: .hasNext();) {
655: String attributeName = (String) iter.next();
656: if (fieldValues.containsKey(attributeName)) {
657: // get label of attribute for message
658: String attributeLabel = getDataDictionaryService()
659: .getAttributeLabel(getBusinessObjectClass(),
660: attributeName);
661:
662: String attributeValue = (String) fieldValues
663: .get(attributeName);
664:
665: boolean isSecureField = !StringUtils
666: .isBlank(getDataDictionaryService()
667: .getAttributeDisplayWorkgroup(
668: getBusinessObjectClass(),
669: attributeName));
670:
671: // check for required if field does not have value
672: if (StringUtils.isBlank(attributeValue)) {
673: if ((getBusinessObjectDictionaryService()
674: .getLookupAttributeRequired(
675: getBusinessObjectClass(),
676: attributeName)).booleanValue()) {
677: GlobalVariables.getErrorMap().putError(
678: attributeName,
679: RiceKeyConstants.ERROR_REQUIRED,
680: attributeLabel);
681: }
682: } else if (isSecureField) {
683: // following loop would be trivial if Constants.QUERY_CHARACTERS would implement CharSequence but not so
684: // sure if that makes sense...
685: for (int i = 0; i < RiceConstants.QUERY_CHARACTERS.length; i++) {
686: String queryCharacter = RiceConstants.QUERY_CHARACTERS[i];
687:
688: if (attributeValue.contains(queryCharacter)) {
689: GlobalVariables
690: .getErrorMap()
691: .putError(
692: attributeName,
693: RiceKeyConstants.ERROR_SECURE_FIELD,
694: attributeLabel);
695: }
696: }
697: }
698: }
699: }
700:
701: if (!GlobalVariables.getErrorMap().isEmpty()) {
702: throw new ValidationException("errors in search criteria");
703: }
704: }
705:
706: protected UniversalUserService getUniversalUserService() {
707: return universalUserService;
708: }
709:
710: public void setUniversalUserService(
711: UniversalUserService universalUserService) {
712: this .universalUserService = universalUserService;
713: }
714:
715: /**
716: * Constructs the list of rows for the search fields. All properties for the field objects come from the DataDictionary.
717: * To be called by setBusinessObject
718: */
719: protected void setRows() {
720: List localRows = new ArrayList();
721: List<String> lookupFieldAttributeList = null;
722: if (getBusinessObjectMetaDataService().isLookupable(
723: getBusinessObjectClass())) {
724: lookupFieldAttributeList = getBusinessObjectMetaDataService()
725: .getLookupableFieldNames(getBusinessObjectClass());
726: }
727: if (lookupFieldAttributeList == null) {
728: throw new RuntimeException(
729: "Lookup not defined for business object "
730: + getBusinessObjectClass());
731: }
732:
733: // construct field object for each search attribute
734: List fields = new ArrayList();
735: try {
736: fields = FieldUtils.createAndPopulateFieldsForLookup(
737: lookupFieldAttributeList, getReadOnlyFieldsList(),
738: getBusinessObjectClass());
739:
740: } catch (InstantiationException e) {
741: throw new RuntimeException(
742: "Unable to create instance of business object class"
743: + e.getMessage());
744: } catch (IllegalAccessException e) {
745: throw new RuntimeException(
746: "Unable to create instance of business object class"
747: + e.getMessage());
748: }
749: this .rows = FieldUtils.wrapFields(fields);
750: }
751:
752: public List<Row> getRows() {
753: return rows;
754: }
755:
756: public abstract List<? extends BusinessObject> getSearchResults(
757: Map<String, String> fieldValues);
758:
759: /**
760: * This implementation of this method throws an UnsupportedOperationException, since not every implementation
761: * may actually want to use this operation. Subclasses desiring other behaviors
762: * will need to override this.
763: *
764: * @see org.kuali.core.lookup.LookupableHelperService#getSearchResultsUnbounded(java.util.Map)
765: */
766: public List<? extends BusinessObject> getSearchResultsUnbounded(
767: Map<String, String> fieldValues) {
768: throw new UnsupportedOperationException(
769: "Lookupable helper services do not always support getSearchResultsUnbounded");
770: }
771:
772: /**
773: *
774: * This method performs the lookup and returns a collection of lookup items
775: * @param lookupForm
776: * @param kualiLookupable
777: * @param resultTable
778: * @param bounded
779: * @return
780: */
781: public Collection performLookup(LookupForm lookupForm,
782: Collection resultTable, boolean bounded) {
783: Collection displayList;
784:
785: // call search method to get results
786: if (bounded) {
787: displayList = getSearchResults(lookupForm
788: .getFieldsForLookup());
789: } else {
790: displayList = getSearchResultsUnbounded(lookupForm
791: .getFieldsForLookup());
792: }
793:
794: // iterate through result list and wrap rows with return url and action urls
795: for (Iterator iter = displayList.iterator(); iter.hasNext();) {
796: BusinessObject element = (BusinessObject) iter.next();
797:
798: String returnUrl = getReturnUrl(element, lookupForm
799: .getFieldConversions(), lookupForm
800: .getLookupableImplServiceName());
801: String actionUrls = getActionUrls(element);
802:
803: List<Column> columns = getColumns();
804: List<Column> rowColumns = new ArrayList<Column>();
805: for (Iterator iterator = columns.iterator(); iterator
806: .hasNext();) {
807:
808: Column col = (Column) iterator.next();
809: Formatter formatter = col.getFormatter();
810:
811: // pick off result column from result list, do formatting
812: String propValue = RiceConstants.EMPTY_STRING;
813: Object prop = ObjectUtils.getPropertyValue(element, col
814: .getPropertyName());
815:
816: // set comparator and formatter based on property type
817: Class propClass = null;
818: try {
819: propClass = ObjectUtils.getPropertyType(element,
820: col.getPropertyName(),
821: getPersistenceStructureService());
822: } catch (Exception e) {
823: throw new RuntimeException(
824: "Cannot access PropertyType for property "
825: + "'" + col.getPropertyName()
826: + "' " + " on an instance of '"
827: + element.getClass().getName()
828: + "'.", e);
829: }
830:
831: // formatters
832: if (prop != null) {
833: // for Booleans, always use BooleanFormatter
834: if (prop instanceof Boolean) {
835: formatter = new BooleanFormatter();
836: }
837:
838: // for Dates, always use DateFormatter
839: if (prop instanceof Date) {
840: formatter = new DateFormatter();
841: }
842:
843: // for collection, use the list formatter if a formatter hasn't been defined yet
844: if (prop instanceof Collection && formatter == null) {
845: formatter = new CollectionFormatter();
846: }
847:
848: if (formatter != null) {
849: propValue = (String) formatter.format(prop);
850: } else {
851: propValue = prop.toString();
852: }
853: }
854:
855: // comparator
856: col
857: .setComparator(CellComparatorHelper
858: .getAppropriateComparatorForPropertyClass(propClass));
859: col
860: .setValueComparator(CellComparatorHelper
861: .getAppropriateValueComparatorForPropertyClass(propClass));
862:
863: // check security on field and do masking if necessary
864: boolean viewAuthorized = KNSServiceLocator
865: .getAuthorizationService()
866: .isAuthorizedToViewAttribute(
867: GlobalVariables.getUserSession()
868: .getUniversalUser(),
869: element.getClass().getName(),
870: col.getPropertyName());
871: if (!viewAuthorized) {
872: Mask displayMask = getDataDictionaryService()
873: .getAttributeDisplayMask(
874: element.getClass().getName(),
875: col.getPropertyName());
876: propValue = displayMask.maskValue(propValue);
877: }
878: col.setPropertyValue(propValue);
879:
880: if (StringUtils.isNotBlank(propValue)) {
881: col.setPropertyURL(getInquiryUrl(element, col
882: .getPropertyName()));
883: }
884:
885: rowColumns.add(col);
886: }
887:
888: ResultRow row = new ResultRow(rowColumns, returnUrl,
889: actionUrls);
890: if (element instanceof PersistableBusinessObject) {
891: row.setObjectId(((PersistableBusinessObject) element)
892: .getObjectId());
893: }
894: resultTable.add(row);
895: }
896:
897: return displayList;
898: }
899:
900: protected void setReferencesToRefresh(String referencesToRefresh) {
901: this .referencesToRefresh = referencesToRefresh;
902: }
903:
904: public String getReferencesToRefresh() {
905: return referencesToRefresh;
906: }
907:
908: protected SequenceAccessorService getSequenceAccessorService() {
909: return sequenceAccessorService;
910: }
911:
912: public void setSequenceAccessorService(
913: SequenceAccessorService sequenceAccessorService) {
914: this .sequenceAccessorService = sequenceAccessorService;
915: }
916:
917: public BusinessObjectService getBusinessObjectService() {
918: return businessObjectService;
919: }
920:
921: public void setBusinessObjectService(
922: BusinessObjectService businessObjectService) {
923: this .businessObjectService = businessObjectService;
924: }
925:
926: protected LookupResultsService getLookupResultsService() {
927: return lookupResultsService;
928: }
929:
930: public void setLookupResultsService(
931: LookupResultsService lookupResultsService) {
932: this .lookupResultsService = lookupResultsService;
933: }
934:
935: /**
936: * @return false always, subclasses should override to do something smarter
937: * @see org.kuali.core.lookup.LookupableHelperService#isSearchUsingOnlyPrimaryKeyValues()
938: */
939: public boolean isSearchUsingOnlyPrimaryKeyValues() {
940: // by default, this implementation returns false, as lookups may not necessarily support this
941: return false;
942: }
943:
944: /**
945: * Returns "N/A"
946: *
947: * @return "N/A"
948: * @see org.kuali.core.lookup.LookupableHelperService#getPrimaryKeyFieldLabels()
949: */
950: public String getPrimaryKeyFieldLabels() {
951: return RiceConstants.NOT_AVAILABLE_STRING;
952: }
953: }
|