1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.themis.xanalysis.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.xanalysis.parser.ThemisXAnalysisParser;
23 import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisChar;
24 import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisInstance.ThemisXAnalysisClassInstance;
25 import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisInstance.ThemisXAnalysisTypeInstance;
26 import io.github.tonywasher.joceanus.themis.xanalysis.parser.proj.ThemisXAnalysisMaven.ThemisXAnalysisMavenId;
27 import io.github.tonywasher.joceanus.themis.xanalysis.parser.proj.ThemisXAnalysisProject;
28 import io.github.tonywasher.joceanus.themis.xanalysis.parser.type.ThemisXAnalysisTypeClassInterface;
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 ThemisXAnalysisReflectJar
45 implements AutoCloseable {
46
47
48
49 private final ThemisXAnalysisParser theProjectParser;
50
51
52
53
54 private final URLClassLoader theClassLoader;
55
56
57
58
59 private Map<String, ThemisXAnalysisReflectExternal> theExternalClasses;
60
61
62
63
64
65
66
67 public ThemisXAnalysisReflectJar(final ThemisXAnalysisParser pParser) throws OceanusException {
68
69 theProjectParser = pParser;
70 final URL[] myUrls = determineURLList(pParser.getProject());
71 theClassLoader = URLClassLoader.newInstance(myUrls);
72 }
73
74
75
76
77
78
79
80 public void processExternalClasses(final Map<String, ThemisXAnalysisReflectExternal> pExternalClasses) throws OceanusException {
81
82 theExternalClasses = pExternalClasses;
83 final List<ThemisXAnalysisReflectExternal> myExternals = new ArrayList<>(pExternalClasses.values());
84
85
86 for (ThemisXAnalysisReflectExternal myClass : myExternals) {
87
88 final Class<?> myLoaded = loadClass(myClass.getFullName());
89
90
91 final BodyDeclaration<?> myResolved = buildClass(myLoaded);
92 final ThemisXAnalysisClassInstance myInstance = (ThemisXAnalysisClassInstance) theProjectParser.parseDeclaration(myResolved);
93 myClass.setClassInstance(myInstance);
94
95
96 processAncestors(myInstance);
97 }
98 }
99
100
101
102
103
104
105
106 private void processAncestors(final ThemisXAnalysisClassInstance pExternal) throws OceanusException {
107
108 for (ThemisXAnalysisTypeInstance myAncestor : pExternal.getExtends()) {
109
110 processAncestor((ThemisXAnalysisTypeClassInterface) myAncestor);
111 }
112
113
114 for (ThemisXAnalysisTypeInstance myAncestor : pExternal.getImplements()) {
115
116 processAncestor((ThemisXAnalysisTypeClassInterface) myAncestor);
117 }
118 }
119
120
121
122
123
124
125
126 private void processAncestor(final ThemisXAnalysisTypeClassInterface pAncestor) throws OceanusException {
127
128 final String myFullName = pAncestor.getFullName().replace(ThemisXAnalysisChar.DOLLAR, ThemisXAnalysisChar.PERIOD);
129
130
131 ThemisXAnalysisReflectExternal myExternal = theExternalClasses.get(myFullName);
132 if (myExternal == null) {
133
134 final Class<?> myLoaded = loadClass(myFullName);
135
136
137 final BodyDeclaration<?> myResolved = buildClass(myLoaded);
138 final ThemisXAnalysisClassInstance myInstance = (ThemisXAnalysisClassInstance) theProjectParser.parseDeclaration(myResolved);
139 myExternal = new ThemisXAnalysisReflectExternal(myInstance);
140 theExternalClasses.put(myFullName, myExternal);
141
142
143 processAncestors(myInstance);
144
145
146 } else {
147
148 pAncestor.setClassInstance(myExternal);
149 }
150 }
151
152
153
154
155
156
157
158
159 private URL[] determineURLList(final ThemisXAnalysisProject pProject) throws OceanusException {
160
161 final List<URL> myList = new ArrayList<>();
162 for (ThemisXAnalysisMavenId myId : pProject.getDependencies()) {
163
164 try {
165 final File myJar = myId.getMavenJarPath();
166 final URL myUrl = (new URI("jar:file:/" + myJar + "!/")).toURL();
167 myList.add(myUrl);
168
169
170 } catch (URISyntaxException
171 | MalformedURLException e) {
172 throw new ThemisDataException("Failed to build URL", e);
173 }
174 }
175
176
177 return myList.toArray(new URL[0]);
178 }
179
180
181
182
183
184
185
186
187 private Class<?> loadClass(final String pClassName) throws OceanusException {
188
189 try {
190 return theClassLoader.loadClass(pClassName);
191
192
193 } catch (ClassNotFoundException e) {
194
195 final String mySubClass = trySubClass(pClassName);
196 if (mySubClass != null) {
197 return loadClass(mySubClass);
198 }
199
200
201 throw new ThemisDataException("Failed to find class " + pClassName, e);
202 }
203 }
204
205
206
207
208
209
210
211 private static String trySubClass(final String pClassName) {
212
213 final int myLastIndex = pClassName.lastIndexOf(ThemisXAnalysisChar.PERIOD);
214 return myLastIndex != -1
215 ? pClassName.substring(0, myLastIndex) + ThemisXAnalysisChar.DOLLAR + pClassName.substring(myLastIndex + 1)
216 : null;
217 }
218
219
220
221
222
223
224
225
226 private BodyDeclaration<?> buildClass(final Class<?> pSource) throws OceanusException {
227
228 if (pSource.isAnnotation()) {
229 return new ThemisXAnalysisReflectAnnotation(pSource);
230 } else if (pSource.isEnum()) {
231 return new ThemisXAnalysisReflectEnum(pSource);
232 } else if (pSource.isRecord()) {
233 return new ThemisXAnalysisReflectRecord(pSource);
234 } else {
235 return new ThemisXAnalysisReflectClass(pSource);
236 }
237 }
238
239 @Override
240 public void close() {
241 try {
242 if (theClassLoader != null) {
243 theClassLoader.close();
244 }
245 } catch (IOException e) {
246
247 }
248 }
249 }