// Class you need
package com.luna.lib.reflection;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import com.luna.lib.loggers.BasicLogger;
import com.luna.lib.loggers.enums.EnumLogType;
public class ClassEnumerator {
/**
* Singleton instance
*/
private static volatile ClassEnumerator instance;
/**
* Returns the singleton instance creates one if the instance is null
*
* @return instance
*/
public static ClassEnumerator getInstance() {
if( instance == null ) {
BasicLogger.getInstance().setDebug( true );
instance = new ClassEnumerator();
}
return instance;
}
/**
* Parses a directory for jar files and class files
*
* Recurses through if necessary
*
* @param directory
* directory to parse
* @return class array
*/
public List< Class< ? >> getClassesFromExternalDirectory( final File directory ) {
final List< Class< ? >> classes = new ArrayList< Class< ? >>();
for( final File file : directory.listFiles() ) {
try {
final ClassLoader classLoader = new URLClassLoader( new URL[ ] {
file.toURI().toURL()
}, /* this */directory.getClass().getClassLoader() );
if( file.getName().toLowerCase().trim().endsWith( ".class" ) ) {
BasicLogger.getInstance().log( EnumLogType.DEBUG, file.getName() );
classes.add( classLoader.loadClass( file.getName().replace( ".class", "" )
.replace( "/", "." ) ) );
}
if( file.getName().toLowerCase().trim().endsWith( ".jar" ) ) {
classes.addAll( getClassesFromJar( file, classLoader ) );
}
if( file.isDirectory() ) {
classes.addAll( getClassesFromExternalDirectory( file ) );
}
} catch( final MalformedURLException e ) {
e.printStackTrace();
} catch( final ClassNotFoundException e ) {
e.printStackTrace();
}
}
return classes;
}
/**
* Returns the class array of all classes within a package
*
* @param classe
* class to get code source location for
*
* @return class array
*/
public Class< ? >[ ] getClassesFromPackage( final Class< ? > classe ) {
final List< Class< ? >> classes = new ArrayList< Class< ? >>();
URI uri = null;
try {
uri = classe.getProtectionDomain().getCodeSource().getLocation().toURI();
} catch( final URISyntaxException e ) {
e.printStackTrace();
}
if( uri == null ) {
throw new RuntimeException( "No uri for "
+ classe.getProtectionDomain().getCodeSource().getLocation() );
}
BasicLogger.getInstance().log( EnumLogType.DEBUG, "URI: " + uri.toString() );
classes.addAll( processDirectory( new File( uri ), "" ) );
return classes.toArray( new Class[ classes.size() ] );
}
/**
* Returns all class files inside a jar
*
* @param file
* jar file
* @param classLoader
* classloader created previously using the jar file
* @return class list
*/
public List< Class< ? >> getClassesFromJar( final File file, final ClassLoader classLoader ) {
final List< Class< ? >> classes = new ArrayList< Class< ? >>();
try {
final JarFile jarFile = new JarFile( file );
final Enumeration< JarEntry > enumeration = jarFile.entries();
while( enumeration.hasMoreElements() ) {
final JarEntry jarEntry = enumeration.nextElement();
if( jarEntry.isDirectory() || !jarEntry.getName().toLowerCase().trim().endsWith( ".class" ) ) {
continue;
}
classes.add( classLoader.loadClass( jarEntry.getName().replace( ".class", "" )
.replace( "/", "." ) ) );
}
jarFile.close();
} catch( final IOException e ) {
e.printStackTrace();
} catch( final ClassNotFoundException e ) {
e.printStackTrace();
}
return classes;
}
/**
* Processes a directory and retrieves all classes from it and its
* subdirectories
*
* Recurses if necessary
*
* @param directory
* directory file to traverse
* @return list of classes
*/
private List< Class< ? >> processDirectory( final File directory, final String append ) {
final List< Class< ? >> classes = new ArrayList< Class< ? >>();
final String[ ] files = directory.list();
for( final String fileName : files ) {
String className = null;
if( fileName.endsWith( ".class" ) ) {
className = append + '.' + fileName.replace( ".class", "" );
}
if( className != null ) {
classes.add( loadClass( className.substring( 1 ) ) );
}
final File subdir = new File( directory, fileName );
if( subdir.isDirectory() ) {
classes.addAll( processDirectory( subdir, append + "." + fileName ) );
}
}
return classes;
}
/**
* Loads a class based upon the name
*
* @param className
* name of class (.class is pre removed)
* @return Class if it was loaded properly
*/
private Class< ? > loadClass( final String className ) {
try {
return Class.forName( className );
} catch( final ClassNotFoundException e ) {
throw new RuntimeException( "Error loading class '" + className + "'" );
}
}
}
// In your Module Manager class, use this to get the list of classes that are modules
/**
* Parses the class code source to get modules from the retrieved classes
*
* @param classe
* class to get code source for
* @return module array from package
*/
private Module[ ] getModulesFromPackage( final Class< ? > classe ) {
final List< Module > modules = new ArrayList<>();
final Class< ? >[ ] classes = ClassEnumerator.getInstance().getClassesFromPackage( classe );
for ( final Class< ? > c : classes ) {
if ( Module.class.isAssignableFrom( c ) && !c.equals( Module.class ) ) {
try {
modules.add( ( Module ) c.newInstance() );
} catch ( final InstantiationException e ) {
e.printStackTrace();
} catch ( final IllegalAccessException e ) {
e.printStackTrace();
}
}
}
return modules.toArray( new Module[ modules.size() ] );
}
// And then use the returned array to load your modules!