001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1999 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: *
054: */
055:
056: package org.apache.commons.el;
057:
058: import java.beans.BeanInfo;
059: import java.beans.EventSetDescriptor;
060: import java.beans.IndexedPropertyDescriptor;
061: import java.beans.IntrospectionException;
062: import java.beans.Introspector;
063: import java.beans.PropertyDescriptor;
064: import java.lang.reflect.Method;
065: import java.lang.reflect.Modifier;
066: import java.text.MessageFormat;
067: import java.util.HashMap;
068: import java.util.Map;
069: import javax.servlet.jsp.el.ELException;
070:
071: /**
072: *
073: * <p>Manages the BeanInfo for one class - contains the BeanInfo, and
074: * also a mapping from property name to BeanInfoProperty. There are
075: * also static methods for accessing the BeanInfoManager for a class -
076: * those mappings are cached permanently so that once the
077: * BeanInfoManager is calculated, it doesn't have to be calculated
078: * again.
079: *
080: * @author Nathan Abramson - Art Technology Group
081: * @version $Change: 181181 $$DateTime: 2001/06/26 09:55:09 $$Author: luehe $
082: **/
083:
084: public class BeanInfoManager {
085: //-------------------------------------
086: // Properties
087: //-------------------------------------
088: // property beanClass
089:
090: Class mBeanClass;
091:
092: public Class getBeanClass() {
093: return mBeanClass;
094: }
095:
096: //-------------------------------------
097: // Member variables
098: //-------------------------------------
099:
100: // The BeanInfo
101: BeanInfo mBeanInfo;
102:
103: // Mapping from property name to BeanInfoProperty
104: Map mPropertyByName;
105:
106: // Mapping from property name to BeanInfoIndexedProperty
107: Map mIndexedPropertyByName;
108:
109: // Mapping from event set name to event set descriptor
110: Map mEventSetByName;
111:
112: // Flag if this is initialized
113: boolean mInitialized;
114:
115: // The global mapping from class to BeanInfoManager
116: static Map mBeanInfoManagerByClass = new HashMap();
117:
118: //-------------------------------------
119: /**
120: *
121: * Constructor
122: **/
123: BeanInfoManager(Class pBeanClass) {
124: mBeanClass = pBeanClass;
125: }
126:
127: //-------------------------------------
128: /**
129: *
130: * Returns the BeanInfoManager for the specified class
131: **/
132: public static BeanInfoManager getBeanInfoManager(Class pClass) {
133: BeanInfoManager ret = (BeanInfoManager) mBeanInfoManagerByClass
134: .get(pClass);
135: if (ret == null) {
136: ret = createBeanInfoManager(pClass);
137: }
138: return ret;
139: }
140:
141: //-------------------------------------
142: /**
143: *
144: * Creates and registers the BeanInfoManager for the given class if
145: * it isn't already registered.
146: **/
147: static synchronized BeanInfoManager createBeanInfoManager(
148: Class pClass) {
149: // Because this method is synchronized statically, the
150: // BeanInfoManager is not initialized at this time (otherwise it
151: // could end up being a bottleneck for the entire system). It is
152: // put into the map in an uninitialized state. The first time
153: // someone tries to use it, it will be initialized (with proper
154: // synchronizations in place to make sure it is only initialized
155: // once).
156:
157: BeanInfoManager ret = (BeanInfoManager) mBeanInfoManagerByClass
158: .get(pClass);
159: if (ret == null) {
160: ret = new BeanInfoManager(pClass);
161: mBeanInfoManagerByClass.put(pClass, ret);
162: }
163: return ret;
164: }
165:
166: //-------------------------------------
167: /**
168: *
169: * Returns the BeanInfoProperty for the specified property in the
170: * given class, or null if not found.
171: **/
172: public static BeanInfoProperty getBeanInfoProperty(Class pClass,
173: String pPropertyName, Logger pLogger) throws ELException {
174: return getBeanInfoManager(pClass).getProperty(pPropertyName,
175: pLogger);
176: }
177:
178: //-------------------------------------
179: /**
180: *
181: * Returns the BeanInfoIndexedProperty for the specified property in
182: * the given class, or null if not found.
183: **/
184: public static BeanInfoIndexedProperty getBeanInfoIndexedProperty(
185: Class pClass, String pIndexedPropertyName, Logger pLogger)
186: throws ELException {
187: return getBeanInfoManager(pClass).getIndexedProperty(
188: pIndexedPropertyName, pLogger);
189: }
190:
191: //-------------------------------------
192: /**
193: *
194: * Makes sure that this class has been initialized, and synchronizes
195: * the initialization if it's required.
196: **/
197: void checkInitialized(Logger pLogger) throws ELException {
198: if (!mInitialized) {
199: synchronized (this ) {
200: if (!mInitialized) {
201: initialize(pLogger);
202: mInitialized = true;
203: }
204: }
205: }
206: }
207:
208: //-------------------------------------
209: /**
210: *
211: * Initializes by mapping property names to BeanInfoProperties
212: **/
213: void initialize(Logger pLogger) throws ELException {
214: try {
215: mBeanInfo = Introspector.getBeanInfo(mBeanClass);
216:
217: mPropertyByName = new HashMap();
218: mIndexedPropertyByName = new HashMap();
219: PropertyDescriptor[] pds = mBeanInfo
220: .getPropertyDescriptors();
221: for (int i = 0; pds != null && i < pds.length; i++) {
222: // Treat as both an indexed property and a normal property
223: PropertyDescriptor pd = pds[i];
224: if (pd instanceof IndexedPropertyDescriptor) {
225: IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
226: Method readMethod = getPublicMethod(ipd
227: .getIndexedReadMethod());
228: Method writeMethod = getPublicMethod(ipd
229: .getIndexedWriteMethod());
230: BeanInfoIndexedProperty property = new BeanInfoIndexedProperty(
231: readMethod, writeMethod, ipd);
232:
233: mIndexedPropertyByName.put(ipd.getName(), property);
234: }
235:
236: Method readMethod = getPublicMethod(pd.getReadMethod());
237: Method writeMethod = getPublicMethod(pd
238: .getWriteMethod());
239: BeanInfoProperty property = new BeanInfoProperty(
240: readMethod, writeMethod, pd);
241:
242: mPropertyByName.put(pd.getName(), property);
243: }
244:
245: mEventSetByName = new HashMap();
246: EventSetDescriptor[] esds = mBeanInfo
247: .getEventSetDescriptors();
248: for (int i = 0; esds != null && i < esds.length; i++) {
249: EventSetDescriptor esd = esds[i];
250: mEventSetByName.put(esd.getName(), esd);
251: }
252: } catch (IntrospectionException exc) {
253: if (pLogger.isLoggingWarning()) {
254: pLogger.logWarning(
255: Constants.EXCEPTION_GETTING_BEANINFO, exc,
256: mBeanClass.getName());
257: }
258: }
259: }
260:
261: //-------------------------------------
262: /**
263: *
264: * Returns the BeanInfo for the class
265: **/
266: BeanInfo getBeanInfo(Logger pLogger) throws ELException {
267: checkInitialized(pLogger);
268: return mBeanInfo;
269: }
270:
271: //-------------------------------------
272: /**
273: *
274: * Returns the BeanInfoProperty for the given property name, or null
275: * if not found.
276: **/
277: public BeanInfoProperty getProperty(String pPropertyName,
278: Logger pLogger) throws ELException {
279: checkInitialized(pLogger);
280: return (BeanInfoProperty) mPropertyByName.get(pPropertyName);
281: }
282:
283: //-------------------------------------
284: /**
285: *
286: * Returns the BeanInfoIndexedProperty for the given property name,
287: * or null if not found.
288: **/
289: public BeanInfoIndexedProperty getIndexedProperty(
290: String pIndexedPropertyName, Logger pLogger)
291: throws ELException {
292: checkInitialized(pLogger);
293: return (BeanInfoIndexedProperty) mIndexedPropertyByName
294: .get(pIndexedPropertyName);
295: }
296:
297: //-------------------------------------
298: /**
299: *
300: * Returns the EventSetDescriptor for the given event set name, or
301: * null if not found.
302: **/
303: public EventSetDescriptor getEventSet(String pEventSetName,
304: Logger pLogger) throws ELException {
305: checkInitialized(pLogger);
306: return (EventSetDescriptor) mEventSetByName.get(pEventSetName);
307: }
308:
309: //-------------------------------------
310: // Finding the public version of a method - if a PropertyDescriptor
311: // is obtained for a non-public class that implements a public
312: // interface, the read/write methods will be for the class, and
313: // therefore inaccessible. To correct this, a version of the same
314: // method must be found in a superclass or interface.
315: //-------------------------------------
316: /**
317: *
318: * Returns a publicly-accessible version of the given method, by
319: * searching for a public declaring class.
320: **/
321: static Method getPublicMethod(Method pMethod) {
322: if (pMethod == null) {
323: return null;
324: }
325:
326: // See if the method is already available from a public class
327: Class cl = pMethod.getDeclaringClass();
328: if (Modifier.isPublic(cl.getModifiers())) {
329: return pMethod;
330: }
331:
332: // Otherwise, try to find a public class that declares the method
333: Method ret = getPublicMethod(cl, pMethod);
334: if (ret != null) {
335: return ret;
336: } else {
337: return pMethod;
338: }
339: }
340:
341: //-------------------------------------
342: /**
343: *
344: * If the given class is public and has a Method that declares the
345: * same name and arguments as the given method, then that method is
346: * returned. Otherwise the superclass and interfaces are searched
347: * recursively.
348: **/
349: static Method getPublicMethod(Class pClass, Method pMethod) {
350: // See if this is a public class declaring the method
351: if (Modifier.isPublic(pClass.getModifiers())) {
352: try {
353: Method m;
354: try {
355: m = pClass.getDeclaredMethod(pMethod.getName(),
356: pMethod.getParameterTypes());
357: } catch (java.security.AccessControlException ex) {
358: // kludge to accommodate J2EE RI's default settings
359: // TODO: see if we can simply replace
360: // getDeclaredMethod() with getMethod() ...?
361: m = pClass.getMethod(pMethod.getName(), pMethod
362: .getParameterTypes());
363: }
364: if (Modifier.isPublic(m.getModifiers())) {
365: return m;
366: }
367: } catch (NoSuchMethodException exc) {
368: }
369: }
370:
371: // Search the interfaces
372: {
373: Class[] interfaces = pClass.getInterfaces();
374: if (interfaces != null) {
375: for (int i = 0; i < interfaces.length; i++) {
376: Method m = getPublicMethod(interfaces[i], pMethod);
377: if (m != null) {
378: return m;
379: }
380: }
381: }
382: }
383:
384: // Search the superclass
385: {
386: Class super class = pClass.getSuperclass();
387: if (super class != null) {
388: Method m = getPublicMethod(super class, pMethod);
389: if (m != null) {
390: return m;
391: }
392: }
393: }
394:
395: return null;
396: }
397:
398: //-------------------------------------
399: }
|