|
From: <tri...@us...> - 2007-12-19 15:40:52
|
Revision: 212
http://equanda.svn.sourceforge.net/equanda/?rev=212&view=rev
Author: triathlon98
Date: 2007-12-19 07:40:46 -0800 (Wed, 19 Dec 2007)
Log Message:
-----------
EQ-116 allow determining classes in a package
Added Paths:
-----------
trunk/equanda-util/src/main/java/org/equanda/util/ReflectionUtil.java
Added: trunk/equanda-util/src/main/java/org/equanda/util/ReflectionUtil.java
===================================================================
--- trunk/equanda-util/src/main/java/org/equanda/util/ReflectionUtil.java (rev 0)
+++ trunk/equanda-util/src/main/java/org/equanda/util/ReflectionUtil.java 2007-12-19 15:40:46 UTC (rev 212)
@@ -0,0 +1,253 @@
+/**
+ * This file is part of the equanda project.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
+ * ANY KIND, either express or implied. See the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ */
+
+package org.equanda.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * based on the ReflectionUtil class in m-m-m
+ *
+ * @author Joerg Hohwiller (hohwille at users.sourceforge.net)
+ */
+public class ReflectionUtil
+{
+
+ /**
+ * This is the singleton instance of this {@link ReflectionUtil}. Instead of declaring the methods static, we declare
+ * this static instance what gives the same way of access while still allowing a design for extension by inheriting
+ * from this class.
+ */
+ public static final ReflectionUtil INSTANCE = new ReflectionUtil();
+
+ /**
+ * Forbidden constructor.
+ */
+ private ReflectionUtil()
+ {
+ super();
+ }
+
+ /**
+ * This method checks and transforms the filename of a potential {@link Class} given by <code>fileName</code>.
+ *
+ * @param fileName is the filename.
+ * @return the according Java {@link Class#getName() class-name} for the given <code>fileName</code> if it is a
+ * class-file that is no anonymous {@link Class}, else <code>null</code>.
+ */
+ private static String fixClassName( String fileName )
+ {
+
+ if ( fileName.endsWith( ".class" ) )
+ {
+ // remove extension (".class".length() == 6)
+ String nameWithoutExtension = fileName.substring( 0, fileName.length() - 6 );
+ // handle inner classes...
+ /*
+ * int lastDollar = nameWithoutExtension.lastIndexOf('$'); if (lastDollar >
+ * 0) { char innerClassStart = nameWithoutExtension.charAt(lastDollar +
+ * 1); if ((innerClassStart >= '0') && (innerClassStart <= '9')) { //
+ * ignore anonymous class } else { return
+ * nameWithoutExtension.replace('$', '.'); } } else { return
+ * nameWithoutExtension; }
+ */
+ return nameWithoutExtension;
+ }
+ return null;
+ }
+
+ /**
+ * This method finds the recursively scans the given <code>packageDirectory</code> for {@link Class} files and adds
+ * their according Java names to the given <code>classSet</code>.
+ *
+ * @param packageDirectory is the directory representing the {@link Package}.
+ * @param classSet is where to add the Java {@link Class}-names to.
+ * @param qualifiedNameBuilder is a {@link StringBuilder} containing the qualified prefix (the {@link Package} with a
+ * trailing dot).
+ * @param qualifiedNamePrefixLength the length of the prefix used to rest the string-builder after reuse.
+ */
+ private static void findClassNamesRecursive( File packageDirectory, Set<String> classSet,
+ StringBuilder qualifiedNameBuilder, int qualifiedNamePrefixLength )
+ {
+
+ for ( File childFile : packageDirectory.listFiles() )
+ {
+ String fileName = childFile.getName();
+ if ( childFile.isDirectory() )
+ {
+ qualifiedNameBuilder.setLength( qualifiedNamePrefixLength );
+ StringBuilder subBuilder = new StringBuilder( qualifiedNameBuilder );
+ subBuilder.append( fileName );
+ subBuilder.append( '.' );
+ findClassNamesRecursive( childFile, classSet, subBuilder, subBuilder.length() );
+ }
+ else
+ {
+ String simpleClassName = fixClassName( fileName );
+ if ( simpleClassName != null )
+ {
+ qualifiedNameBuilder.setLength( qualifiedNamePrefixLength );
+ qualifiedNameBuilder.append( simpleClassName );
+ classSet.add( qualifiedNameBuilder.toString() );
+ }
+ }
+ }
+ }
+
+ /**
+ * This method finds all classes that are located in the package identified by the given <code>packageName</code>.<br>
+ * <b>ATTENTION:</b><br> This is a relative expensive operation. Depending on your classpath multiple
+ * directories,JAR-, and WAR-files may need to be scanned.
+ *
+ * @param packageName is the name of the {@link Package} to scan.
+ * @param includeSubPackages - if <code>true</code> all sub-packages of the specified {@link Package} will be included
+ * in the search.
+ * @return a {@link Set} will the fully qualified names of all requested classes.
+ * @throws java.io.IOException if the operation failed with an I/O error.
+ */
+ public Set<String> findClassNames( String packageName, boolean includeSubPackages )
+ throws IOException
+ {
+
+ Set<String> classSet = new HashSet<String>();
+ findClassNames( packageName, includeSubPackages, classSet );
+ return classSet;
+ }
+
+ /**
+ * This method finds all classes that are located in the package identified by the given <code>packageName</code>.<br>
+ * <b>ATTENTION:</b><br> This is a relative expensive operation. Depending on your classpath multiple
+ * directories,JAR-, and WAR-files may need to be scanned.
+ *
+ * @param packageName is the name of the {@link Package} to scan.
+ * @param includeSubPackages - if <code>true</code> all sub-packages of the specified {@link Package} will be included
+ * in the search.
+ * @param classSet is where to add the classes.
+ * @throws IOException if the operation failed with an I/O error.
+ */
+ public void findClassNames( String packageName, boolean includeSubPackages, Set<String> classSet )
+ throws IOException
+ {
+
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ String path = packageName.replace( '.', '/' );
+ String pathWithPrefix = path + '/';
+ Enumeration<URL> urls = classLoader.getResources( path );
+ StringBuilder qualifiedNameBuilder = new StringBuilder( packageName );
+ qualifiedNameBuilder.append( '.' );
+ int qualifiedNamePrefixLength = qualifiedNameBuilder.length();
+ while ( urls.hasMoreElements() )
+ {
+ URL packageUrl = urls.nextElement();
+ String urlString = URLDecoder.decode( packageUrl.getFile(), "UTF-8" );
+ String protocol = packageUrl.getProtocol().toLowerCase();
+ if ( "file".equals( protocol ) )
+ {
+ File packageDirectory = new File( urlString );
+ if ( packageDirectory.isDirectory() )
+ {
+ if ( includeSubPackages )
+ {
+ findClassNamesRecursive( packageDirectory, classSet, qualifiedNameBuilder,
+ qualifiedNamePrefixLength );
+ }
+ else
+ {
+ for ( String fileName : packageDirectory.list() )
+ {
+ String simpleClassName = fixClassName( fileName );
+ if ( simpleClassName != null )
+ {
+ qualifiedNameBuilder.setLength( qualifiedNamePrefixLength );
+ qualifiedNameBuilder.append( simpleClassName );
+ classSet.add( qualifiedNameBuilder.toString() );
+ }
+ }
+ }
+ }
+ }
+ else if ( "jar".equals( protocol ) )
+ {
+ // somehow the connection has no close method and can NOT be disposed
+ JarURLConnection connection = (JarURLConnection) packageUrl.openConnection();
+ JarFile jarFile = connection.getJarFile();
+ Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
+ while ( jarEntryEnumeration.hasMoreElements() )
+ {
+ JarEntry jarEntry = jarEntryEnumeration.nextElement();
+ String absoluteFileName = jarEntry.getName();
+ if ( absoluteFileName.endsWith( ".class" ) )
+ {
+ if ( absoluteFileName.startsWith( "/" ) )
+ {
+ absoluteFileName = absoluteFileName.substring( 1 );
+ }
+ // special treatment for WAR files...
+ // "WEB-INF/lib/" entries should be opened directly in contained jar
+ if ( absoluteFileName.startsWith( "WEB-INF/classes/" ) )
+ {
+ // "WEB-INF/classes/".length() == 16
+ absoluteFileName = absoluteFileName.substring( 16 );
+ }
+ boolean accept = true;
+ if ( absoluteFileName.startsWith( pathWithPrefix ) )
+ {
+ String qualifiedName = absoluteFileName.replace( '/', '.' );
+ if ( !includeSubPackages )
+ {
+ int index = absoluteFileName.indexOf( '/', qualifiedNamePrefixLength + 1 );
+ if ( index != -1 )
+ {
+ accept = false;
+ }
+ }
+ if ( accept )
+ {
+ String className = fixClassName( qualifiedName );
+ if ( className != null )
+ {
+ classSet.add( className );
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // TODO: unknown protocol - log this?
+ }
+ }
+ }
+}
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|