Home | History | Annotate | Download | only in search
      1 /**
      2  * Copyright 2006-2017 the original author or authors.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package org.objenesis.tck.search;
     17 
     18 import java.io.File;
     19 import java.io.IOException;
     20 import java.net.URL;
     21 import java.util.*;
     22 import java.util.jar.JarEntry;
     23 import java.util.jar.JarFile;
     24 
     25 /**
     26  * Taken and adapted from <a href="https://raw.githubusercontent.com/ddopson/java-class-enumerator/master/src/pro/ddopson/ClassEnumerator.java">here</a>
     27  *
     28  * @author Henri Tremblay
     29  */
     30 public class ClassEnumerator {
     31 
     32     private static void processDirectory(File directory, String pkgname, SortedSet<String> classes) {
     33 
     34         // Get the list of the files contained in the package
     35         String[] files = directory.list();
     36 
     37         for (int i = 0; i < files.length; i++) {
     38             String fileName = files[i];
     39             // we are only interested in .class files
     40             if (fileName.endsWith(".class")) {
     41                 // removes the .class extension
     42                 String className = pkgname + '.' + fileName.substring(0, fileName.length() - 6);
     43                 classes.add(className);
     44                 continue;
     45             }
     46 
     47             File subdir = new File(directory, fileName);
     48             if (subdir.isDirectory()) {
     49                 processDirectory(subdir, pkgname + '.' + fileName, classes);
     50             }
     51         }
     52     }
     53 
     54     private static void processJarfile(URL resource, String pkgname, SortedSet<String> classes) {
     55         String relPath = pkgname.replace('.', '/');
     56         String resPath = resource.getPath();
     57         String jarPath = resPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
     58 
     59         JarFile jarFile;
     60         try {
     61             jarFile = new JarFile(jarPath);
     62         } catch (IOException e) {
     63             throw new RuntimeException("Unexpected IOException reading JAR File '" + jarPath + "'", e);
     64         }
     65         Enumeration<JarEntry> entries = jarFile.entries();
     66         while(entries.hasMoreElements()) {
     67             JarEntry entry = entries.nextElement();
     68             String entryName = entry.getName();
     69             String className = null;
     70             if(entryName.endsWith(".class") && entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) {
     71                 className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
     72             }
     73 
     74             if (className != null) {
     75                 classes.add(className);
     76             }
     77         }
     78     }
     79 
     80    /**
     81     * Return all the classes in this package recursively. The class loader of the {@code ClassEnumerator} class
     82     * is used
     83     *
     84     * @param pkg the searched package
     85     * @return list of full class names
     86     */
     87     public static SortedSet<String> getClassesForPackage(Package pkg) {
     88         return getClassesForPackage(pkg, ClassEnumerator.class.getClassLoader());
     89     }
     90 
     91    /**
     92     * Return all the classes in this package recursively.
     93     *
     94     * @param pkg the searched package
     95     * @return list of full class names
     96     */
     97     public static SortedSet<String> getClassesForPackage(Package pkg, ClassLoader classLoader) {
     98         SortedSet<String> classes = new TreeSet<String>(new Comparator<String>() {
     99            public int compare(String o1, String o2) {
    100               String simpleName1 = getSimpleName(o1);
    101               String simpleName2 = getSimpleName(o2);
    102               return simpleName1.compareTo(simpleName2);
    103            }
    104 
    105            private String getSimpleName(String className) {
    106               return className.substring(className.lastIndexOf('.'));
    107            }
    108         });
    109 
    110         String pkgname = pkg.getName();
    111         String relPath = pkgname.replace('.', '/');
    112 
    113         // Get a File object for the package
    114         Enumeration<URL> resources;
    115         try {
    116             resources = classLoader.getResources(relPath);
    117         } catch (IOException e) {
    118             throw new RuntimeException(e);
    119         }
    120 
    121         while(resources.hasMoreElements()) {
    122             URL resource = resources.nextElement();
    123             if (resource.toString().startsWith("jar:")) {
    124                 processJarfile(resource, pkgname, classes);
    125             } else {
    126                 processDirectory(new File(resource.getPath()), pkgname, classes);
    127             }
    128         }
    129 
    130         return classes;
    131     }
    132 
    133 }
    134