001: /*
002: * Copyright 2004 Jonathan M. Lehr
003: *
004: * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
011: * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
012: * governing permissions and limitations under the License.
013: *
014: * MODIFIED BY THE KUALI FOUNDATION
015: */
016:
017: // begin Kuali Foundation modification
018: package org.kuali.core.web.struts.pojo;
019:
020: import java.beans.IntrospectionException;
021: import java.beans.PropertyDescriptor;
022: import java.lang.reflect.InvocationTargetException;
023: import java.lang.reflect.Method;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029:
030: import org.apache.commons.beanutils.MappedPropertyDescriptor;
031: import org.apache.commons.beanutils.NestedNullException;
032: import org.apache.commons.beanutils.PropertyUtils;
033: import org.apache.commons.beanutils.PropertyUtilsBean;
034: import org.apache.commons.collections.FastHashMap;
035: import org.apache.log4j.Logger;
036: import org.kuali.core.web.format.Formatter;
037:
038: /**
039: * begin Kuali Foundation modification
040: * This class is used to access the properties of a Pojo bean.
041: * deleted author tag
042: * end Kuali Foundation modification
043: */
044: // Kuali Foundation modification: class originally SLPropertyUtilsBean
045: public class PojoPropertyUtilsBean extends PropertyUtilsBean {
046:
047: public static final Logger logger = Logger
048: .getLogger(PojoPropertyUtilsBean.class.getName());
049:
050: // begin Kuali Foundation modification
051: public PojoPropertyUtilsBean() {
052: super ();
053: }
054:
055: // end Kuali Foundation modification
056:
057: public Object getProperty(Object bean, String key)
058: throws IllegalAccessException, InvocationTargetException,
059: NoSuchMethodException {
060: // begin Kuali Foundation modification
061: if (!(bean instanceof PojoForm))
062: return super .getProperty(bean, key);
063:
064: PojoForm form = (PojoForm) bean;
065: Map unconvertedValues = form.getUnconvertedValues();
066:
067: if (unconvertedValues.containsKey(key))
068: return unconvertedValues.get(key);
069:
070: Object val = getNestedProperty(bean, key);
071: Class type = String.class;
072: try {
073: type = getPropertyType(bean, key);
074: } catch (Exception ex) {
075: type = String.class;
076: logger.warn("Unable to get property type for Class: "
077: + bean.getClass().getName() + "/Property: " + key);
078: }
079:
080: return (Formatter.isSupportedType(type) ? form.formatValue(val,
081: key, type) : val);
082: // end Kuali Foundation modification
083: }
084:
085: // begin Kuali Foundation modification
086: private Map cache = new HashMap();
087:
088: public Object fastGetNestedProperty(Object o, String s)
089: throws IntrospectionException, IllegalArgumentException,
090: IllegalAccessException, InvocationTargetException {
091: logger.debug("entering fastGetNestedProperty");
092:
093: List methods = (List) cache.get(s + o.getClass().getName());
094: Object[] args = null;
095: if (methods == null) {
096: methods = new ArrayList();
097: Object o2 = o;
098: Class c = o2.getClass();
099:
100: String[] names = s.split("\\.");
101: for (int i = 0; i < names.length; i++) {
102: String name = names[i];
103: PropertyDescriptor p;
104: try {
105: p = new PropertyDescriptor(name, c);
106: logger.debug("created PropertyDescriptor");
107: } catch (IntrospectionException e) {
108: String getterName = "get"
109: + name.substring(0, 1).toUpperCase()
110: + name.substring(1);
111: if (logger.isDebugEnabled()) {
112: logger.debug("using getter named: "
113: + getterName);
114: }
115: p = new PropertyDescriptor(name, c, getterName,
116: null);
117: }
118: Method m = p.getReadMethod();
119: methods.add(m);
120: o2 = m.invoke(o2, args);
121: c = o2.getClass();
122: }
123: cache.put(s + o.getClass().getName(), methods);
124:
125: }
126:
127: Iterator iter = methods.iterator();
128: while (iter.hasNext()) {
129: o = ((Method) iter.next()).invoke(o, args);
130: }
131:
132: logger.debug("exiting fastGetNestedProperty");
133:
134: return o;
135: }
136:
137: // end Kuali Foundation modification
138:
139: /**
140: * begin Kuali Foundation modification
141: * removed comments and @<no space>since javadoc attribute
142: * end Kuali Foundation modification
143: * @see org.apache.commons.beanutils.PropertyUtilsBean#getNestedProperty(java.lang.Object, java.lang.String)
144: */
145: public Object getNestedProperty(Object arg0, String arg1)
146: throws IllegalAccessException, InvocationTargetException,
147: NoSuchMethodException {
148: // begin Kuali Foundation modification
149: try {
150: try {
151: return fastGetNestedProperty(arg0, arg1);
152: } catch (Exception e) {
153: return super .getNestedProperty(arg0, arg1);
154: }
155: } catch (NestedNullException e) {
156: return "";
157: } catch (InvocationTargetException e1) {
158: return "";
159: }
160: // removed commented code
161: // end Kuali Foundation modification
162: }
163:
164: // begin Kuali Foundation modification
165: /**
166: * begin Kuali Foundation modification
167: * Set the value of the (possibly nested) property of the specified name, for the specified bean, with no type conversions.
168: *
169: * @param bean Bean whose property is to be modified
170: * @param name Possibly nested name of the property to be modified
171: * @param value Value to which the property is to be set
172: *
173: * @exception IllegalAccessException if the caller does not have access to the property accessor method
174: * @exception IllegalArgumentException if <code>bean</code> or <code>name</code> is null
175: * @exception IllegalArgumentException if a nested reference to a property returns null
176: * @exception InvocationTargetException if the property accessor method throws an exception
177: * @exception NoSuchMethodException if an accessor method for this propety cannot be found
178: * end Kuali Foundation modification
179: */
180: public void setNestedProperty(Object bean, String name, Object value)
181: throws IllegalAccessException, InvocationTargetException,
182: NoSuchMethodException {
183:
184: if (bean == null) {
185: throw new IllegalArgumentException("No bean specified");
186: }
187: if (name == null) {
188: throw new IllegalArgumentException("No name specified");
189: }
190:
191: Object propBean = null;
192: int indexOfINDEXED_DELIM = -1;
193: int indexOfMAPPED_DELIM = -1;
194: while (true) {
195: int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
196: if (delim < 0) {
197: break;
198: }
199: String next = name.substring(0, delim);
200: indexOfINDEXED_DELIM = next
201: .indexOf(PropertyUtils.INDEXED_DELIM);
202: indexOfMAPPED_DELIM = next
203: .indexOf(PropertyUtils.MAPPED_DELIM);
204: if (bean instanceof Map) {
205: propBean = ((Map) bean).get(next);
206: } else if (indexOfMAPPED_DELIM >= 0) {
207: propBean = getMappedProperty(bean, next);
208: } else if (indexOfINDEXED_DELIM >= 0) {
209: propBean = getIndexedProperty(bean, next);
210: } else {
211: propBean = getSimpleProperty(bean, next);
212: }
213: if (propBean == null) {
214: Class propertyType = getPropertyType(bean, next);
215: if (propertyType != null) {
216: try {
217: setSimpleProperty(bean, next, propertyType
218: .newInstance());
219: propBean = getSimpleProperty(bean, next);
220: } catch (InstantiationException e) {
221: throw new IllegalArgumentException(
222: "Null property value for '"
223: + name.substring(0, delim)
224: + "'");
225: }
226: }
227: }
228: bean = propBean;
229: name = name.substring(delim + 1);
230: }
231:
232: indexOfINDEXED_DELIM = name
233: .indexOf(PropertyUtils.INDEXED_DELIM);
234: indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM);
235:
236: if (bean instanceof Map) {
237: // check to see if the class has a standard property
238: PropertyDescriptor descriptor = getPropertyDescriptor(bean,
239: name);
240: if (descriptor == null) {
241: // no - then put the value into the map
242: ((Map) bean).put(name, value);
243: } else {
244: // yes - use that instead
245: setSimpleProperty(bean, name, value);
246: }
247: } else if (indexOfMAPPED_DELIM >= 0) {
248: setMappedProperty(bean, name, value);
249: } else if (indexOfINDEXED_DELIM >= 0) {
250: setIndexedProperty(bean, name, value);
251: } else {
252: setSimpleProperty(bean, name, value);
253: }
254: }
255:
256: // end Kuali Foundation modification
257:
258: // begin Kuali Foundation modification
259: /**
260: * <p>
261: * Retrieve the property descriptor for the specified property of the specified bean, or return <code>null</code> if there is
262: * no such descriptor. This method resolves indexed and nested property references in the same manner as other methods in this
263: * class, except that if the last (or only) name element is indexed, the descriptor for the last resolved property itself is
264: * returned.
265: * </p>
266: *
267: * <p>
268: * <strong>FIXME </strong>- Does not work with DynaBeans.
269: * </p>
270: *
271: * @param bean Bean for which a property descriptor is requested
272: * @param name Possibly indexed and/or nested name of the property for which a property descriptor is requested
273: *
274: * @exception IllegalAccessException if the caller does not have access to the property accessor method
275: * @exception IllegalArgumentException if <code>bean</code> or <code>name</code> is null
276: * @exception IllegalArgumentException if a nested reference to a property returns null
277: * @exception InvocationTargetException if the property accessor method throws an exception
278: * @exception NoSuchMethodException if an accessor method for this propety cannot be found
279: */
280: public PropertyDescriptor getPropertyDescriptor(Object bean,
281: String name) throws IllegalAccessException,
282: InvocationTargetException, NoSuchMethodException {
283:
284: if (bean == null) {
285: throw new IllegalArgumentException("No bean specified");
286: }
287: if (name == null) {
288: throw new IllegalArgumentException("No name specified");
289: }
290:
291: // Resolve nested references
292: Object propBean = null;
293: while (true) {
294: int delim = findNextNestedIndex(name);
295: //int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
296: if (delim < 0) {
297: break;
298: }
299: String next = name.substring(0, delim);
300: int indexOfINDEXED_DELIM = next
301: .indexOf(PropertyUtils.INDEXED_DELIM);
302: int indexOfMAPPED_DELIM = next
303: .indexOf(PropertyUtils.MAPPED_DELIM);
304: if (indexOfMAPPED_DELIM >= 0
305: && (indexOfINDEXED_DELIM < 0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) {
306: propBean = getMappedProperty(bean, next);
307: } else {
308: if (indexOfINDEXED_DELIM >= 0) {
309: propBean = getIndexedProperty(bean, next);
310: } else {
311: propBean = getSimpleProperty(bean, next);
312: }
313: }
314: if (propBean == null) {
315: Class propertyType = getPropertyType(bean, next);
316: if (propertyType != null) {
317: try {
318: setSimpleProperty(bean, next, propertyType
319: .newInstance());
320: propBean = getSimpleProperty(bean, next);
321: } catch (InstantiationException e) {
322: throw new IllegalArgumentException(
323: "Null property value for '"
324: + name.substring(0, delim)
325: + "'");
326: }
327: }
328: }
329: bean = propBean;
330: name = name.substring(delim + 1);
331: }
332:
333: // Remove any subscript from the final name value
334: int left = name.indexOf(PropertyUtils.INDEXED_DELIM);
335: if (left >= 0) {
336: name = name.substring(0, left);
337: }
338: left = name.indexOf(PropertyUtils.MAPPED_DELIM);
339: if (left >= 0) {
340: name = name.substring(0, left);
341: }
342:
343: // Look up and return this property from our cache
344: // creating and adding it to the cache if not found.
345: if ((bean == null) || (name == null)) {
346: return (null);
347: }
348:
349: PropertyDescriptor descriptors[] = getPropertyDescriptors(bean);
350: if (descriptors != null) {
351:
352: for (int i = 0; i < descriptors.length; i++) {
353: if (name.equals(descriptors[i].getName()))
354: return (descriptors[i]);
355: }
356: }
357:
358: PropertyDescriptor result = null;
359: FastHashMap mappedDescriptors = getMappedPropertyDescriptors(bean);
360: if (mappedDescriptors == null) {
361: mappedDescriptors = new FastHashMap();
362: mappedDescriptors.setFast(true);
363: }
364: result = (PropertyDescriptor) mappedDescriptors.get(name);
365: if (result == null) {
366: // not found, try to create it
367: try {
368: result = new MappedPropertyDescriptor(name, bean
369: .getClass());
370: } catch (IntrospectionException ie) {
371: }
372: if (result != null) {
373: mappedDescriptors.put(name, result);
374: }
375: }
376:
377: return result;
378:
379: }
380:
381: // end Kuali Foundation modification
382:
383: private int findNextNestedIndex(String expression) {
384: // walk back from the end to the start
385: // and find the first index that
386: int bracketCount = 0;
387: for (int i = 0, size = expression.length(); i < size; i++) {
388: char at = expression.charAt(i);
389: switch (at) {
390: case PropertyUtils.NESTED_DELIM:
391: if (bracketCount < 1) {
392: return i;
393: }
394: break;
395:
396: case PropertyUtils.MAPPED_DELIM:
397: case PropertyUtils.INDEXED_DELIM:
398: // not bothered which
399: ++bracketCount;
400: break;
401:
402: case PropertyUtils.MAPPED_DELIM2:
403: case PropertyUtils.INDEXED_DELIM2:
404: // not bothered which
405: --bracketCount;
406: break;
407: }
408: }
409: // can't find any
410: return -1;
411: }
412:
413: }
|