001/** 002 * Copyright 2005-2016 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 */ 016package org.kuali.rice.kns.lookup; 017 018import java.lang.reflect.InvocationTargetException; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import org.apache.commons.beanutils.PropertyUtils; 027import org.apache.commons.lang.StringUtils; 028import org.kuali.rice.core.api.search.SearchOperator; 029import org.kuali.rice.core.web.format.Formatter; 030import org.kuali.rice.kns.datadictionary.BusinessObjectEntry; 031import org.kuali.rice.kns.service.KNSServiceLocator; 032import org.kuali.rice.krad.bo.BusinessObject; 033import org.kuali.rice.krad.datadictionary.AttributeDefinition; 034import org.kuali.rice.krad.service.DataDictionaryService; 035 036/** 037 * LookupResults support strategy which uses the primary keys and lookupable defined in a business object's data dictionary file to support the multivalue lookup 038 * 039 * @author Kuali Rice Team (rice.collab@kuali.org) 040 * 041 * @deprecated Only used by KNS classes, use KRAD. 042 */ 043@Deprecated 044public class DataDictionaryLookupResultsSupportStrategy implements 045 LookupResultsSupportStrategyService { 046 047 private DataDictionaryService dataDictionaryService; 048 049 /** 050 * Builds a lookup id for the given business object 051 * @see LookupResultsSupportStrategyService#getLookupIdForBusinessObject(org.kuali.rice.krad.bo.BusinessObject) 052 */ 053 @Override 054 public String getLookupIdForBusinessObject(BusinessObject businessObject) { 055 final List<String> pkFieldNames = getPrimaryKeyFieldsForBusinessObject(businessObject.getClass()); 056 return convertPKFieldMapToLookupId(pkFieldNames, businessObject); 057 } 058 059 /** 060 * Determines if both the primary keys and Lookupable are present in the data dictionary definition; if so, the BO qualifies, but if either are missing, it does not 061 * @see LookupResultsSupportStrategyService#qualifiesForStrategy(java.lang.Class) 062 */ 063 @Override 064 public boolean qualifiesForStrategy(Class<? extends BusinessObject> boClass) { 065 if (getLookupableForBusinessObject(boClass) == null) { 066 return false; // this probably isn't going to happen, but still... 067 } 068 final List<String> pkFields = getPrimaryKeyFieldsForBusinessObject(boClass); 069 return pkFields != null && pkFields.size() > 0; 070 } 071 072 /** 073 * Uses the Lookupable associated with the given BusinessObject to search for business objects 074 * @see LookupResultsSupportStrategyService#retrieveSelectedResultBOs(java.lang.String, java.lang.Class, java.lang.String, org.kuali.rice.krad.lookup.LookupResultsService) 075 */ 076 @Override 077 public <T extends BusinessObject> Collection<T> retrieveSelectedResultBOs(Class<T> boClass, Set<String> lookupIds) throws Exception { 078 079 List<T> retrievedBusinessObjects = new ArrayList<T>(); 080 final org.kuali.rice.kns.lookup.Lookupable lookupable = getLookupableForBusinessObject(boClass); 081 for (String lookupId : lookupIds) { 082 final Map<String, String> lookupKeys = convertLookupIdToPKFieldMap(lookupId, boClass); 083 List<? extends BusinessObject> bos = lookupable.getSearchResults(lookupKeys); 084 085 // we should only get one business object...but let's put them all in... 086 for (BusinessObject bo : bos) { 087 retrievedBusinessObjects.add((T)bo); 088 } 089 } 090 091 return retrievedBusinessObjects; 092 } 093 094 /** 095 * Retrieves the Lookupable for the given business object class 096 * 097 * @param businessObjectClass the class to find the Lookupable for 098 * @return the Lookupable, or null if nothing could be found 099 */ 100 protected org.kuali.rice.kns.lookup.Lookupable getLookupableForBusinessObject(Class<? extends BusinessObject> businessObjectClass) { 101 final BusinessObjectEntry boEntry = getBusinessObjectEntry(businessObjectClass); 102 if (boEntry == null) { 103 return null; 104 } 105 106 final String lookupableId = boEntry.getLookupDefinition().getLookupableID(); 107 if ( StringUtils.isBlank(lookupableId) ) { 108 return null; // the call below fails if you pass a null service name 109 } 110 return KNSServiceLocator.getLookupable(lookupableId); 111 } 112 113 /** 114 * Returns the data dictionary defined primary keys for the given BusinessObject 115 * 116 * @param businessObjectClass the business object to get DataDictionary defined primary keys for 117 * @return the List of primary key property names, or null if nothing could be found 118 */ 119 protected List<String> getPrimaryKeyFieldsForBusinessObject(Class<? extends BusinessObject> businessObjectClass) { 120 final BusinessObjectEntry boEntry = getBusinessObjectEntry(businessObjectClass); 121 if (boEntry == null) { 122 return null; 123 } 124 return boEntry.getPrimaryKeys(); 125 } 126 127 /** 128 * Converts a lookup id into a PK field map to use to search in a lookupable 129 * 130 * @param lookupId the id returned by the lookup 131 * @param businessObjectClass the class of the business object getting the primary key 132 * @return a Map of field names and values which can be profitably used to search for matching business objects 133 */ 134 protected Map<String, String> convertLookupIdToPKFieldMap(String lookupId, Class<? extends BusinessObject> businessObjectClass) { 135 Map<String, String> pkFields = new HashMap<String, String>(); 136 if (!StringUtils.isBlank(lookupId)) { 137 final String[] pkValues = lookupId.split("\\|"); 138 for (String pkValue : pkValues) { 139 if (!StringUtils.isBlank(pkValue)) { 140 final String[] pkPieces = pkValue.split("-"); 141 if (!StringUtils.isBlank(pkPieces[0]) && !StringUtils.isBlank(pkPieces[1])) { 142 pkFields.put(pkPieces[0], pkPieces[1]); 143 } 144 } 145 } 146 } 147 return pkFields; 148 } 149 150 /** 151 * Converts a Map of PKFields into a String lookup ID 152 * @param pkFieldNames the name of the PK fields, which should be converted to the given lookupId 153 * @param businessObjectClass the class of the business object getting the primary key 154 * @return the String lookup id 155 */ 156 protected String convertPKFieldMapToLookupId(List<String> pkFieldNames, BusinessObject businessObject) { 157 StringBuilder lookupId = new StringBuilder(); 158 for (String pkFieldName : pkFieldNames) { 159 try { 160 final Object value = PropertyUtils.getProperty(businessObject, pkFieldName); 161 162 if (value != null) { 163 lookupId.append(pkFieldName); 164 lookupId.append("-"); 165 final Formatter formatter = retrieveBestFormatter(pkFieldName, businessObject.getClass()); 166 final String formattedValue = (formatter != null) ? formatter.format(value).toString() : value.toString(); 167 168 lookupId.append(formattedValue); 169 } 170 lookupId.append(SearchOperator.OR.op()); 171 } catch (IllegalAccessException iae) { 172 throw new RuntimeException("Could not retrieve pk field value "+pkFieldName+" from business object "+businessObject.getClass().getName(), iae); 173 } catch (InvocationTargetException ite) { 174 throw new RuntimeException("Could not retrieve pk field value "+pkFieldName+" from business object "+businessObject.getClass().getName(), ite); 175 } catch (NoSuchMethodException nsme) { 176 throw new RuntimeException("Could not retrieve pk field value "+pkFieldName+" from business object "+businessObject.getClass().getName(), nsme); 177 } 178 } 179 return lookupId.substring(0, lookupId.length() - 1); // kill the last "|" 180 } 181 182 /** 183 * Like when you're digging through your stuff drawer, you know the one in the kitchen with all the batteries and lint in it, this method 184 * goes through the stuff drawer of KNS formatters and attempts to return you a good one 185 * 186 * @param propertyName the name of the property to retrieve 187 * @param boClass the class of the BusinessObject the property is on 188 * @return a Formatter, or null if we were unsuccessful in finding 189 */ 190 protected Formatter retrieveBestFormatter(String propertyName, Class<? extends BusinessObject> boClass) { 191 Formatter formatter = null; 192 193 try { 194 Class<? extends Formatter> formatterClass = null; 195 196 final BusinessObjectEntry boEntry = getBusinessObjectEntry(boClass); 197 if (boEntry != null) { 198 final AttributeDefinition attributeDefinition = boEntry.getAttributeDefinition(propertyName); 199 if (attributeDefinition != null && attributeDefinition.hasFormatterClass()) { 200 formatterClass = (Class<? extends Formatter>)Class.forName(attributeDefinition.getFormatterClass()); 201 } 202 } 203 if (formatterClass == null) { 204 final java.lang.reflect.Field propertyField = boClass.getDeclaredField(propertyName); 205 if (propertyField != null) { 206 formatterClass = Formatter.findFormatter(propertyField.getType()); 207 } 208 } 209 210 if (formatterClass != null) { 211 formatter = formatterClass.newInstance(); 212 } 213 } catch (SecurityException se) { 214 throw new RuntimeException("Could not retrieve good formatter", se); 215 } catch (ClassNotFoundException cnfe) { 216 throw new RuntimeException("Could not retrieve good formatter", cnfe); 217 } catch (NoSuchFieldException nsfe) { 218 throw new RuntimeException("Could not retrieve good formatter", nsfe); 219 } catch (IllegalAccessException iae) { 220 throw new RuntimeException("Could not retrieve good formatter", iae); 221 } catch (InstantiationException ie) { 222 throw new RuntimeException("Could not retrieve good formatter", ie); 223 } 224 225 return formatter; 226 } 227 228 /** 229 * Looks up the DataDictionary BusinessObjectEntry for the given class 230 * 231 * @param boClass the class of the BusinessObject to find a BusinessObjectEntry for 232 * @return the entry from the data dictionary, or null if nothing was found 233 */ 234 protected BusinessObjectEntry getBusinessObjectEntry(Class<? extends BusinessObject> boClass) { 235 return (BusinessObjectEntry) getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(boClass.getName()); 236 } 237 238 /** 239 * @return the dataDictionaryService 240 */ 241 public DataDictionaryService getDataDictionaryService() { 242 return this.dataDictionaryService; 243 } 244 245 /** 246 * @param dataDictionaryService the dataDictionaryService to set 247 */ 248 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 249 this.dataDictionaryService = dataDictionaryService; 250 } 251}