2012-02-16 9 views
9

Potrzebuję policzyć liczbę skompilowanych klas, interfejsów i enums w danym pliku jar (program potrzebuję trzech oddzielnych numerów). Który interfejs API mi pomoże? (Nie mogę korzystać z bibliotek stron trzecich.)analizować plik jar programowo

Próbowałem już dość skomplikowanego schematu, który nie zawsze jest poprawny. Mianowicie czytam każdy ZipEntry w bajcie [], a następnie przesyłam wynik do mojego niestandardowego programu ładującego klasy, który rozszerza standard CalssLoader i po prostu wysyła ten bajt [] do ClassLoader.defineClass (który jest chroniony i nie można go wywołać bezpośrednio z kodu aplikacji). Pełny kod to on the Pastebin.

+0

Czy można wywołać 'ZipEntry.getName()' i sprawdzić, czy jest to plik '.class'? Czy potrzebujesz osobno liczyć klasy i interfejsy (i wylicza?)? – DNA

+0

Muszę policzyć je oddzielnie (dodatkowo wiadomo, że nie wszystkie pliki .class zawierają poprawny kod bajtowy). –

+0

Pliki .class z niepoprawnym kodem bajtowym nie liczą się. –

Odpowiedz

10

Plik jar to plik zip o określonym wzorze. Możesz użyć pliku ZipFile i ZipEntry lub ich dzieci klasy JarFile i JarEntry.

Ten kod (metoda niestandardowego programu ładującego klasy) zwróci mapę z tablicami każdego typu "klasy", której potrzebujesz.

public Map<String, List<Class<?>>> loadAndScanJar(File jarFile) 
     throws ClassNotFoundException, ZipException, IOException { 

    // Load the jar file into the JVM 
    // You can remove this if the jar file already loaded. 
    super.addURL(jarFile.toURI().toURL()); 

    Map<String, List<Class<?>>> classes = new HashMap<String, List<Class<?>>>(); 

    List<Class<?>> interfaces = new ArrayList<Class<?>>(); 
    List<Class<?>> clazzes = new ArrayList<Class<?>>(); 
    List<Class<?>> enums = new ArrayList<Class<?>>(); 
    List<Class<?>> annotations = new ArrayList<Class<?>>(); 

    classes.put("interfaces", interfaces); 
    classes.put("classes", clazzes); 
    classes.put("annotations", annotations); 
    classes.put("enums", enums); 

    // Count the classes loaded 
    int count = 0; 

    // Your jar file 
    JarFile jar = new JarFile(jarFile); 
    // Getting the files into the jar 
    Enumeration<? extends JarEntry> enumeration = jar.entries(); 

    // Iterates into the files in the jar file 
    while (enumeration.hasMoreElements()) { 
     ZipEntry zipEntry = enumeration.nextElement(); 

     // Is this a class? 
     if (zipEntry.getName().endsWith(".class")) { 

      // Relative path of file into the jar. 
      String className = zipEntry.getName(); 

      // Complete class name 
      className = className.replace(".class", "").replace("/", "."); 
      // Load class definition from JVM 
      Class<?> clazz = this.loadClass(className); 

      try { 
       // Verify the type of the "class" 
       if (clazz.isInterface()) { 
        interfaces.add(clazz); 
       } else if (clazz.isAnnotation()) { 
        annotations.add(clazz); 
       } else if (clazz.isEnum()) { 
        enums.add(clazz); 
       } else { 
        clazzes.add(clazz); 
       } 

       count++; 
      } catch (ClassCastException e) { 

      } 
     } 
    } 

    System.out.println("Total: " + count); 

    return classes; 
} 
+0

Wielkie dzięki, działa prawie idealnie. Kilka drobnych szczegółów: aby móc używać 'super.addURL', powinieneś rozszerzyć' URLClassLoader' nie tylko 'ClassLoader'. A następnie musisz zdefiniować konstruktor, który wywołuje 'super' z tablicą adresów URL. Chciałbym móc ją podać z wartością null, ale otrzymałem wyjątek. Więc skonstruowałem tablicę z jednym adresem URL, który odpowiada mojemu słoikowi. –

+1

Możesz przekazać pustą tablicę ... Działa to dobrze ... Przepraszam za niezrozumienie tej superklasy, jest to UrlClassloader ... – Eldius