001: /*
002: * Copyright (c) 1998-2003 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package javax.el;
031:
032: import java.beans.BeanInfo;
033: import java.beans.FeatureDescriptor;
034: import java.beans.IntrospectionException;
035: import java.beans.Introspector;
036: import java.beans.PropertyDescriptor;
037: import java.lang.ref.SoftReference;
038: import java.lang.reflect.InvocationTargetException;
039: import java.lang.reflect.Method;
040: import java.lang.reflect.Modifier;
041: import java.util.ArrayList;
042: import java.util.Collection;
043: import java.util.HashMap;
044: import java.util.Iterator;
045: import java.util.Map;
046: import java.util.WeakHashMap;
047: import java.util.logging.Logger;
048:
049: /**
050: * Resolves properties based on beans.
051: */
052: public class BeanELResolver extends ELResolver {
053: private final static Logger log = Logger
054: .getLogger(MapELResolver.class.getName());
055:
056: private static WeakHashMap<Class, SoftReference<BeanProperties>> _classMap = new WeakHashMap<Class, SoftReference<BeanProperties>>();
057:
058: private final boolean _isReadOnly;
059:
060: public BeanELResolver() {
061: _isReadOnly = false;
062: }
063:
064: public BeanELResolver(boolean isReadOnly) {
065: _isReadOnly = isReadOnly;
066: }
067:
068: @Override
069: public Class<?> getCommonPropertyType(ELContext context, Object base) {
070: if (base == null)
071: return null;
072:
073: return Object.class;
074: }
075:
076: @Override
077: public Iterator<FeatureDescriptor> getFeatureDescriptors(
078: ELContext context, Object base) {
079: if (base == null)
080: return null;
081:
082: Class cl = base.getClass();
083: BeanProperties props = getProps(cl);
084:
085: if (props == null) {
086: if (cl.isArray() || Collection.class.isAssignableFrom(cl)
087: || Map.class.isAssignableFrom(cl)) {
088: return null;
089: }
090:
091: props = new BeanProperties(cl);
092: setProps(cl, props);
093: }
094:
095: ArrayList<FeatureDescriptor> descriptors = new ArrayList<FeatureDescriptor>();
096:
097: for (BeanProperty prop : props.getProperties()) {
098: descriptors.add(prop.getDescriptor());
099: }
100:
101: return descriptors.iterator();
102: }
103:
104: /**
105: * If the base object is not null, returns the most general type of the
106: * property
107: *
108: * @param context
109: * @param base
110: * @param property
111: * @return
112: */
113: @Override
114: public Class<?> getType(ELContext context, Object base,
115: Object property) {
116: if (base == null || property == null)
117: return null;
118:
119: if (!(property instanceof String))
120: return null;
121:
122: String fieldName = (String) property;
123:
124: if (fieldName.length() == 0)
125: return null;
126:
127: Class cl = base.getClass();
128: BeanProperties props = getProps(cl);
129:
130: if (props == null) {
131: if (cl.isArray() || Collection.class.isAssignableFrom(cl)
132: || Map.class.isAssignableFrom(cl)) {
133: return null;
134: }
135:
136: props = new BeanProperties(cl);
137: setProps(cl, props);
138: }
139:
140: BeanProperty prop = props.getBeanProperty(fieldName);
141:
142: context.setPropertyResolved(true);
143:
144: if (prop == null || prop.getWriteMethod() == null)
145: throw new PropertyNotFoundException("'" + property
146: + "' is an unknown bean property of '"
147: + base.getClass().getName() + "'");
148:
149: return prop.getWriteMethod().getParameterTypes()[0];
150: }
151:
152: @Override
153: public Object getValue(ELContext context, Object base,
154: Object property) {
155: if (base == null || property == null)
156: return null;
157:
158: String fieldName = String.valueOf(property);
159:
160: if (fieldName.length() == 0)
161: return null;
162:
163: Class cl = base.getClass();
164: BeanProperties props = getProps(cl);
165:
166: if (props == null) {
167: if (cl.isArray() || Collection.class.isAssignableFrom(cl)
168: || Map.class.isAssignableFrom(cl)) {
169: return null;
170: }
171:
172: props = new BeanProperties(cl);
173: setProps(cl, props);
174: }
175:
176: BeanProperty prop = props.getBeanProperty(fieldName);
177:
178: context.setPropertyResolved(true);
179:
180: if (prop == null || prop.getReadMethod() == null)
181: throw new PropertyNotFoundException("'" + property
182: + "' is an unknown bean property of '"
183: + base.getClass().getName() + "'");
184:
185: try {
186: return prop.getReadMethod().invoke(base);
187: } catch (IllegalAccessException e) {
188: throw new ELException(e);
189: } catch (InvocationTargetException e) {
190: throw new ELException(e.getCause());
191: }
192: }
193:
194: @Override
195: public boolean isReadOnly(ELContext env, Object base,
196: Object property) {
197: if (base == null)
198: return false;
199:
200: BeanProperties props = getProp(env, base, property);
201:
202: if (props != null) {
203: env.setPropertyResolved(true);
204:
205: if (_isReadOnly)
206: return true;
207:
208: BeanProperty prop = props
209: .getBeanProperty((String) property);
210:
211: return prop != null && prop.isReadOnly();
212: }
213:
214: throw new PropertyNotFoundException("'" + property
215: + "' is an unknown bean property of '"
216: + base.getClass().getName() + "'");
217: }
218:
219: @Override
220: public void setValue(ELContext context, Object base,
221: Object property, Object value) {
222: if (base == null)
223: return;
224:
225: String fieldName = String.valueOf(property);
226:
227: if (fieldName.length() == 0)
228: return;
229:
230: Class cl = base.getClass();
231: BeanProperties props = getProps(cl);
232:
233: if (props == null) {
234: if (cl.isArray() || Collection.class.isAssignableFrom(cl)
235: || Map.class.isAssignableFrom(cl)) {
236: return;
237: }
238:
239: props = new BeanProperties(cl);
240: setProps(cl, props);
241: }
242:
243: BeanProperty prop = props.getBeanProperty(fieldName);
244:
245: context.setPropertyResolved(true);
246:
247: if (prop == null)
248: throw new PropertyNotFoundException(fieldName);
249: else if (prop.getWriteMethod() == null)
250: throw new PropertyNotWritableException(fieldName);
251:
252: try {
253: prop.getWriteMethod().invoke(base, value);
254: } catch (IllegalAccessException e) {
255: throw new ELException(e);
256: } catch (InvocationTargetException e) {
257: throw new ELException(e.getCause());
258: }
259: }
260:
261: private BeanProperties getProp(ELContext context, Object base,
262: Object property) {
263: if (base == null || !(property instanceof String))
264: return null;
265:
266: String fieldName = (String) property;
267:
268: if (fieldName.length() == 0)
269: return null;
270:
271: Class cl = base.getClass();
272: BeanProperties props = getProps(cl);
273:
274: if (props == null) {
275: if (cl.isArray() || Collection.class.isAssignableFrom(cl)
276: || Map.class.isAssignableFrom(cl)) {
277: return null;
278: }
279:
280: props = new BeanProperties(cl);
281: setProps(cl, props);
282: }
283:
284: return props;
285: }
286:
287: static BeanProperties getProps(Class cl) {
288: synchronized (_classMap) {
289: SoftReference<BeanProperties> ref = _classMap.get(cl);
290:
291: if (ref != null)
292: return ref.get();
293: else
294: return null;
295: }
296: }
297:
298: static void setProps(Class cl, BeanProperties props) {
299: synchronized (_classMap) {
300: _classMap.put(cl, new SoftReference<BeanProperties>(props));
301: }
302: }
303:
304: protected static final class BeanProperties {
305: private Class _base;
306:
307: private HashMap<String, BeanProperty> _propMap = new HashMap<String, BeanProperty>();
308:
309: public BeanProperties(Class baseClass) {
310: _base = baseClass;
311:
312: try {
313: BeanInfo info = Introspector.getBeanInfo(baseClass);
314:
315: for (PropertyDescriptor descriptor : info
316: .getPropertyDescriptors()) {
317: _propMap.put(descriptor.getName(),
318: new BeanProperty(baseClass, descriptor));
319: }
320:
321: Method[] methods = baseClass.getMethods();
322:
323: for (int i = 0; i < methods.length; i++) {
324: Method method = methods[i];
325:
326: String name = method.getName();
327:
328: if (method.getParameterTypes().length != 0)
329: continue;
330:
331: if (!Modifier.isPublic(method.getModifiers()))
332: continue;
333:
334: if (Modifier.isStatic(method.getModifiers()))
335: continue;
336:
337: String propName;
338: if (name.startsWith("get"))
339: propName = Introspector.decapitalize(name
340: .substring(3));
341: else if (name.startsWith("is"))
342: propName = Introspector.decapitalize(name
343: .substring(2));
344: else
345: continue;
346:
347: if (_propMap.get(propName) != null)
348: continue;
349:
350: _propMap.put(propName, new BeanProperty(baseClass,
351: propName, method));
352: }
353: } catch (IntrospectionException e) {
354: throw new ELException(e);
355: }
356: }
357:
358: public BeanProperty getBeanProperty(String property) {
359: return _propMap.get(property);
360: }
361:
362: public Collection<BeanProperty> getProperties() {
363: return _propMap.values();
364: }
365: }
366:
367: protected static final class BeanProperty {
368: private Class _base;
369: private PropertyDescriptor _descriptor;
370:
371: public BeanProperty(Class baseClass,
372: PropertyDescriptor descriptor) {
373: _base = baseClass;
374: _descriptor = descriptor;
375:
376: if (descriptor.getReadMethod() != null)
377: descriptor.getReadMethod().setAccessible(true);
378:
379: initDescriptor();
380: }
381:
382: public BeanProperty(Class baseClass, String name, Method getter) {
383: try {
384: _base = baseClass;
385: _descriptor = new PropertyDescriptor(name, getter, null);
386:
387: if (getter != null)
388: getter.setAccessible(true);
389: } catch (Exception e) {
390: throw new RuntimeException(e);
391: }
392:
393: initDescriptor();
394: }
395:
396: private void initDescriptor() {
397: Method readMethod = _descriptor.getReadMethod();
398:
399: if (readMethod != null)
400: _descriptor.setValue(ELResolver.TYPE, readMethod);
401:
402: _descriptor.setValue(ELResolver.RESOLVABLE_AT_DESIGN_TIME,
403: Boolean.TRUE);
404: }
405:
406: public PropertyDescriptor getDescriptor() {
407: return _descriptor;
408: }
409:
410: public Class getPropertyType() {
411: return _descriptor.getPropertyType();
412: }
413:
414: public Method getReadMethod() {
415: return _descriptor.getReadMethod();
416: }
417:
418: public Method getWriteMethod() {
419: return _descriptor.getWriteMethod();
420: }
421:
422: public boolean isReadOnly() {
423: return getWriteMethod() == null;
424: }
425: }
426: }
|