001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.util.internal.cache;
020:
021: import java.beans.Introspector;
022: import java.beans.IntrospectionException;
023: import java.beans.PropertyDescriptor;
024: import java.lang.reflect.Method;
025: import java.lang.reflect.Modifier;
026: import java.util.HashMap;
027: import java.util.Iterator;
028:
029: import org.apache.beehive.netui.util.internal.concurrent.InternalConcurrentHashMap;
030: import org.apache.beehive.netui.util.logging.Logger;
031:
032: /**
033: * The PropertyCache is used to track the JavaBean properties and public
034: * fields of a set of classes that are stored in the cache. This implementation
035: * provides a significant speed-up when looking-up reflected metadata
036: * of Java classes. It is primarily used in the NetUI expression engine
037: * to provide fast access to the properties and fields of classes
038: * against which expressions are being evaluated.
039: */
040: public final class PropertyCache {
041:
042: private static final Logger LOGGER = Logger
043: .getInstance(PropertyCache.class);
044:
045: private final InternalConcurrentHashMap _classCache;
046:
047: public PropertyCache() {
048: _classCache = new InternalConcurrentHashMap();
049: }
050:
051: /**
052: * Get an array of {@link java.beans.PropertyDescriptor} objects that
053: * describe JavaBean properties of the given <code>_type</code>. This
054: * array <b>should not</b> be modified.
055: *
056: * @param type the {@link java.lang.Class} whose JavaBean properties to find
057: * @return an array of {@link java.beans.PropertyDescriptor} objects that describe the JavaBean properties
058: */
059: public final PropertyDescriptor[] getPropertyDescriptors(Class type) {
060: CachedClass cc = getCachedClass(type);
061: return (cc != null ? cc.getPropertyDescriptors() : null);
062: }
063:
064: public final Method getPropertyGetter(Class type, String property) {
065: CachedClass cc = getCachedClass(type);
066: if (cc == null)
067: return null;
068: CachedProperty cp = cc.getProperty(property);
069: return (cp != null ? cp.getReadMethod() : null);
070: }
071:
072: public final Method getPropertySetter(Class type, String property) {
073: CachedClass cc = getCachedClass(type);
074: if (cc == null)
075: return null;
076: CachedProperty cp = cc.getProperty(property);
077: return (cp != null ? cp.getWriteMethod() : null);
078: }
079:
080: public final Class getPropertyType(Class type, String property) {
081: CachedClass cc = getCachedClass(type);
082: if (cc == null)
083: return null;
084: CachedProperty cp = cc.getProperty(property);
085: return (cp != null ? cp.getType() : null);
086: }
087:
088: private final CachedClass getCachedClass(Class type) {
089: Object obj = _classCache.get(type);
090: if (obj == null) {
091: try {
092: obj = new CachedClass(type);
093: _classCache.put(type, obj);
094: } catch (Exception e) {
095: LOGGER
096: .error(
097: "Error introspecting a class of _type \""
098: + type
099: + "\" when determining its JavaBean property info",
100: e);
101: return null;
102: }
103: }
104:
105: return (CachedClass) obj;
106: }
107:
108: /**
109: *
110: */
111: private class CachedClass {
112:
113: private Class _type = null;
114: private HashMap _properties = null;
115: private PropertyDescriptor[] _propertyDescriptors = null;
116:
117: CachedClass(Class type) throws IntrospectionException {
118: this ._type = type;
119: init(type);
120: }
121:
122: private void init(Class type) throws IntrospectionException {
123: _properties = new HashMap();
124:
125: if (Modifier.isPublic(type.getModifiers())) {
126: PropertyDescriptor[] pds = Introspector.getBeanInfo(
127: type).getPropertyDescriptors();
128: for (int i = 0; i < pds.length; i++) {
129: _properties.put(pds[i].getName(),
130: new CachedProperty(pds[i]));
131: }
132: }
133: // not looking at a public class, get all of the JavaBean PDs off of its interfaces
134: else {
135: // look on the public interfaces on this class and all superclasses
136: for (Class c = type; c != null; c = c.getSuperclass()) {
137: Class[] interfaces = c.getInterfaces();
138: for (int i = 0; i < interfaces.length; i++) {
139: Class iface = interfaces[i];
140: if (Modifier.isPublic(iface.getModifiers())) {
141: PropertyDescriptor[] pds = Introspector
142: .getBeanInfo(iface)
143: .getPropertyDescriptors();
144: for (int j = 0; j < pds.length; j++) {
145: if (!_properties.containsKey(pds[j]
146: .getName()))
147: _properties.put(pds[j].getName(),
148: new CachedProperty(pds[j]));
149: }
150: }
151: }
152: }
153:
154: // look on the nearest public base class
155: Class baseClass = type.getSuperclass();
156: while (!Modifier.isPublic(baseClass.getModifiers())) {
157: baseClass = baseClass.getSuperclass();
158: }
159:
160: PropertyDescriptor[] pds = Introspector.getBeanInfo(
161: baseClass).getPropertyDescriptors();
162: for (int j = 0; j < pds.length; j++) {
163: if (!_properties.containsKey(pds[j].getName()))
164: _properties.put(pds[j].getName(),
165: new CachedProperty(pds[j]));
166: }
167: }
168:
169: if (_properties.size() > 0) {
170: _propertyDescriptors = new PropertyDescriptor[_properties
171: .size()];
172: Iterator iterator = _properties.values().iterator();
173: for (int i = 0; iterator.hasNext(); i++) {
174: _propertyDescriptors[i] = ((CachedProperty) iterator
175: .next()).getPropertyDescriptor();
176: }
177: }
178: }
179:
180: PropertyDescriptor[] getPropertyDescriptors() {
181: return _propertyDescriptors;
182: }
183:
184: CachedProperty getProperty(String name) {
185: return (CachedProperty) _properties.get(name);
186: }
187: }
188:
189: /**
190: *
191: */
192: private class CachedProperty {
193:
194: private Method _readMethod = null;
195: private Method _writeMethod = null;
196: private String _name = null;
197: private PropertyDescriptor _pd = null;
198: private Class _type = null;
199:
200: CachedProperty(PropertyDescriptor pd) {
201: _pd = pd;
202: _name = pd.getName();
203: _readMethod = pd.getReadMethod();
204: _writeMethod = pd.getWriteMethod();
205: _type = pd.getPropertyType();
206: }
207:
208: PropertyDescriptor getPropertyDescriptor() {
209: return _pd;
210: }
211:
212: Method getReadMethod() {
213: return _readMethod;
214: }
215:
216: Method getWriteMethod() {
217: return _writeMethod;
218: }
219:
220: String getName() {
221: return _name;
222: }
223:
224: Class getType() {
225: return _type;
226: }
227: }
228: }
|