View Javadoc
1   package org.opentrafficsim.core;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.lang.reflect.Field;
6   import java.lang.reflect.Modifier;
7   import java.util.ArrayList;
8   import java.util.Collection;
9   import java.util.Enumeration;
10  import java.util.regex.Pattern;
11  import java.util.zip.ZipEntry;
12  import java.util.zip.ZipException;
13  import java.util.zip.ZipFile;
14  
15  /**
16   * Build a list of the classes in the project (or under a specific directory/package in the project).<br>
17   * Adapted from <a href="http://stackoverflow.com/questions/3923129/get-a-list-of-resources-from-classpath-directory">
18   * http://stackoverflow.com/questions/3923129/get-a-list-of-resources-from-classpath-directory</a> which apparently copied the
19   * code from <a href="http://forums.devx.com/showthread.php?153784-how-to-list-resources-in-a-package">
20   * http://forums.devx.com/showthread.php?153784-how-to-list-resources-in-a-package</a>. Original poster stoughto has not visited
21   * that forum after 2006.
22   * <p>
23   * Copyright (c) 2006 by stoughto! TODO replace this by something that is provably free code.
24   * <p>
25   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 8, 2016 <br>
26   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
27   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
28   */
29  public final class ClassList
30  {
31      /**
32       * Do not instantiate this class.
33       */
34      private ClassList()
35      {
36          // This class cannot be instantiated
37      }
38  
39      /**
40       * For all elements of java.class.path get a Collection of resources Pattern pattern = Pattern.compile(".*"); gets all
41       * resources.
42       * @param pattern the pattern to match
43       * @return the resources in the order they are found
44       */
45      public static Collection<String> getResources(final Pattern pattern)
46      {
47          final ArrayList<String> retval = new ArrayList<String>();
48          final String classPath = System.getProperty("java.class.path", ".");
49          final String[] classPathElements = classPath.split(System.getProperty("path.separator"));
50          for (final String element : classPathElements)
51          {
52              retval.addAll(getResources(element, pattern));
53          }
54          return retval;
55      }
56  
57      /**
58       * Recursively load names from a resource tree.
59       * @param element String; root of the tree to load
60       * @param pattern Pattern; only return names matching this pattern
61       * @return Collection&lt;String&gt;; a list of names
62       */
63      private static Collection<String> getResources(final String element, final Pattern pattern)
64      {
65          final ArrayList<String> retval = new ArrayList<String>();
66          final File file = new File(element);
67          if (file.isDirectory())
68          {
69              retval.addAll(getResourcesFromDirectory(file, pattern));
70          }
71          else
72          {
73              retval.addAll(getResourcesFromJarFile(file, pattern));
74          }
75          return retval;
76      }
77  
78      /**
79       * Recursively load names from a jar file.
80       * @param file File; root of the tree to load
81       * @param pattern Pattern; only return names matching this pattern
82       * @return Collection&lt;String&gt;; a list of names
83       */
84      private static Collection<String> getResourcesFromJarFile(final File file, final Pattern pattern)
85      {
86          final ArrayList<String> retval = new ArrayList<String>();
87          ZipFile zf;
88          try
89          {
90              zf = new ZipFile(file);
91          }
92          catch (final ZipException e)
93          {
94              throw new Error(e);
95          }
96          catch (final IOException e)
97          {
98              throw new Error(e);
99          }
100         final Enumeration<?> e = zf.entries();
101         while (e.hasMoreElements())
102         {
103             final ZipEntry ze = (ZipEntry) e.nextElement();
104             final String fileName = ze.getName();
105             final boolean accept = pattern.matcher(fileName).matches();
106             if (accept)
107             {
108                 retval.add(fileName);
109             }
110         }
111         try
112         {
113             zf.close();
114         }
115         catch (final IOException e1)
116         {
117             throw new Error(e1);
118         }
119         return retval;
120     }
121 
122     /**
123      * Recursively load names from a directory.
124      * @param directory File; root of the tree to load
125      * @param pattern Pattern; only return names matching this pattern
126      * @return Collection&lt;String&gt;; a list of names
127      */
128     private static Collection<String> getResourcesFromDirectory(final File directory, final Pattern pattern)
129     {
130         final ArrayList<String> retval = new ArrayList<String>();
131         final File[] fileList = directory.listFiles();
132         if (null == fileList)
133         {
134             throw new Error("Could not list files");
135         }
136         for (final File file : fileList)
137         {
138             if (file.isDirectory())
139             {
140                 retval.addAll(getResourcesFromDirectory(file, pattern));
141             }
142             else
143             {
144                 try
145                 {
146                     final String fileName = file.getCanonicalPath();
147                     final boolean accept = pattern.matcher(fileName).matches();
148                     if (accept)
149                     {
150                         retval.add(fileName);
151                     }
152                 }
153                 catch (final IOException e)
154                 {
155                     throw new Error(e);
156                 }
157             }
158         }
159         return retval;
160     }
161 
162     /**
163      * Return a List of all the classes under a package. Test-classes are excluded from the result.
164      * @param packageRoot String package name
165      * @param excludeInterfaces boolean; if true; interfaces are excluded from the result
166      * @return Collection&lt;Class&lt;?&gt;&gt;; the classes under the package
167      */
168     public static Collection<Class<?>> classList(final String packageRoot, final boolean excludeInterfaces)
169     {
170         Collection<String> classList = ClassList.getResources(Pattern.compile(".*[^-]classes." + packageRoot + ".*\\.class"));
171         Collection<Class<?>> result = new ArrayList<Class<?>>();
172         for (String className : classList)
173         {
174             int pos = className.indexOf("\\org\\");
175             if (pos >= 0)
176             {
177                 className = className.substring(pos + 1);
178             }
179             className = className.replaceAll("\\\\", ".");
180             pos = className.lastIndexOf(".class");
181             if (pos >= 0)
182             {
183                 className = className.substring(0, pos);
184             }
185             if (className.endsWith("package-info"))
186             {
187                 continue; // Not a real class
188             }
189             // System.out.println("Checking class \"" + className + "\"");
190             try
191             {
192                 Class<?> c = Class.forName(className);
193                 // System.out.println("modifiers: " + Modifier.toString(c.getModifiers()));
194                 boolean exclude = false;
195                 if (excludeInterfaces)
196                 {
197                     for (String modifierString : Modifier.toString(c.getModifiers()).split(" "))
198                     {
199                         if (modifierString.equals("interface"))
200                         {
201                             // System.out.println(className + " is an interface");
202                             exclude = true;
203                             continue;
204                         }
205                     }
206                 }
207                 if (!exclude)
208                 {
209                     result.add(c);
210                 }
211             }
212             catch (ClassNotFoundException exception)
213             {
214                 exception.printStackTrace();
215             }
216         }
217         return result;
218     }
219 
220     /**
221      * Determine if a class is an anonymous inner class.
222      * @param c Class; the class to check
223      * @return boolean; true if <cite>c</cite> is an anonymous inner class; false otherwise
224      */
225     public static boolean isAnonymousInnerClass(final Class<?> c)
226     {
227         String className = c.getName();
228         int pos = className.lastIndexOf("$");
229         if (pos > 0)
230         {
231             while (++pos < className.length())
232             {
233                 if (!Character.isDigit(className.charAt(pos)))
234                 {
235                     break;
236                 }
237             }
238             if (pos >= className.length())
239             {
240                 return true;
241             }
242         }
243         return false;
244     }
245 
246     /**
247      * Report if a class has non-static fields.
248      * @param c Class&lt;?&gt;; the class
249      * @return boolean; true if the class has non-static fields
250      */
251     public static boolean hasNonStaticFields(final Class<?> c)
252     {
253         for (Field f : c.getDeclaredFields())
254         {
255             // System.out.println("field " + f.getName() + ": " + Modifier.toString(f.getModifiers()));
256             if (!Modifier.isStatic(f.getModifiers()))
257             {
258                 return true;
259             }
260         }
261         if (c.equals(Object.class))
262         {
263             return false;
264         }
265         return hasNonStaticFields(c.getSuperclass());
266     }
267 
268     /**
269      * List the resources that match args[0], or a fixed pattern to demonstrate the use of this class.
270      * @param args args[0] is the pattern to match, or list all resources matching a built-in pattern if there are no args
271      */
272     public static void main(final String[] args)
273     {
274         Pattern pattern;
275         if (args.length < 1)
276         {
277             pattern = Pattern.compile(".*[^-]classes.org.opentrafficsim.*\\.class");
278         }
279         else
280         {
281             pattern = Pattern.compile(args[0]);
282         }
283         final Collection<String> list = ClassList.getResources(pattern);
284         for (final String name : list)
285         {
286             System.out.println(name);
287         }
288     }
289 
290 }