1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.themis.solver.reflect;
18
19 import com.github.javaparser.ast.body.BodyDeclaration;
20 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
21 import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
22 import io.github.tonywasher.joceanus.themis.parser.ThemisParser;
23 import io.github.tonywasher.joceanus.themis.parser.base.ThemisChar;
24 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisClassInstance;
25 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisTypeInstance;
26 import io.github.tonywasher.joceanus.themis.parser.proj.ThemisMavenId;
27 import io.github.tonywasher.joceanus.themis.parser.proj.ThemisProject;
28 import io.github.tonywasher.joceanus.themis.parser.type.ThemisTypeClassInterface;
29
30 import java.io.File;
31 import java.io.IOException;
32 import java.net.MalformedURLException;
33 import java.net.URI;
34 import java.net.URISyntaxException;
35 import java.net.URL;
36 import java.net.URLClassLoader;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Map;
40
41
42
43
44 public class ThemisReflectJar
45 implements AutoCloseable {
46
47
48
49 private static final String URLJAR_PREFIX = "jar:file:/";
50
51
52
53
54 private static final String URLJAR_SUFFIX = "!/";
55
56
57
58
59 private final ThemisParser theProjectParser;
60
61
62
63
64 private final URLClassLoader theClassLoader;
65
66
67
68
69 private final Map<String, ThemisReflectExternal> theExternalClasses;
70
71
72
73
74
75
76
77
78 public ThemisReflectJar(final ThemisParser pParser,
79 final Map<String, ThemisReflectExternal> pExternal) throws OceanusException {
80
81 theProjectParser = pParser;
82 theExternalClasses = pExternal;
83 final URL[] myUrls = determineURLList(pParser.getProject());
84 theClassLoader = URLClassLoader.newInstance(myUrls);
85 }
86
87
88
89
90
91
92 public void processExternalClasses() throws OceanusException {
93
94 final List<ThemisReflectExternal> myExternals = new ArrayList<>(theExternalClasses.values());
95
96
97 for (ThemisReflectExternal myClass : myExternals) {
98
99 final Class<?> myLoaded = loadClass(myClass.getFullName());
100
101
102 final BodyDeclaration<?> myResolved = buildClass(myLoaded);
103 final ThemisClassInstance myInstance = (ThemisClassInstance) theProjectParser.parseDeclaration(myResolved);
104 myClass.setClassInstance(myInstance);
105
106
107 processAncestors(myClass);
108
109
110 processChildren(myLoaded.getClasses());
111 }
112 }
113
114
115
116
117
118
119
120 public ThemisReflectExternal tryJavaLang(final String pName) {
121
122 final String myFullName = ThemisReflectExternal.JAVALANG + pName;
123 return tryNamedClass(myFullName);
124 }
125
126
127
128
129
130
131
132 public ThemisReflectExternal tryNamedClass(final String pName) {
133
134 try {
135
136 final ThemisReflectExternal myExternal = theExternalClasses.get(pName);
137 if (myExternal == null) {
138 return loadNamedClass(pName);
139 }
140 return myExternal;
141
142
143 } catch (OceanusException e) {
144 return null;
145 }
146 }
147
148
149
150
151
152
153
154
155 private ThemisReflectExternal loadNamedClass(final String pFullName) throws OceanusException {
156
157 final Class<?> myLoaded = loadClass(pFullName);
158
159
160 final BodyDeclaration<?> myResolved = buildClass(myLoaded);
161 final ThemisClassInstance myInstance = (ThemisClassInstance) theProjectParser.parseDeclaration(myResolved);
162 final ThemisReflectExternal myExternal = new ThemisReflectExternal(myInstance);
163
164
165 theExternalClasses.put(pFullName, myExternal);
166
167
168 processAncestors(myExternal);
169
170
171 processChildren(myLoaded.getClasses());
172
173
174 return myExternal;
175 }
176
177
178
179
180
181
182
183 private void processAncestors(final ThemisReflectExternal pExternal) throws OceanusException {
184
185 final ThemisClassInstance myInstance = pExternal.getClassInstance();
186
187
188 for (ThemisTypeInstance myAncestor : myInstance.getExtends()) {
189
190 pExternal.addAncestor(processAncestor((ThemisTypeClassInterface) myAncestor));
191 }
192
193
194 for (ThemisTypeInstance myAncestor : myInstance.getImplements()) {
195
196 pExternal.addAncestor(processAncestor((ThemisTypeClassInterface) myAncestor));
197 }
198 }
199
200
201
202
203
204
205
206
207 private ThemisReflectExternal processAncestor(final ThemisTypeClassInterface pAncestor) throws OceanusException {
208
209 final String myFullName = pAncestor.getFullName().replace(ThemisChar.DOLLAR, ThemisChar.PERIOD);
210
211
212 ThemisReflectExternal myExternal = theExternalClasses.get(myFullName);
213 if (myExternal == null) {
214
215 myExternal = loadNamedClass(myFullName);
216 }
217
218
219 pAncestor.setClassInstance(myExternal);
220 return myExternal;
221 }
222
223
224
225
226
227
228
229 private void processChildren(final Class<?>[] pChildren) throws OceanusException {
230
231 for (Class<?> myChild : pChildren) {
232
233 final boolean isLocalAnon = myChild.isAnonymousClass() || myChild.isLocalClass();
234 final boolean isPrivate = ThemisReflectBaseUtils.isPrivate(myChild.getModifiers());
235 if (!isPrivate && !isLocalAnon) {
236 processChild(myChild);
237 }
238 }
239 }
240
241
242
243
244
245
246
247 private void processChild(final Class<?> pChild) throws OceanusException {
248
249 final BodyDeclaration<?> myResolved = buildClass(pChild);
250 final ThemisClassInstance myInstance = (ThemisClassInstance) theProjectParser.parseDeclaration(myResolved);
251 final ThemisReflectExternal myExternal = new ThemisReflectExternal(myInstance);
252
253
254 theExternalClasses.put(myExternal.getFullName(), myExternal);
255
256
257 processAncestors(myExternal);
258 }
259
260
261
262
263
264
265
266
267 private URL[] determineURLList(final ThemisProject pProject) throws OceanusException {
268
269 final List<URL> myList = new ArrayList<>();
270 for (ThemisMavenId myId : pProject.getDependencies()) {
271
272 try {
273 final File myJar = myId.getMavenJarPath();
274 final String myName = myJar.toString().replace("\\", "/");
275 final String myPrefix = myName.startsWith("/") ? URLJAR_PREFIX + ThemisChar.COMMENT : URLJAR_PREFIX;
276 final URL myUrl = (new URI(myPrefix + myName + URLJAR_SUFFIX)).toURL();
277 myList.add(myUrl);
278
279
280 } catch (URISyntaxException
281 | MalformedURLException e) {
282 throw new ThemisDataException("Failed to build URL", e);
283 }
284 }
285
286
287 return myList.toArray(new URL[0]);
288 }
289
290
291
292
293
294
295
296
297 private Class<?> loadClass(final String pClassName) throws OceanusException {
298
299 try {
300 return theClassLoader.loadClass(pClassName);
301
302
303 } catch (ClassNotFoundException e) {
304
305 final String mySubClass = trySubClass(pClassName);
306 if (mySubClass != null) {
307 return loadClass(mySubClass);
308 }
309
310
311 throw new ThemisDataException("Failed to find class " + pClassName, e);
312 }
313 }
314
315
316
317
318
319
320
321 private static String trySubClass(final String pClassName) {
322
323 final int myLastIndex = pClassName.lastIndexOf(ThemisChar.PERIOD);
324 return myLastIndex != -1
325 ? pClassName.substring(0, myLastIndex) + ThemisChar.DOLLAR + pClassName.substring(myLastIndex + 1)
326 : null;
327 }
328
329
330
331
332
333
334
335
336 private BodyDeclaration<?> buildClass(final Class<?> pSource) throws OceanusException {
337
338 if (pSource.isAnnotation()) {
339 return new ThemisReflectAnnotation(pSource);
340 } else if (pSource.isEnum()) {
341 return new ThemisReflectEnum(pSource);
342 } else if (pSource.isRecord()) {
343 return new ThemisReflectRecord(pSource);
344 } else {
345 return new ThemisReflectClass(pSource);
346 }
347 }
348
349 @Override
350 public void close() {
351 try {
352 if (theClassLoader != null) {
353 theClassLoader.close();
354 }
355 } catch (IOException e) {
356
357 }
358 }
359 }