@ -28,15 +28,23 @@ import java.net.URI;
import java.net.URL ;
import java.net.URLDecoder ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.Enumeration ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.concurrent.ConcurrentHashMap ;
import java.util.jar.JarEntry ;
import java.util.stream.Collectors ;
public class PackageInternalsFinder {
private final ClassLoader classLoader ;
private static final String CLASS_FILE_EXTENSION = ".class" ;
private static final Map < String , JarFileIndex > INDEXS = new ConcurrentHashMap < > ( ) ;
public PackageInternalsFinder ( ClassLoader classLoader ) {
this . classLoader = classLoader ;
}
@ -60,11 +68,30 @@ public class PackageInternalsFinder {
if ( directory . isDirectory ( ) ) { // browse local .class files - useful for local execution
return processDir ( packageName , directory ) ;
} else { // browse a jar file
return processJar ( packageFolderURL ) ;
} // maybe there can be something else for more involved class loaders
return processJar ( packageName , packageFolderURL ) ;
}
}
private List < JavaFileObject > processJar ( String packageName , URL packageFolderURL ) {
try {
String jarUri = packageFolderURL . toExternalForm ( ) . substring ( 0 , packageFolderURL . toExternalForm ( ) . lastIndexOf ( "!/" ) ) ;
JarFileIndex jarFileIndex = INDEXS . get ( jarUri ) ;
if ( jarFileIndex = = null ) {
jarFileIndex = new JarFileIndex ( jarUri , URI . create ( jarUri + "!/" ) ) ;
INDEXS . put ( jarUri , jarFileIndex ) ;
}
List < JavaFileObject > result = jarFileIndex . search ( packageName ) ;
if ( result ! = null ) {
return result ;
}
} catch ( Exception e ) {
// ignore
}
// 保底
return fuse ( packageFolderURL ) ;
}
private List < JavaFileObject > processJar ( URL packageFolderURL ) {
private List < JavaFileObject > fuse ( URL packageFolderURL ) {
List < JavaFileObject > result = new ArrayList < JavaFileObject > ( ) ;
try {
String jarUri = packageFolderURL . toExternalForm ( ) . substring ( 0 , packageFolderURL . toExternalForm ( ) . lastIndexOf ( "!/" ) ) ;
@ -92,24 +119,16 @@ public class PackageInternalsFinder {
}
private List < JavaFileObject > processDir ( String packageName , File directory ) {
List < JavaFileObject > result = new ArrayList < JavaFileObject > ( ) ;
File [ ] childFiles = directory . listFiles ( ) ;
if ( childFiles ! = null ) {
for ( File childFile : childFiles ) {
if ( childFile . isFile ( ) ) {
// We only want the .class files.
if ( childFile . getName ( ) . endsWith ( CLASS_FILE_EXTENSION ) ) {
String binaryName = packageName + "." + childFile . getName ( ) ;
binaryName = binaryName . replaceAll ( CLASS_FILE_EXTENSION + "$" , "" ) ;
result . add ( new CustomJavaFileObject ( binaryName , childFile . toURI ( ) ) ) ;
}
}
}
File [ ] files = directory . listFiles ( item - >
item . isFile ( ) & & getKind ( item . getName ( ) ) = = JavaFileObject . Kind . CLASS ) ;
if ( files ! = null ) {
return Arrays . stream ( files ) . map ( item - > {
String className = packageName + "." + item . getName ( )
. replaceAll ( CLASS_FILE_EXTENSION + "$" , "" ) ;
return new CustomJavaFileObject ( className , item . toURI ( ) ) ;
} ) . collect ( Collectors . toList ( ) ) ;
}
return result ;
return Collections . emptyList ( ) ;
}
private String decode ( String filePath ) {
@ -121,4 +140,69 @@ public class PackageInternalsFinder {
return filePath ;
}
public static JavaFileObject . Kind getKind ( String name ) {
if ( name . endsWith ( JavaFileObject . Kind . CLASS . extension ) )
return JavaFileObject . Kind . CLASS ;
else if ( name . endsWith ( JavaFileObject . Kind . SOURCE . extension ) )
return JavaFileObject . Kind . SOURCE ;
else if ( name . endsWith ( JavaFileObject . Kind . HTML . extension ) )
return JavaFileObject . Kind . HTML ;
else
return JavaFileObject . Kind . OTHER ;
}
public static class JarFileIndex {
private String jarUri ;
private URI uri ;
private Map < String , List < ClassUriWrapper > > packages = new HashMap < > ( ) ;
public JarFileIndex ( String jarUri , URI uri ) throws IOException {
this . jarUri = jarUri ;
this . uri = uri ;
loadIndex ( ) ;
}
private void loadIndex ( ) throws IOException {
JarURLConnection jarConn = ( JarURLConnection ) uri . toURL ( ) . openConnection ( ) ;
String rootEntryName = jarConn . getEntryName ( ) = = null ? "" : jarConn . getEntryName ( ) ;
Enumeration < JarEntry > entryEnum = jarConn . getJarFile ( ) . entries ( ) ;
while ( entryEnum . hasMoreElements ( ) ) {
JarEntry jarEntry = entryEnum . nextElement ( ) ;
String entryName = jarEntry . getName ( ) ;
if ( entryName . startsWith ( rootEntryName ) & & entryName . endsWith ( CLASS_FILE_EXTENSION ) ) {
String className = entryName
. substring ( 0 , entryName . length ( ) - CLASS_FILE_EXTENSION . length ( ) )
. replace ( rootEntryName , "" )
. replace ( "/" , "." ) ;
if ( className . startsWith ( "." ) ) className = className . substring ( 1 ) ;
if ( className . equals ( "package-info" )
| | className . equals ( "module-info" )
| | className . lastIndexOf ( "." ) = = - 1 ) {
continue ;
}
String packageName = className . substring ( 0 , className . lastIndexOf ( "." ) ) ;
List < ClassUriWrapper > classes = packages . get ( packageName ) ;
if ( classes = = null ) {
classes = new ArrayList < > ( ) ;
packages . put ( packageName , classes ) ;
}
classes . add ( new ClassUriWrapper ( className , URI . create ( jarUri + "!/" + entryName ) ) ) ;
}
}
}
public List < JavaFileObject > search ( String packageName ) {
if ( this . packages . isEmpty ( ) ) {
return null ;
}
if ( this . packages . containsKey ( packageName ) ) {
return packages . get ( packageName ) . stream ( ) . map ( item - > {
return new CustomJavaFileObject ( item . getClassName ( ) , item . getUri ( ) ) ;
} ) . collect ( Collectors . toList ( ) ) ;
}
return Collections . emptyList ( ) ;
}
}
}