1 package org.apache.velocity.util.introspection;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Modifier;
25 import java.util.Map;
26 import java.util.concurrent.ConcurrentHashMap;
27 import org.apache.velocity.runtime.log.Log;
28
29 /**
30 * A cache of introspection information for a specific class instance.
31 * Keys {@link java.lang.reflect.Field} objects by the field names.
32 *
33 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
34 * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
35 * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
36 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
37 * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
38 * @author Nathan Bubna
39 * @author <a href="mailto:cdauth@cdauth.eu">Candid Dauth</a>
40 */
41 public class ClassFieldMap
42 {
43 /** Set true if you want to debug the reflection code */
44 private static final boolean debugReflection = false;
45
46 /** Class logger */
47 private final Log log;
48
49 /**
50 * Class passed into the constructor used to as
51 * the basis for the Field map.
52 */
53 private final Class clazz;
54
55 /**
56 * String --> Field map, the key is the field name
57 */
58 private final Map fieldCache;
59
60 /**
61 * Standard constructor
62 * @param clazz The class for which this ClassMap gets constructed.
63 */
64 public ClassFieldMap(final Class clazz, final Log log)
65 {
66 this.clazz = clazz;
67 this.log = log;
68
69 if (debugReflection && log.isDebugEnabled())
70 {
71 log.debug("=================================================================");
72 log.debug("== Class: " + clazz);
73 }
74
75 fieldCache = createFieldCache();
76
77 if (debugReflection && log.isDebugEnabled())
78 {
79 log.debug("=================================================================");
80 }
81 }
82
83 /**
84 * Returns the class object whose fields are cached by this map.
85 *
86 * @return The class object whose fields are cached by this map.
87 */
88 public Class getCachedClass()
89 {
90 return clazz;
91 }
92
93 /**
94 * Find a Field using the field name.
95 *
96 * @param name The field name to look up.
97 * @return A Field object representing the field to invoke or null.
98 */
99 public Field findField(final String name)
100 {
101 return (Field)fieldCache.get(name);
102 }
103
104 /**
105 * Populate the Map of direct hits. These
106 * are taken from all the public fields
107 * that our class, its parents and their implemented interfaces provide.
108 */
109 private Map createFieldCache()
110 {
111 Map fieldCache = new ConcurrentHashMap();
112 //
113 // Looks through all elements in the class hierarchy.
114 //
115 // We ignore all SecurityExceptions that might happen due to SecurityManager restrictions (prominently
116 // hit with Tomcat 5.5).
117 // Ah, the miracles of Java for(;;) ...
118 for (Class classToReflect = getCachedClass(); classToReflect != null ; classToReflect = classToReflect.getSuperclass())
119 {
120 if (Modifier.isPublic(classToReflect.getModifiers()))
121 {
122 populateFieldCacheWith(fieldCache, classToReflect);
123 }
124 Class [] interfaces = classToReflect.getInterfaces();
125 for (int i = 0; i < interfaces.length; i++)
126 {
127 populateFieldCacheWithInterface(fieldCache, interfaces[i]);
128 }
129 }
130 // return the already initialized cache
131 return fieldCache;
132 }
133
134 /* recurses up interface heirarchy to get all super interfaces (VELOCITY-689) */
135 private void populateFieldCacheWithInterface(Map fieldCache, Class iface)
136 {
137 if (Modifier.isPublic(iface.getModifiers()))
138 {
139 populateFieldCacheWith(fieldCache, iface);
140 }
141 Class[] supers = iface.getInterfaces();
142 for (int i=0; i < supers.length; i++)
143 {
144 populateFieldCacheWithInterface(fieldCache, supers[i]);
145 }
146 }
147
148 private void populateFieldCacheWith(Map fieldCache, Class classToReflect)
149 {
150 if (debugReflection && log.isDebugEnabled())
151 {
152 log.debug("Reflecting " + classToReflect);
153 }
154
155 try
156 {
157 Field[] fields = classToReflect.getDeclaredFields();
158 for (int i = 0; i < fields.length; i++)
159 {
160 int modifiers = fields[i].getModifiers();
161 if (Modifier.isPublic(modifiers))
162 {
163 fieldCache.put(fields[i].getName(), fields[i]);
164 }
165 }
166 }
167 catch (SecurityException se) // Everybody feels better with...
168 {
169 if (log.isDebugEnabled())
170 {
171 log.debug("While accessing fields of " + classToReflect + ": ", se);
172 }
173 }
174 }
175
176 }