/** * Copyright (c) 2007-2011, JAGaToo Project Group all rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.jagatoo.util.classes; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * The {@link PackageSearcher} provides utility methods to search classes. * * @author Marvin Froehlich (aka Qudus) */ public class PackageSearcher { /** * Reads all package names in a given folder and its subfolders and * puts them into the List. * * @param baseFolder the folder from which to read the classes * @param folder the folder from which to read the classes as a File object * @param packages the List to put the packages into */ private static void findPackageNamesFromFolder( String baseFolder, File folder, int levelToParent, String[] packagePrefixes, boolean includeParents, boolean includeSubpackages, List<String> packages ) { for ( File file: folder.listFiles() ) { if ( file.isDirectory() ) { final String packageName = file.getAbsolutePath(); for ( int i = 0; i < packagePrefixes.length; i++ ) { boolean isSubPackage = false; if ( packageName.startsWith( packagePrefixes[i], baseFolder.length() ) ) { if ( packageName.length() == baseFolder.length() + packagePrefixes[i].length() ) { if ( includeParents ) packages.add( packageName.substring( baseFolder.length() ).replace( File.separatorChar, '.' ) ); levelToParent = 0; } else { if ( includeSubpackages || ( levelToParent <= 1 ) ) packages.add( packageName.substring( baseFolder.length() ).replace( File.separatorChar, '.' ) ); isSubPackage = true; } } if ( levelToParent == 0 ) includeParents = false; if ( ( levelToParent >= 0 ) && isSubPackage ) levelToParent++; if ( ( levelToParent > 1 ) && !includeSubpackages ) findPackageNamesFromFolder( baseFolder, file, levelToParent, packagePrefixes, includeParents, false, packages ); else findPackageNamesFromFolder( baseFolder, file, levelToParent, packagePrefixes, includeParents, includeSubpackages, packages ); } } } } /** * Reads all package names in a given folder and its subfolders and * puts them into the List. * * @param baseFolder the folder from which to read the classes * @param packages the List to put the packages into */ private static void findPackageNamesFromFolder( String folderName, String[] packagePrefixes, boolean includeParents, boolean includeSubpackages, List<String> packages ) { findPackageNamesFromFolder( new File( folderName ).getAbsolutePath() + File.separator, new File( folderName ), -1, packagePrefixes, includeParents, includeSubpackages, packages ); } /** * Reads all package names in a given folder and its subfolders and * puts them into the List. * * @param baseFolder the folder from which to read the classes * @param folder the folder from which to read the classes as a File object * @param packages the List to put the packages into */ private static void findPackageNamesFromFolder( String baseFolder, File folder, String[] packageContains, List<String> packages ) { for ( File file: folder.listFiles() ) { if ( file.isDirectory() ) { final String packageName = file.getAbsolutePath(); for ( int i = 0; i < packageContains.length; i++ ) { if ( packageName.contains( packageContains[i] ) ) { packages.add( packageName.substring( baseFolder.length() ).replace( File.separatorChar, '.' ) ); } } findPackageNamesFromFolder( baseFolder, file, packageContains, packages ); } } } private static int getDepth( String packageName, char separator ) { if ( ( packageName.length() == 1 ) && ( packageName.charAt( 0 ) == separator ) ) return ( 0 ); int depth = 1; for ( int i = 0; i < packageName.length(); i++ ) { if ( packageName.charAt( i ) == separator ) depth++; } if ( packageName.charAt( packageName.length() - 1 ) == separator ) return ( depth - 1 ); return ( depth ); } /** * Reads all package names in given packages from a JarFile and puts their names * into a List. * * @param jarFilename the filename of the jar * @param packages the List to put the packages into */ private static void findPackageNamesFromJar( String jarFilename, String[] packagePrefixes, boolean includeParents, boolean includeSubpackages, List<String> packages ) { if ( !( new File( jarFilename ).exists() ) ) { System.err.println( "Couldn't find jar file " + jarFilename ); return; } int[] depths = new int[ packagePrefixes.length ]; for ( int i = 0; i < packagePrefixes.length; i++ ) { depths[i] = getDepth( packagePrefixes[i], '/' ); } try { HashSet<String> set = new HashSet<String>(); for ( int i = 0; i < packagePrefixes.length; i++ ) { set.add( packagePrefixes[i].replace( '/', '.' ) ); } jarFilename = new File( jarFilename ).getCanonicalPath(); JarFile jar = new JarFile( jarFilename ); Enumeration<JarEntry> jarEntries = jar.entries(); while ( jarEntries.hasMoreElements() ) { JarEntry jarEntry = jarEntries.nextElement(); for ( int i = 0; i < packagePrefixes.length; i++ ) { if ( jarEntry.getName().startsWith( packagePrefixes[i] ) ) { if ( jarEntry.isDirectory() ) { set.add( jarEntry.getName().replace( '/', '.' ) ); } else { String entryName = jarEntry.getName(); int idx = entryName.lastIndexOf( '/' ); if ( idx >= 0 ) { entryName = entryName.substring( 0, idx ); set.add( entryName.replace( '/', '.' ) ); } } } } } for ( String packageName : set ) { for ( int i = 0; i < packagePrefixes.length; i++ ) { if ( packageName.length() == packagePrefixes[ i ].length() ) { if ( includeParents ) packages.add( packageName ); } else { if ( includeSubpackages ) packages.add( packageName ); else if ( getDepth( packageName, '.' ) == depths[i] + 1 ) packages.add( packageName ); } } } } catch ( IOException e ) { e.printStackTrace(); } } /** * Reads all package names in given packages from a JarFile and puts their names * into a List. * * @param jarFilename the filename of the jar * @param packages the List to put the packages into */ private static void findPackageNamesFromJar( String jarFilename, String[] packageContains, List<String> packages ) { if ( !( new File( jarFilename ).exists() ) ) { System.err.println( "Couldn't find jar file " + jarFilename ); return; } try { HashSet<String> set = new HashSet<String>(); for ( int i = 0; i < packageContains.length; i++ ) { set.add( packageContains[i].replace( '/', '.' ) ); } jarFilename = new File( jarFilename ).getCanonicalPath(); JarFile jar = new JarFile( jarFilename ); Enumeration<JarEntry> jarEntries = jar.entries(); while ( jarEntries.hasMoreElements() ) { JarEntry jarEntry = jarEntries.nextElement(); for ( int i = 0; i < packageContains.length; i++ ) { if ( jarEntry.getName().contains( packageContains[i] ) ) { if ( jarEntry.isDirectory() ) { set.add( jarEntry.getName().replace( '/', '.' ) ); } else { String entryName = jarEntry.getName(); int idx = entryName.lastIndexOf( '/' ); if ( idx >= 0 ) { entryName = entryName.substring( 0, idx ); set.add( entryName.replace( '/', '.' ) ); } } } } } for ( String packageName : set ) { packages.add( packageName ); } } catch ( IOException e ) { e.printStackTrace(); } } /** * Reads all sub-packages from given packages into a List. * * @param includeParents include the packages from the passed-in list themselfs into the result list * @param includeSubpackages include the packages not directly in the searched packages into the result list * @param packagePrefixes dot separated package prefix names (like "org.xith3d.test") * * @return the filled up List */ public static List<String> findPackages( boolean includeParents, boolean includeSubpackages, String... packagePrefixes ) { String[] packagePrifixes_slash = new String[ packagePrefixes.length ]; String[] packagePrifixes_separator = new String[ packagePrefixes.length ]; String[] packageContains_slash = new String[ packagePrefixes.length ]; String[] packageContains_separator = new String[ packagePrefixes.length ]; int numPrefixes = 0; int numContains = 0; for ( int i = 0; i < packagePrefixes.length; i++ ) { String prefixSlash = packagePrefixes[i].replace( '.', '/' ); String prefixSeparator = packagePrefixes[i].replace( '.', File.separatorChar ); if ( prefixSlash.startsWith( "*" ) ) { if ( prefixSlash.endsWith( "*" ) ) { packageContains_slash[numContains] = prefixSlash.substring( 1, prefixSlash.length() - 1 ); packageContains_separator[numContains] = prefixSeparator.substring( 1, prefixSeparator.length() - 1 ); } else { packageContains_slash[numContains] = prefixSlash.substring( 1 ); packageContains_separator[numContains] = prefixSeparator.substring( 1 ); } numContains++; } else if ( prefixSlash.endsWith( "*" ) ) { packageContains_slash[numContains] = prefixSlash.substring( 0, prefixSlash.length() - 1 ); packageContains_separator[numContains] = prefixSeparator.substring( 0, prefixSeparator.length() - 1 ); numContains++; } else { packagePrifixes_slash[numPrefixes] = prefixSlash; packagePrifixes_separator[numPrefixes] = prefixSeparator; numPrefixes++; } } if ( numPrefixes < packagePrefixes.length ) { String[] tmp = new String[ numPrefixes ]; System.arraycopy( packagePrifixes_slash, 0, tmp, 0, numPrefixes ); packagePrifixes_slash = tmp; tmp = new String[ numPrefixes ]; System.arraycopy( packagePrifixes_separator, 0, tmp, 0, numPrefixes ); packagePrifixes_separator = tmp; } if ( numContains < packagePrefixes.length ) { String[] tmp = new String[ numContains ]; System.arraycopy( packageContains_slash, 0, tmp, 0, numContains ); packageContains_slash = tmp; tmp = new String[ numContains ]; System.arraycopy( packageContains_separator, 0, tmp, 0, numContains ); packageContains_separator = tmp; } List<String> packages = new ArrayList<String>(); String[] classPath = System.getProperty( "java.class.path" ).split( System.getProperty( "path.separator" ) ); for ( String cp: classPath ) { if ( cp.endsWith( ".jar" ) ) { if ( numPrefixes > 0 ) findPackageNamesFromJar( cp, packagePrifixes_slash, includeParents, includeSubpackages, packages ); if ( numContains > 0 ) findPackageNamesFromJar( cp, packageContains_slash, packages ); } else { if ( numPrefixes > 0 ) findPackageNamesFromFolder( cp, packagePrifixes_separator, includeParents, includeSubpackages, packages ); if ( numContains > 0 ) findPackageNamesFromFolder( new File( cp ).getAbsolutePath() + File.separator, new File( cp ), packageContains_separator, packages ); } } Collections.sort( packages, new Comparator<String>() { @Override public int compare( String p1, String p2 ) { return ( p1.compareTo( p2 ) ); } } ); return ( packages ); } /** * Reads all sub-packages from given packages into a List. * * @param packagePrefixes dot separated package prefix names (like "org.xith3d.test") * * @return the filled up List */ public static List<String> findPackages( String... packagePrefixes ) { return ( findPackages( true, true, packagePrefixes ) ); } private PackageSearcher() { } }