28
28
import java .net .URL ;
29
29
import java .net .URLDecoder ;
30
30
import java .util .ArrayList ;
31
+ import java .util .Arrays ;
31
32
import java .util .Collection ;
33
+ import java .util .Collections ;
32
34
import java .util .Enumeration ;
35
+ import java .util .HashMap ;
33
36
import java .util .List ;
37
+ import java .util .Map ;
38
+ import java .util .concurrent .ConcurrentHashMap ;
34
39
import java .util .jar .JarEntry ;
40
+ import java .util .stream .Collectors ;
35
41
36
42
public class PackageInternalsFinder {
37
43
private final ClassLoader classLoader ;
38
44
private static final String CLASS_FILE_EXTENSION = ".class" ;
39
45
46
+ private static final Map <String , JarFileIndex > INDEXS = new ConcurrentHashMap <>();
47
+
40
48
public PackageInternalsFinder (ClassLoader classLoader ) {
41
49
this .classLoader = classLoader ;
42
50
}
@@ -60,11 +68,30 @@ private Collection<JavaFileObject> listUnder(String packageName, URL packageFold
60
68
if (directory .isDirectory ()) { // browse local .class files - useful for local execution
61
69
return processDir (packageName , directory );
62
70
} else { // browse a jar file
63
- return processJar (packageFolderURL );
64
- } // maybe there can be something else for more involved class loaders
71
+ return processJar (packageName , packageFolderURL );
72
+ }
73
+ }
74
+
75
+ private List <JavaFileObject > processJar (String packageName , URL packageFolderURL ) {
76
+ try {
77
+ String jarUri = packageFolderURL .toExternalForm ().substring (0 , packageFolderURL .toExternalForm ().lastIndexOf ("!/" ));
78
+ JarFileIndex jarFileIndex = INDEXS .get (jarUri );
79
+ if (jarFileIndex == null ) {
80
+ jarFileIndex = new JarFileIndex (jarUri , URI .create (jarUri + "!/" ));
81
+ INDEXS .put (jarUri , jarFileIndex );
82
+ }
83
+ List <JavaFileObject > result = jarFileIndex .search (packageName );
84
+ if (result != null ) {
85
+ return result ;
86
+ }
87
+ } catch (Exception e ) {
88
+ // ignore
89
+ }
90
+ // 保底
91
+ return fuse (packageFolderURL );
65
92
}
66
93
67
- private List <JavaFileObject > processJar (URL packageFolderURL ) {
94
+ private List <JavaFileObject > fuse (URL packageFolderURL ) {
68
95
List <JavaFileObject > result = new ArrayList <JavaFileObject >();
69
96
try {
70
97
String jarUri = packageFolderURL .toExternalForm ().substring (0 , packageFolderURL .toExternalForm ().lastIndexOf ("!/" ));
@@ -92,24 +119,16 @@ private List<JavaFileObject> processJar(URL packageFolderURL) {
92
119
}
93
120
94
121
private List <JavaFileObject > processDir (String packageName , File directory ) {
95
- List <JavaFileObject > result = new ArrayList <JavaFileObject >();
96
-
97
- File [] childFiles = directory .listFiles ();
98
- if (childFiles != null ) {
99
- for (File childFile : childFiles ) {
100
- if (childFile .isFile ()) {
101
- // We only want the .class files.
102
- if (childFile .getName ().endsWith (CLASS_FILE_EXTENSION )) {
103
- String binaryName = packageName + "." + childFile .getName ();
104
- binaryName = binaryName .replaceAll (CLASS_FILE_EXTENSION + "$" , "" );
105
-
106
- result .add (new CustomJavaFileObject (binaryName , childFile .toURI ()));
107
- }
108
- }
109
- }
122
+ File [] files = directory .listFiles (item ->
123
+ item .isFile () && getKind (item .getName ()) == JavaFileObject .Kind .CLASS );
124
+ if (files != null ) {
125
+ return Arrays .stream (files ).map (item -> {
126
+ String className = packageName + "." + item .getName ()
127
+ .replaceAll (CLASS_FILE_EXTENSION + "$" , "" );
128
+ return new CustomJavaFileObject (className , item .toURI ());
129
+ }).collect (Collectors .toList ());
110
130
}
111
-
112
- return result ;
131
+ return Collections .emptyList ();
113
132
}
114
133
115
134
private String decode (String filePath ) {
@@ -121,4 +140,69 @@ private String decode(String filePath) {
121
140
122
141
return filePath ;
123
142
}
143
+
144
+ public static JavaFileObject .Kind getKind (String name ) {
145
+ if (name .endsWith (JavaFileObject .Kind .CLASS .extension ))
146
+ return JavaFileObject .Kind .CLASS ;
147
+ else if (name .endsWith (JavaFileObject .Kind .SOURCE .extension ))
148
+ return JavaFileObject .Kind .SOURCE ;
149
+ else if (name .endsWith (JavaFileObject .Kind .HTML .extension ))
150
+ return JavaFileObject .Kind .HTML ;
151
+ else
152
+ return JavaFileObject .Kind .OTHER ;
153
+ }
154
+
155
+ public static class JarFileIndex {
156
+ private String jarUri ;
157
+ private URI uri ;
158
+
159
+ private Map <String , List <ClassUriWrapper >> packages = new HashMap <>();
160
+
161
+ public JarFileIndex (String jarUri , URI uri ) throws IOException {
162
+ this .jarUri = jarUri ;
163
+ this .uri = uri ;
164
+ loadIndex ();
165
+ }
166
+
167
+ private void loadIndex () throws IOException {
168
+ JarURLConnection jarConn = (JarURLConnection ) uri .toURL ().openConnection ();
169
+ String rootEntryName = jarConn .getEntryName () == null ? "" : jarConn .getEntryName ();
170
+ Enumeration <JarEntry > entryEnum = jarConn .getJarFile ().entries ();
171
+ while (entryEnum .hasMoreElements ()) {
172
+ JarEntry jarEntry = entryEnum .nextElement ();
173
+ String entryName = jarEntry .getName ();
174
+ if (entryName .startsWith (rootEntryName ) && entryName .endsWith (CLASS_FILE_EXTENSION )) {
175
+ String className = entryName
176
+ .substring (0 , entryName .length () - CLASS_FILE_EXTENSION .length ())
177
+ .replace (rootEntryName , "" )
178
+ .replace ("/" , "." );
179
+ if (className .startsWith ("." )) className = className .substring (1 );
180
+ if (className .equals ("package-info" )
181
+ || className .equals ("module-info" )
182
+ || className .lastIndexOf ("." ) == -1 ) {
183
+ continue ;
184
+ }
185
+ String packageName = className .substring (0 , className .lastIndexOf ("." ));
186
+ List <ClassUriWrapper > classes = packages .get (packageName );
187
+ if (classes == null ) {
188
+ classes = new ArrayList <>();
189
+ packages .put (packageName , classes );
190
+ }
191
+ classes .add (new ClassUriWrapper (className , URI .create (jarUri + "!/" + entryName )));
192
+ }
193
+ }
194
+ }
195
+
196
+ public List <JavaFileObject > search (String packageName ) {
197
+ if (this .packages .isEmpty ()) {
198
+ return null ;
199
+ }
200
+ if (this .packages .containsKey (packageName )) {
201
+ return packages .get (packageName ).stream ().map (item -> {
202
+ return new CustomJavaFileObject (item .getClassName (), item .getUri ());
203
+ }).collect (Collectors .toList ());
204
+ }
205
+ return Collections .emptyList ();
206
+ }
207
+ }
124
208
}
0 commit comments