Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BeanInfoManager |
|
| 3.0714285714285716;3.071 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | package org.apache.commons.el; | |
18 | ||
19 | import java.beans.BeanInfo; | |
20 | import java.beans.EventSetDescriptor; | |
21 | import java.beans.IndexedPropertyDescriptor; | |
22 | import java.beans.IntrospectionException; | |
23 | import java.beans.Introspector; | |
24 | import java.beans.PropertyDescriptor; | |
25 | import java.lang.reflect.Method; | |
26 | import java.lang.reflect.Modifier; | |
27 | import java.util.HashMap; | |
28 | import java.util.Map; | |
29 | ||
30 | import javax.servlet.jsp.el.ELException; | |
31 | ||
32 | import org.apache.commons.logging.Log; | |
33 | import org.apache.commons.logging.LogFactory; | |
34 | ||
35 | /** | |
36 | * | |
37 | * <p>Manages the BeanInfo for one class - contains the BeanInfo, and | |
38 | * also a mapping from property name to BeanInfoProperty. There are | |
39 | * also static methods for accessing the BeanInfoManager for a class - | |
40 | * those mappings are cached permanently so that once the | |
41 | * BeanInfoManager is calculated, it doesn't have to be calculated | |
42 | * again. | |
43 | * | |
44 | * @author Nathan Abramson - Art Technology Group | |
45 | * @version $Change: 181181 $$DateTime: 2001/06/26 09:55:09 $$Author: bayard $ | |
46 | **/ | |
47 | ||
48 | public class BeanInfoManager | |
49 | { | |
50 | //------------------------------------- | |
51 | // Constants | |
52 | //------------------------------------- | |
53 | 0 | private static Log log = LogFactory.getLog(BeanInfoManager.class); |
54 | ||
55 | //------------------------------------- | |
56 | // Properties | |
57 | //------------------------------------- | |
58 | // property beanClass | |
59 | ||
60 | Class mBeanClass; | |
61 | public Class getBeanClass () | |
62 | 0 | { return mBeanClass; } |
63 | ||
64 | //------------------------------------- | |
65 | // Member variables | |
66 | //------------------------------------- | |
67 | ||
68 | // The BeanInfo | |
69 | BeanInfo mBeanInfo; | |
70 | ||
71 | // Mapping from property name to BeanInfoProperty | |
72 | Map mPropertyByName; | |
73 | ||
74 | // Mapping from property name to BeanInfoIndexedProperty | |
75 | Map mIndexedPropertyByName; | |
76 | ||
77 | // Mapping from event set name to event set descriptor | |
78 | Map mEventSetByName; | |
79 | ||
80 | // Flag if this is initialized | |
81 | boolean mInitialized; | |
82 | ||
83 | // The global mapping from class to BeanInfoManager | |
84 | 0 | static Map mBeanInfoManagerByClass = new HashMap (); |
85 | ||
86 | //------------------------------------- | |
87 | /** | |
88 | * | |
89 | * Constructor | |
90 | **/ | |
91 | BeanInfoManager (Class pBeanClass) | |
92 | 0 | { |
93 | 0 | mBeanClass = pBeanClass; |
94 | 0 | } |
95 | ||
96 | //------------------------------------- | |
97 | /** | |
98 | * | |
99 | * Returns the BeanInfoManager for the specified class | |
100 | **/ | |
101 | public static BeanInfoManager getBeanInfoManager (Class pClass) | |
102 | { | |
103 | 0 | BeanInfoManager ret = (BeanInfoManager) |
104 | mBeanInfoManagerByClass.get (pClass); | |
105 | 0 | if (ret == null) { |
106 | 0 | ret = createBeanInfoManager (pClass); |
107 | } | |
108 | 0 | return ret; |
109 | } | |
110 | ||
111 | //------------------------------------- | |
112 | /** | |
113 | * | |
114 | * Creates and registers the BeanInfoManager for the given class if | |
115 | * it isn't already registered. | |
116 | **/ | |
117 | static synchronized BeanInfoManager createBeanInfoManager (Class pClass) | |
118 | { | |
119 | // Because this method is synchronized statically, the | |
120 | // BeanInfoManager is not initialized at this time (otherwise it | |
121 | // could end up being a bottleneck for the entire system). It is | |
122 | // put into the map in an uninitialized state. The first time | |
123 | // someone tries to use it, it will be initialized (with proper | |
124 | // synchronizations in place to make sure it is only initialized | |
125 | // once). | |
126 | ||
127 | 0 | BeanInfoManager ret = (BeanInfoManager) |
128 | mBeanInfoManagerByClass.get (pClass); | |
129 | 0 | if (ret == null) { |
130 | 0 | ret = new BeanInfoManager (pClass); |
131 | 0 | mBeanInfoManagerByClass.put (pClass, ret); |
132 | } | |
133 | 0 | return ret; |
134 | } | |
135 | ||
136 | //------------------------------------- | |
137 | /** | |
138 | * | |
139 | * Returns the BeanInfoProperty for the specified property in the | |
140 | * given class, or null if not found. | |
141 | **/ | |
142 | public static BeanInfoProperty getBeanInfoProperty | |
143 | (Class pClass, | |
144 | String pPropertyName) | |
145 | throws ELException | |
146 | { | |
147 | 0 | return getBeanInfoManager (pClass).getProperty (pPropertyName); |
148 | } | |
149 | ||
150 | //------------------------------------- | |
151 | /** | |
152 | * | |
153 | * Returns the BeanInfoIndexedProperty for the specified property in | |
154 | * the given class, or null if not found. | |
155 | **/ | |
156 | public static BeanInfoIndexedProperty getBeanInfoIndexedProperty | |
157 | (Class pClass, | |
158 | String pIndexedPropertyName) | |
159 | throws ELException | |
160 | { | |
161 | 0 | return getBeanInfoManager |
162 | (pClass).getIndexedProperty (pIndexedPropertyName); | |
163 | } | |
164 | ||
165 | //------------------------------------- | |
166 | /** | |
167 | * | |
168 | * Makes sure that this class has been initialized, and synchronizes | |
169 | * the initialization if it's required. | |
170 | **/ | |
171 | void checkInitialized () | |
172 | throws ELException | |
173 | { | |
174 | 0 | if (!mInitialized) { |
175 | 0 | synchronized (this) { |
176 | 0 | if (!mInitialized) { |
177 | 0 | initialize(); |
178 | 0 | mInitialized = true; |
179 | } | |
180 | 0 | } |
181 | } | |
182 | 0 | } |
183 | ||
184 | //------------------------------------- | |
185 | /** | |
186 | * | |
187 | * Initializes by mapping property names to BeanInfoProperties | |
188 | **/ | |
189 | void initialize () | |
190 | throws ELException | |
191 | { | |
192 | try { | |
193 | 0 | mBeanInfo = Introspector.getBeanInfo (mBeanClass); |
194 | ||
195 | 0 | mPropertyByName = new HashMap (); |
196 | 0 | mIndexedPropertyByName = new HashMap (); |
197 | 0 | PropertyDescriptor [] pds = mBeanInfo.getPropertyDescriptors (); |
198 | 0 | for (int i = 0; pds != null && i < pds.length; i++) { |
199 | // Treat as both an indexed property and a normal property | |
200 | 0 | PropertyDescriptor pd = pds [i]; |
201 | 0 | if (pd instanceof IndexedPropertyDescriptor) { |
202 | 0 | IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd; |
203 | 0 | Method readMethod = getPublicMethod (ipd.getIndexedReadMethod ()); |
204 | 0 | Method writeMethod = getPublicMethod (ipd.getIndexedWriteMethod ()); |
205 | 0 | BeanInfoIndexedProperty property = new BeanInfoIndexedProperty |
206 | (readMethod, | |
207 | writeMethod, | |
208 | ipd); | |
209 | ||
210 | 0 | mIndexedPropertyByName.put (ipd.getName (), property); |
211 | } | |
212 | ||
213 | 0 | Method readMethod = getPublicMethod (pd.getReadMethod ()); |
214 | 0 | Method writeMethod = getPublicMethod (pd.getWriteMethod ()); |
215 | 0 | BeanInfoProperty property = new BeanInfoProperty |
216 | (readMethod, | |
217 | writeMethod, | |
218 | pd); | |
219 | ||
220 | 0 | mPropertyByName.put (pd.getName (), property); |
221 | } | |
222 | ||
223 | 0 | mEventSetByName = new HashMap (); |
224 | 0 | EventSetDescriptor [] esds = mBeanInfo.getEventSetDescriptors (); |
225 | 0 | for (int i = 0; esds != null && i < esds.length; i++) { |
226 | 0 | EventSetDescriptor esd = esds [i]; |
227 | 0 | mEventSetByName.put (esd.getName (), esd); |
228 | } | |
229 | } | |
230 | 0 | catch (IntrospectionException exc) { |
231 | 0 | if (log.isWarnEnabled()) { |
232 | 0 | log.warn( |
233 | MessageUtil.getMessageWithArgs( | |
234 | Constants.EXCEPTION_GETTING_BEANINFO, mBeanClass.getName()), exc); | |
235 | } | |
236 | 0 | } |
237 | 0 | } |
238 | ||
239 | //------------------------------------- | |
240 | /** | |
241 | * | |
242 | * Returns the BeanInfo for the class | |
243 | **/ | |
244 | BeanInfo getBeanInfo () | |
245 | throws ELException | |
246 | { | |
247 | 0 | checkInitialized(); |
248 | 0 | return mBeanInfo; |
249 | } | |
250 | ||
251 | //------------------------------------- | |
252 | /** | |
253 | * | |
254 | * Returns the BeanInfoProperty for the given property name, or null | |
255 | * if not found. | |
256 | **/ | |
257 | public BeanInfoProperty getProperty (String pPropertyName) | |
258 | throws ELException | |
259 | { | |
260 | 0 | checkInitialized(); |
261 | 0 | return (BeanInfoProperty) mPropertyByName.get (pPropertyName); |
262 | } | |
263 | ||
264 | //------------------------------------- | |
265 | /** | |
266 | * | |
267 | * Returns the BeanInfoIndexedProperty for the given property name, | |
268 | * or null if not found. | |
269 | **/ | |
270 | public BeanInfoIndexedProperty getIndexedProperty | |
271 | (String pIndexedPropertyName) | |
272 | throws ELException | |
273 | { | |
274 | 0 | checkInitialized(); |
275 | 0 | return (BeanInfoIndexedProperty) |
276 | mIndexedPropertyByName.get (pIndexedPropertyName); | |
277 | } | |
278 | ||
279 | //------------------------------------- | |
280 | /** | |
281 | * | |
282 | * Returns the EventSetDescriptor for the given event set name, or | |
283 | * null if not found. | |
284 | **/ | |
285 | public EventSetDescriptor getEventSet (String pEventSetName) | |
286 | throws ELException | |
287 | { | |
288 | 0 | checkInitialized(); |
289 | 0 | return (EventSetDescriptor) mEventSetByName.get (pEventSetName); |
290 | } | |
291 | ||
292 | //------------------------------------- | |
293 | // Finding the public version of a method - if a PropertyDescriptor | |
294 | // is obtained for a non-public class that implements a public | |
295 | // interface, the read/write methods will be for the class, and | |
296 | // therefore inaccessible. To correct this, a version of the same | |
297 | // method must be found in a superclass or interface. | |
298 | //------------------------------------- | |
299 | /** | |
300 | * | |
301 | * Returns a publicly-accessible version of the given method, by | |
302 | * searching for a public declaring class. | |
303 | **/ | |
304 | static Method getPublicMethod (Method pMethod) | |
305 | { | |
306 | 0 | if (pMethod == null) { |
307 | 0 | return null; |
308 | } | |
309 | ||
310 | // See if the method is already available from a public class | |
311 | 0 | Class cl = pMethod.getDeclaringClass (); |
312 | 0 | if (Modifier.isPublic (cl.getModifiers ())) { |
313 | 0 | return pMethod; |
314 | } | |
315 | ||
316 | // Otherwise, try to find a public class that declares the method | |
317 | 0 | Method ret = getPublicMethod (cl, pMethod); |
318 | 0 | if (ret != null) { |
319 | 0 | return ret; |
320 | } | |
321 | else { | |
322 | 0 | return pMethod; |
323 | } | |
324 | } | |
325 | ||
326 | //------------------------------------- | |
327 | /** | |
328 | * | |
329 | * If the given class is public and has a Method that declares the | |
330 | * same name and arguments as the given method, then that method is | |
331 | * returned. Otherwise the superclass and interfaces are searched | |
332 | * recursively. | |
333 | **/ | |
334 | static Method getPublicMethod (Class pClass, | |
335 | Method pMethod) | |
336 | { | |
337 | // See if this is a public class declaring the method | |
338 | 0 | if (Modifier.isPublic (pClass.getModifiers ())) { |
339 | try { | |
340 | Method m; | |
341 | try { | |
342 | 0 | m = pClass.getDeclaredMethod (pMethod.getName (), |
343 | pMethod.getParameterTypes ()); | |
344 | 0 | } catch (java.security.AccessControlException ex) { |
345 | // kludge to accommodate J2EE RI's default settings | |
346 | // TODO: see if we can simply replace | |
347 | // getDeclaredMethod() with getMethod() ...? | |
348 | 0 | m = pClass.getMethod(pMethod.getName (), |
349 | pMethod.getParameterTypes ()); | |
350 | 0 | } |
351 | 0 | if (Modifier.isPublic (m.getModifiers ())) { |
352 | 0 | return m; |
353 | } | |
354 | } | |
355 | 0 | catch (NoSuchMethodException exc) {} |
356 | } | |
357 | ||
358 | // Search the interfaces | |
359 | { | |
360 | 0 | Class [] interfaces = pClass.getInterfaces (); |
361 | 0 | if (interfaces != null) { |
362 | 0 | for (int i = 0; i < interfaces.length; i++) { |
363 | 0 | Method m = getPublicMethod (interfaces [i], pMethod); |
364 | 0 | if (m != null) { |
365 | 0 | return m; |
366 | } | |
367 | } | |
368 | } | |
369 | } | |
370 | ||
371 | // Search the superclass | |
372 | { | |
373 | 0 | Class superclass = pClass.getSuperclass (); |
374 | 0 | if (superclass != null) { |
375 | 0 | Method m = getPublicMethod (superclass, pMethod); |
376 | 0 | if (m != null) { |
377 | 0 | return m; |
378 | } | |
379 | } | |
380 | } | |
381 | ||
382 | 0 | return null; |
383 | } | |
384 | ||
385 | //------------------------------------- | |
386 | } |