2012-02-16 20 views
9

Necesito contar el número de clases compiladas, interfaces y enumeraciones en un archivo jar dado programáticamente (así que necesito tres números separados). ¿Qué API me ayudaría? (No puedo usar bibliotecas de terceros.)analizar el archivo jar programmatically

Ya he intentado un esquema bastante complicado que parece no ser siempre correcto. A saber, leo cada ZipEntry en un byte [] y luego paso el resultado a mi cargador de clases personalizado que extiende el CalssLoader estándar y simplemente envía este byte [] a ClassLoader.defineClass (que es protectivo y no se puede invocar desde el código de la aplicación directamente) El código completo es on the Pastebin.

+0

¿Puede llamar a 'ZipEntry.getName()' y ver si se trata de un archivo '.class'? ¿O es necesario contar por separado las clases y las interfaces (y las enumeraciones?)? – DNA

+0

Necesito contarlos por separado (además, se sabe que no todos los archivos .class contienen el código de bytes correcto). –

+0

Los archivos .class con código de byte incorrecto que no deseamos contar. –

Respuesta

10

Un archivo jar es un archivo zip con un patrón específico. Puede usar un ZipFile y ZipEntry o sus clases secundarias JarFile y JarEntry.

Este código (un método de un cargador de clases personalizado) devolverá un Mapa con matrices de cada tipo de "clase" que necesite.

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

Muchas gracias, funciona casi a la perfección. Algunos detalles menores: para poder usar 'super.addURL' debes extender' URLClassLoader' no solo 'ClassLoader'. Y luego tienes que definir el constructor que llama a 'super' con la matriz de urls. Ojalá pudiera alimentarlo con nulo, pero tengo una excepción. Así que construí una matriz con una URL que corresponde a mi jar. –

+1

Puede pasar una matriz vacía ... Esto funciona bien ... Perdón por el malentendido acerca de la súper clase, este es un UrlClassloader ... – Eldius