View Javadoc

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 --&gt; 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 }