View Javadoc

1   package org.apache.velocity.tools;
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  import java.io.InputStream;
23  import java.io.IOException;
24  import java.lang.reflect.Field;
25  import java.lang.reflect.InvocationTargetException;
26  import java.lang.reflect.Method;
27  import java.lang.reflect.Modifier;
28  import java.net.MalformedURLException;
29  import java.net.URL;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.Enumeration;
34  import java.util.Iterator;
35  import java.util.LinkedHashSet;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  import org.apache.velocity.util.ArrayIterator;
40  import org.apache.velocity.util.EnumerationIterator;
41  
42  /**
43   * Repository for common class and reflection methods.
44   *
45   * @author Nathan Bubna
46   * @version $Id: ClassUtils.java 511959 2007-02-26 19:24:39Z nbubna $
47   */
48  public class ClassUtils
49  {
50      public static final ClassUtils INSTANCE = new ClassUtils();
51  
52      private ClassUtils() {}
53  
54      public ClassUtils getInstance()
55      {
56          return INSTANCE;
57      }
58  
59      // shortcuts for readability...
60      private static final ClassLoader getThreadContextLoader()
61      {
62          return Thread.currentThread().getContextClassLoader();
63      }
64  
65      private static final ClassLoader getClassLoader()
66      {
67          return ClassUtils.class.getClassLoader();
68      }
69  
70      private static final ClassLoader getCallerLoader(Object caller)
71      {
72          if (caller instanceof Class)
73          {
74              return ((Class)caller).getClassLoader();
75          }
76          else
77          {
78              return caller.getClass().getClassLoader();
79          }
80      }
81  
82      /**
83       * Load a class with a given name.
84       * <p/>
85       * It will try to load the class in the following order:
86       * <ul>
87       * <li>From {@link Thread}.currentThread().getContextClassLoader()
88       * <li>Using the basic {@link Class#forName(java.lang.String) }
89       * <li>From {@link ClassUtils}.class.getClassLoader()
90       * </ul>
91       *
92       * @param name Fully qualified class name to be loaded
93       * @return Class object
94       * @exception ClassNotFoundException if the class cannot be found
95       */
96      public static Class getClass(String name) throws ClassNotFoundException
97      {
98          try
99          {
100             return getThreadContextLoader().loadClass(name);
101         }
102         catch (ClassNotFoundException e)
103         {
104             try
105             {
106                 return Class.forName(name);
107             }
108             catch (ClassNotFoundException ex)
109             {
110                 return getClassLoader().loadClass(name);
111             }
112         }
113     }
114 
115     public static Object getInstance(String classname)
116          throws ClassNotFoundException, IllegalAccessException,
117                 InstantiationException
118     {
119         return getClass(classname).newInstance();
120     }
121 
122     /**
123      * Load all resources with the specified name. If none are found, we 
124      * prepend the name with '/' and try again.
125      *
126      * This will attempt to load the resources from the following methods (in order):
127      * <ul>
128      * <li>Thread.currentThread().getContextClassLoader().getResources(name)</li>
129      * <li>{@link ClassUtils}.class.getClassLoader().getResources(name)</li>
130      * <li>{@link ClassUtils}.class.getResource(name)</li>
131      * <li>{@link #getCallerLoader(Object caller)}.getResources(name)</li>
132      * <li>caller.getClass().getResource(name)</li>
133      * </ul>
134      *
135      * @param name The name of the resources to load
136      * @param caller The instance or {@link Class} calling this method
137      */
138     public static List<URL> getResources(String name, Object caller)
139     {
140         Set<String> urls = new LinkedHashSet<String>();
141 
142         // try to load all from the current thread context classloader
143         addResources(name, urls, getThreadContextLoader());
144 
145         // try to load all from this class' classloader
146         if (!addResources(name, urls, getClassLoader()))
147         {
148             // ok, try to load one directly from this class
149             addResource(name, urls, ClassUtils.class);
150         }
151 
152         // try to load all from the classloader of the calling class
153         if (!addResources(name, urls, getCallerLoader(caller)))
154         {
155             // try to load one directly from the calling class
156             addResource(name, urls, caller.getClass());
157         }
158 
159         if (!urls.isEmpty())
160         {
161             List<URL> result = new ArrayList<URL>(urls.size());
162             try
163             {
164                 for (String url : urls)
165                 {
166                     result.add(new URL(url));
167                 }
168             }
169             catch (MalformedURLException mue)
170             {
171                 throw new IllegalStateException("A URL could not be recreated from its own toString() form", mue);
172             }
173             return result;
174         }
175         else if (!name.startsWith("/"))
176         {
177             // try again with a / in front of the name
178             return getResources("/"+name, caller);
179         }
180         else
181         {
182             return Collections.emptyList();
183         }
184     }
185 
186     private static final void addResource(String name, Set<String> urls, Class c)
187     {
188         URL url = c.getResource(name);
189         if (url != null)
190         {
191             urls.add(url.toString());
192         }
193     }
194 
195     private static final boolean addResources(String name, Set<String> urls,
196                                               ClassLoader loader)
197     {
198         boolean foundSome = false;
199         try
200         {
201             Enumeration<URL> e = loader.getResources(name);
202             while (e.hasMoreElements())
203             {
204                 urls.add(e.nextElement().toString());
205                 foundSome = true;
206             }
207         }
208         catch (IOException ioe)
209         {
210             // ignore
211         }
212         return foundSome;
213     }
214 
215     /**
216      * Load a given resource.
217      * <p/>
218      * This method will try to load the resource using the following methods (in order):
219      * <ul>
220      * <li>Thread.currentThread().getContextClassLoader().getResource(name)</li>
221      * <li>{@link ClassUtils}.class.getClassLoader().getResource(name)</li>
222      * <li>{@link ClassUtils}.class.getResource(name)</li>
223      * <li>caller.getClass().getResource(name) or, if caller is a Class,
224      *     caller.getResource(name)</li>
225      * </ul>
226      *
227      * @param name The name of the resource to load
228      * @param caller The instance or {@link Class} calling this method
229      */
230     public static URL getResource(String name, Object caller)
231     {
232         URL url = getThreadContextLoader().getResource(name);
233         if (url == null)
234         {
235             url = getClassLoader().getResource(name);
236             if (url == null)
237             {
238                 url = ClassUtils.class.getResource(name);
239                 if (url == null && caller != null)
240                 {
241                     Class callingClass = caller.getClass();
242                     if (callingClass == Class.class)
243                     {
244                         callingClass = (Class)caller;
245                     }
246                     url = callingClass.getResource(name);
247                 }
248             }
249         }
250         return url;
251     }
252 
253     /**
254      * This is a convenience method to load a resource as a stream.
255      * <p/>
256      * The algorithm used to find the resource is given in getResource()
257      *
258      * @param name The name of the resource to load
259      * @param caller The instance or {@link Class} calling this method
260      */
261     public static InputStream getResourceAsStream(String name, Object caller)
262     {
263         URL url = getResource(name, caller);
264         try
265         {
266             return (url == null) ? null : url.openStream();
267         }
268         catch (IOException e)
269         {
270             return null;
271         }
272     }
273 
274     public static Method findMethod(Class clazz, String name, Class[] params)
275         throws SecurityException
276     {
277         try
278         {
279             // check for a public setup(Map) method first
280             return clazz.getMethod(name, params);
281         }
282         catch (NoSuchMethodException nsme)
283         {
284             // ignore this
285         }
286         return findDeclaredMethod(clazz, name, params);
287     }
288 
289     public static Method findDeclaredMethod(Class clazz, String name, Class[] params)
290         throws SecurityException
291     {
292         try
293         {
294             // check for a protected one
295             Method method = clazz.getDeclaredMethod(name, params);
296             if (method != null)
297             {
298                 // and give this class access to it
299                 method.setAccessible(true);
300                 return method;
301             }
302         }
303         catch (NoSuchMethodException nsme)
304         {
305             // ignore this
306         }
307 
308         // ok, didn't find it declared in this class, try the superclass
309         Class supclazz = clazz.getSuperclass();
310         if (supclazz != null)
311         {
312             // recurse upward
313             return findDeclaredMethod(supclazz, name, params);
314         }
315         // otherwise, return null
316         return null;
317     }
318 
319     public static Object getFieldValue(String fieldPath)
320         throws ClassNotFoundException, NoSuchFieldException,
321                SecurityException, IllegalAccessException
322     {
323         int lastDot = fieldPath.lastIndexOf('.');
324         String classname = fieldPath.substring(0, lastDot);
325         String fieldname = fieldPath.substring(lastDot + 1, fieldPath.length());
326 
327         Class clazz = getClass(classname);
328         return getFieldValue(clazz, fieldname);
329     }
330 
331     public static Object getFieldValue(Class clazz, String fieldname)
332         throws NoSuchFieldException, SecurityException, IllegalAccessException
333     {
334         Field field = clazz.getField(fieldname);
335         int mod = field.getModifiers();
336         if (!Modifier.isStatic(mod))
337         {
338             throw new UnsupportedOperationException("Field "+fieldname+" in class "+clazz.getName()+" is not static.  Only static fields are supported.");
339         }
340         return field.get(null);
341     }
342 
343     /**
344      * Retrieves an Iterator from or creates and Iterator for the specified object.
345      * This method is almost entirely copied from Engine's UberspectImpl class.
346      */
347     public static Iterator getIterator(Object obj)
348         throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
349     {
350         if (obj.getClass().isArray())
351         {
352             return new ArrayIterator(obj);
353         }
354         else if (obj instanceof Collection)
355         {
356             return ((Collection) obj).iterator();
357         }
358         else if (obj instanceof Map)
359         {
360             return ((Map) obj).values().iterator();
361         }
362         else if (obj instanceof Iterator)
363         {
364             return ((Iterator) obj);
365         }
366         else if (obj instanceof Iterable)
367         {
368             return ((Iterable)obj).iterator();
369         }
370         else if (obj instanceof Enumeration)
371         {
372             return new EnumerationIterator((Enumeration) obj);
373         }
374         else
375         {
376             // look for an iterator() method to support
377             // any user tools/DTOs that want to work in
378             // foreach w/o implementing the Collection interface
379             Method iter = obj.getClass().getMethod("iterator");
380             if (Iterator.class.isAssignableFrom(iter.getReturnType()))
381             {
382                 return (Iterator)iter.invoke(obj);
383             }
384             else
385             {
386                 return null;
387             }
388         }
389     }
390 
391 }