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: package org.jbpm.jpdl.el.impl;
056:
057: import java.beans.BeanInfo;
058: import java.beans.EventSetDescriptor;
059: import java.beans.IndexedPropertyDescriptor;
060: import java.beans.IntrospectionException;
061: import java.beans.Introspector;
062: import java.beans.PropertyDescriptor;
063: import java.lang.reflect.Method;
064: import java.lang.reflect.Modifier;
065: import java.util.HashMap;
066: import java.util.Map;
067:
068: import org.jbpm.jpdl.el.ELException;
069:
070: /**
071: *
072: * <p>
073: * Manages the BeanInfo for one class - contains the BeanInfo, and also a
074: * mapping from property name to BeanInfoProperty. There are also static methods
075: * for accessing the BeanInfoManager for a class - those mappings are cached
076: * permanently so that once the BeanInfoManager is calculated, it doesn't have
077: * to be calculated again.
078: *
079: * @author Nathan Abramson - Art Technology Group
080: * @author Tom Baeyens - JBoss
081: * @version $Change: 181181 $$DateTime: 2001/06/26 09:55:09 $$Author$
082: */
083:
084: public class BeanInfoManager {
085:
086: // -------------------------------------
087: // Properties
088: // -------------------------------------
089: // property beanClass
090:
091: Class mBeanClass;
092:
093: public Class getBeanClass() {
094: return mBeanClass;
095: }
096:
097: // -------------------------------------
098: // Member variables
099: // -------------------------------------
100:
101: // The BeanInfo
102: BeanInfo mBeanInfo;
103:
104: // Mapping from property name to BeanInfoProperty
105: Map mPropertyByName;
106:
107: // Mapping from property name to BeanInfoIndexedProperty
108: Map mIndexedPropertyByName;
109:
110: // Mapping from method name to BeanMethod
111: Map mMethodByName;
112:
113: // Mapping from event set name to event set descriptor
114: Map mEventSetByName;
115:
116: // Flag if this is initialized
117: boolean mInitialized;
118:
119: // The global mapping from class to BeanInfoManager
120: static Map mBeanInfoManagerByClass = new HashMap();
121:
122: // -------------------------------------
123: /**
124: *
125: * Constructor
126: */
127: BeanInfoManager(Class pBeanClass) {
128: mBeanClass = pBeanClass;
129: }
130:
131: // -------------------------------------
132: /**
133: *
134: * Returns the BeanInfoManager for the specified class
135: */
136: public static BeanInfoManager getBeanInfoManager(Class pClass) {
137: BeanInfoManager ret = (BeanInfoManager) mBeanInfoManagerByClass
138: .get(pClass);
139: if (ret == null) {
140: ret = createBeanInfoManager(pClass);
141: }
142: return ret;
143: }
144:
145: // -------------------------------------
146: /**
147: *
148: * Creates and registers the BeanInfoManager for the given class if it isn't
149: * already registered.
150: */
151: static synchronized BeanInfoManager createBeanInfoManager(
152: Class pClass) {
153: // Because this method is synchronized statically, the
154: // BeanInfoManager is not initialized at this time (otherwise it
155: // could end up being a bottleneck for the entire system). It is
156: // put into the map in an uninitialized state. The first time
157: // someone tries to use it, it will be initialized (with proper
158: // synchronizations in place to make sure it is only initialized
159: // once).
160:
161: BeanInfoManager ret = (BeanInfoManager) mBeanInfoManagerByClass
162: .get(pClass);
163: if (ret == null) {
164: ret = new BeanInfoManager(pClass);
165: mBeanInfoManagerByClass.put(pClass, ret);
166: }
167: return ret;
168: }
169:
170: // -------------------------------------
171: /**
172: *
173: * Returns the BeanInfoProperty for the specified property in the given class,
174: * or null if not found.
175: */
176: public static BeanInfoProperty getBeanInfoProperty(Class pClass,
177: String pPropertyName, Logger pLogger) throws ELException {
178: return getBeanInfoManager(pClass).getProperty(pPropertyName,
179: pLogger);
180: }
181:
182: // -------------------------------------
183: /**
184: *
185: * Returns the BeanInfoIndexedProperty for the specified property in the given
186: * class, or null if not found.
187: */
188: public static BeanInfoIndexedProperty getBeanInfoIndexedProperty(
189: Class pClass, String pIndexedPropertyName, Logger pLogger)
190: throws ELException {
191: return getBeanInfoManager(pClass).getIndexedProperty(
192: pIndexedPropertyName, pLogger);
193: }
194:
195: public static BeanMethod getBeanMethod(Class pClass,
196: String indexStr, Logger logger) throws ELException {
197: return getBeanInfoManager(pClass).getBeanMethod(indexStr,
198: logger);
199: }
200:
201: // -------------------------------------
202: /**
203: *
204: * Makes sure that this class has been initialized, and synchronizes the
205: * initialization if it's required.
206: */
207: void checkInitialized(Logger pLogger) throws ELException {
208: if (!mInitialized) {
209: synchronized (this ) {
210: if (!mInitialized) {
211: initialize(pLogger);
212: mInitialized = true;
213: }
214: }
215: }
216: }
217:
218: // -------------------------------------
219: /**
220: *
221: * Initializes by mapping property names to BeanInfoProperties
222: */
223: void initialize(Logger pLogger) throws ELException {
224: try {
225: mBeanInfo = Introspector.getBeanInfo(mBeanClass);
226:
227: mPropertyByName = new HashMap();
228: mIndexedPropertyByName = new HashMap();
229: mMethodByName = new HashMap();
230:
231: PropertyDescriptor[] pds = mBeanInfo
232: .getPropertyDescriptors();
233: for (int i = 0; pds != null && i < pds.length; i++) {
234: // Treat as both an indexed property and a normal property
235: PropertyDescriptor pd = pds[i];
236: if (pd instanceof IndexedPropertyDescriptor) {
237: IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
238: Method readMethod = getPublicMethod(ipd
239: .getIndexedReadMethod());
240: Method writeMethod = getPublicMethod(ipd
241: .getIndexedWriteMethod());
242: BeanInfoIndexedProperty property = new BeanInfoIndexedProperty(
243: readMethod, writeMethod, ipd);
244:
245: mIndexedPropertyByName.put(ipd.getName(), property);
246: }
247:
248: Method readMethod = getPublicMethod(pd.getReadMethod());
249: Method writeMethod = getPublicMethod(pd
250: .getWriteMethod());
251: BeanInfoProperty property = new BeanInfoProperty(
252: readMethod, writeMethod, pd);
253:
254: mPropertyByName.put(pd.getName(), property);
255: }
256:
257: mEventSetByName = new HashMap();
258: EventSetDescriptor[] esds = mBeanInfo
259: .getEventSetDescriptors();
260: for (int i = 0; esds != null && i < esds.length; i++) {
261: EventSetDescriptor esd = esds[i];
262: mEventSetByName.put(esd.getName(), esd);
263: }
264: } catch (IntrospectionException exc) {
265: if (pLogger.isLoggingWarning()) {
266: pLogger.logWarning(
267: Constants.EXCEPTION_GETTING_BEANINFO, exc,
268: mBeanClass.getName());
269: }
270: }
271:
272: // parsing the bean methods
273: Class scannedClass = mBeanClass;
274: while (scannedClass != null) {
275: Method[] methods = scannedClass.getDeclaredMethods();
276: for (int i = 0; i < methods.length; i++) {
277: Method method = methods[i];
278: if ((!mMethodByName.containsKey(method.getName()))
279: && ((method.getParameterTypes() == null) || (method
280: .getParameterTypes().length == 0))) {
281: mMethodByName.put(method.getName(), new BeanMethod(
282: method));
283: }
284: }
285: scannedClass = scannedClass.getSuperclass();
286: }
287: }
288:
289: // -------------------------------------
290: /**
291: *
292: * Returns the BeanInfo for the class
293: */
294: BeanInfo getBeanInfo(Logger pLogger) throws ELException {
295: checkInitialized(pLogger);
296: return mBeanInfo;
297: }
298:
299: // -------------------------------------
300: /**
301: *
302: * Returns the BeanInfoProperty for the given property name, or null if not
303: * found.
304: */
305: public BeanInfoProperty getProperty(String pPropertyName,
306: Logger pLogger) throws ELException {
307: checkInitialized(pLogger);
308: return (BeanInfoProperty) mPropertyByName.get(pPropertyName);
309: }
310:
311: // -------------------------------------
312: /**
313: *
314: * Returns the BeanInfoIndexedProperty for the given property name, or null if
315: * not found.
316: */
317: public BeanInfoIndexedProperty getIndexedProperty(
318: String pIndexedPropertyName, Logger pLogger)
319: throws ELException {
320: checkInitialized(pLogger);
321: return (BeanInfoIndexedProperty) mIndexedPropertyByName
322: .get(pIndexedPropertyName);
323: }
324:
325: public BeanMethod getBeanMethod(String pMethodName, Logger pLogger)
326: throws ELException {
327: checkInitialized(pLogger);
328: return (BeanMethod) mMethodByName.get(pMethodName);
329: }
330:
331: // -------------------------------------
332: /**
333: *
334: * Returns the EventSetDescriptor for the given event set name, or null if not
335: * found.
336: */
337: public EventSetDescriptor getEventSet(String pEventSetName,
338: Logger pLogger) throws ELException {
339: checkInitialized(pLogger);
340: return (EventSetDescriptor) mEventSetByName.get(pEventSetName);
341: }
342:
343: // -------------------------------------
344: // Finding the public version of a method - if a PropertyDescriptor
345: // is obtained for a non-public class that implements a public
346: // interface, the read/write methods will be for the class, and
347: // therefore inaccessible. To correct this, a version of the same
348: // method must be found in a superclass or interface.
349: // -------------------------------------
350: /**
351: *
352: * Returns a publicly-accessible version of the given method, by searching for
353: * a public declaring class.
354: */
355: static Method getPublicMethod(Method pMethod) {
356: if (pMethod == null) {
357: return null;
358: }
359:
360: // See if the method is already available from a public class
361: Class cl = pMethod.getDeclaringClass();
362: if (Modifier.isPublic(cl.getModifiers())) {
363: return pMethod;
364: }
365:
366: // Otherwise, try to find a public class that declares the method
367: Method ret = getPublicMethod(cl, pMethod);
368: if (ret != null) {
369: return ret;
370: } else {
371: return pMethod;
372: }
373: }
374:
375: // -------------------------------------
376: /**
377: *
378: * If the given class is public and has a Method that declares the same name
379: * and arguments as the given method, then that method is returned. Otherwise
380: * the superclass and interfaces are searched recursively.
381: */
382: static Method getPublicMethod(Class pClass, Method pMethod) {
383: // See if this is a public class declaring the method
384: if (Modifier.isPublic(pClass.getModifiers())) {
385: try {
386: Method m;
387: try {
388: m = pClass.getDeclaredMethod(pMethod.getName(),
389: pMethod.getParameterTypes());
390: } catch (java.security.AccessControlException ex) {
391: // kludge to accommodate J2EE RI's default settings
392: // TODO: see if we can simply replace
393: // getDeclaredMethod() with getMethod() ...?
394: m = pClass.getMethod(pMethod.getName(), pMethod
395: .getParameterTypes());
396: }
397: if (Modifier.isPublic(m.getModifiers())) {
398: return m;
399: }
400: } catch (NoSuchMethodException exc) {
401: }
402: }
403:
404: // Search the interfaces
405: {
406: Class[] interfaces = pClass.getInterfaces();
407: if (interfaces != null) {
408: for (int i = 0; i < interfaces.length; i++) {
409: Method m = getPublicMethod(interfaces[i], pMethod);
410: if (m != null) {
411: return m;
412: }
413: }
414: }
415: }
416:
417: // Search the superclass
418: {
419: Class super class = pClass.getSuperclass();
420: if (super class != null) {
421: Method m = getPublicMethod(super class, pMethod);
422: if (m != null) {
423: return m;
424: }
425: }
426: }
427:
428: return null;
429: }
430:
431: // -------------------------------------
432: }
|