1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package io.github.tonywasher.joceanus.themis.solver.mapper;
19
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.base.ThemisInstance;
23 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisClassInstance;
24 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisExpressionInstance;
25 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisNodeInstance;
26 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisTypeInstance;
27 import io.github.tonywasher.joceanus.themis.parser.expr.ThemisExprFieldAccess;
28 import io.github.tonywasher.joceanus.themis.parser.expr.ThemisExprMethodCall;
29 import io.github.tonywasher.joceanus.themis.parser.expr.ThemisExprMethodRef;
30 import io.github.tonywasher.joceanus.themis.parser.expr.ThemisExprName;
31 import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeImport;
32 import io.github.tonywasher.joceanus.themis.parser.type.ThemisTypeClassInterface;
33 import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverClass;
34 import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverFile;
35 import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverPackage;
36 import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverProject;
37
38 import java.util.ArrayList;
39 import java.util.List;
40
41
42
43
44 public class ThemisMapper
45 implements AutoCloseable {
46
47
48
49 private final ThemisMapperProjectState theProject;
50
51
52
53
54 private final ThemisMapperFileState theFile;
55
56
57
58
59 private final ThemisMapperTypeState theType;
60
61
62
63
64 private final ThemisMapperNameState theName;
65
66
67
68
69
70
71
72 public ThemisMapper(final ThemisSolverProject pProject) throws OceanusException {
73
74 theProject = new ThemisMapperProjectState(pProject);
75
76
77 theFile = new ThemisMapperFileState(theProject);
78 theType = new ThemisMapperTypeState();
79 theName = new ThemisMapperNameState();
80 }
81
82
83
84
85
86
87 public void preProcessPackage(final ThemisSolverPackage pPackage) {
88
89 for (ThemisSolverFile myFile : pPackage.getFiles()) {
90
91 if (myFile.needsPreProcess()) {
92 preProcessFile(myFile);
93 }
94 }
95 }
96
97
98
99
100
101
102
103 public void processPackage(final ThemisSolverPackage pPackage) throws OceanusException {
104
105 for (ThemisSolverFile myFile : pPackage.getFiles()) {
106
107 processFile(myFile);
108 }
109
110
111 for (ThemisSolverFile myFile : pPackage.getFiles()) {
112
113 myFile.processLocalReferences();
114 }
115 }
116
117
118
119
120
121
122 private void resetFileState(final ThemisSolverFile pFile) {
123
124 theFile.initForFile(pFile);
125 theType.reset();
126 theName.reset();
127 }
128
129
130
131
132
133
134 private void preProcessFile(final ThemisSolverFile pFile) {
135
136 pFile.markPreProcessed();
137
138
139 preProcessImports(pFile);
140
141
142 resetFileState(pFile);
143
144
145 for (ThemisSolverClass myClass : pFile.getClasses()) {
146 final ThemisClassInstance myInstance = myClass.getUnderlyingClass();
147
148
149 for (ThemisTypeInstance myExtends : myInstance.getExtends()) {
150 processAncestor(myClass, myExtends);
151 }
152
153
154 for (ThemisTypeInstance myImplements : myInstance.getImplements()) {
155 processAncestor(myClass, myImplements);
156 }
157
158
159 theFile.processInherited(myClass);
160 }
161 }
162
163
164
165
166
167
168
169 private void preProcessImports(final ThemisSolverFile pFile) {
170
171 for (ThemisNodeInstance myNode : pFile.getUnderlyingFile().getContents().getImports()) {
172
173 final ThemisNodeImport myImport = (ThemisNodeImport) myNode;
174 final ThemisSolverClass myClass = theProject.getProjectClassMap().get(myImport.getFullName());
175 if (myClass != null) {
176
177 final ThemisSolverFile myFile = (ThemisSolverFile) myClass.getOwningFile();
178 if (myFile.needsPreProcess()) {
179
180 preProcessFile(myFile);
181 }
182 }
183 }
184 }
185
186
187
188
189
190
191
192 private void processAncestor(final ThemisSolverClass pClass,
193 final ThemisTypeInstance pAncestor) {
194
195 if (pAncestor instanceof ThemisTypeClassInterface myRef) {
196 final ThemisClassInstance myResolved = theFile.processPossibleReference(myRef.getFullName());
197 if (myResolved != null) {
198 pClass.addAncestor(myResolved.getFullName());
199 myRef.setClassInstance(myResolved);
200 }
201 }
202 }
203
204
205
206
207
208
209
210 private void processFile(final ThemisSolverFile pFile) throws OceanusException {
211
212 resetFileState(pFile);
213
214
215 final ThemisSolverClass myBase = pFile.getTopLevel();
216 final ThemisInstance myInstance = (ThemisInstance) myBase.getUnderlyingClass();
217 processInstance(myInstance);
218
219
220 pFile.setReferenced(theFile.getReferenced());
221 }
222
223
224
225
226
227
228
229 private void processInstance(final ThemisInstance pInstance) throws OceanusException {
230
231 final boolean bumpType = theType.processInstance(pInstance);
232 final boolean bumpName = theName.processInstance(pInstance);
233
234
235 final boolean doChildren = processElement(pInstance);
236
237
238 if (doChildren) {
239 for (ThemisInstance myChild : sortedChildren(pInstance)) {
240 processInstance(myChild);
241 }
242 }
243
244
245 if (bumpName) {
246 theName.cleanUpAfterInstance();
247 }
248 if (bumpType) {
249 theType.cleanUpAfterInstance();
250 }
251 }
252
253
254
255
256
257
258
259
260 private boolean processElement(final ThemisInstance pElement) throws OceanusException {
261
262 if (pElement instanceof ThemisClassInstance myInstance) {
263 final ThemisSolverClass myClass = theProject.getProjectClassMap().get(myInstance.getFullName());
264 theFile.processInherited(myClass);
265 }
266
267
268 if (pElement instanceof ThemisTypeClassInterface myRef) {
269 processClassReference(myRef);
270 return false;
271 }
272
273
274 if (pElement instanceof ThemisExprFieldAccess myAccess) {
275 processFieldAccess(myAccess);
276 return false;
277 }
278
279
280 if (pElement instanceof ThemisExprMethodCall myCall) {
281 processMethodCall(myCall);
282 return false;
283 }
284
285
286 if (pElement instanceof ThemisExprMethodRef myRef) {
287 processMethodReference(myRef);
288 return false;
289 }
290 return true;
291 }
292
293
294
295
296
297
298
299 private void processClassReference(final ThemisTypeClassInterface pReference) throws OceanusException {
300
301 final ThemisClassInstance myResolved = theFile.processPossibleReference(pReference.getFullName());
302
303
304 if (myResolved == null) {
305
306 final ThemisInstance myType = theType.lookUpType(pReference.getName());
307 final ThemisInstance myName = theName.lookUpName(pReference.getName());
308
309
310 final boolean bFound = (myType != null || myName != null);
311 if (!bFound) {
312 throw new ThemisDataException("Unresolved link: " + pReference.getFullName());
313 }
314 }
315 }
316
317
318
319
320
321
322
323 private void processFieldAccess(final ThemisExprFieldAccess pAccess) throws OceanusException {
324 final ThemisExpressionInstance myExpr = pAccess.getScope();
325 if (myExpr instanceof ThemisExprName myNameExpr) {
326
327 final String myFullName = pAccess.toString();
328 final String myNameRef = myNameExpr.toString();
329 ThemisClassInstance myResolved = theFile.processPossibleReference(myFullName);
330
331
332 if (myResolved == null) {
333 myResolved = theFile.processPossibleReference(myNameRef);
334 }
335
336
337 if (myResolved == null) {
338
339 final ThemisInstance myName = theName.lookUpName(myNameRef);
340
341
342 if (myName == null) {
343 throw new ThemisDataException("Unresolved fieldAccess: " + myNameRef);
344 }
345 }
346 }
347 }
348
349
350
351
352
353
354
355 private void processMethodCall(final ThemisExprMethodCall pCall) throws OceanusException {
356 final ThemisExpressionInstance myExpr = pCall.getScope();
357 if (myExpr instanceof ThemisExprName myNameExpr) {
358
359 final String myNameRef = myNameExpr.toString();
360 final ThemisClassInstance myResolved = theFile.processPossibleReference(myNameRef);
361
362
363 if (myResolved == null) {
364
365 final ThemisInstance myName = theName.lookUpName(myNameRef);
366
367
368 if (myName == null) {
369 throw new ThemisDataException("Unresolved method call using name: " + myNameRef);
370 }
371 }
372 } else if (myExpr instanceof ThemisExprFieldAccess myFieldExpr) {
373 processFieldAccess(myFieldExpr);
374 } else if (myExpr instanceof ThemisExprMethodCall myMethodCall) {
375 processMethodCall(myMethodCall);
376 }
377 }
378
379
380
381
382
383
384
385 private void processMethodReference(final ThemisExprMethodRef pReference) throws OceanusException {
386 final ThemisExpressionInstance myExpr = pReference.getScope();
387 if (myExpr instanceof ThemisExprName myNameExpr) {
388
389 final String myNameRef = myNameExpr.toString();
390 final ThemisClassInstance myResolved = theFile.processPossibleReference(myNameRef);
391
392
393 if (myResolved == null) {
394
395 final ThemisInstance myName = theName.lookUpName(myNameRef);
396
397
398 if (myName == null) {
399 throw new ThemisDataException("Unresolved methodRef: " + myNameRef);
400 }
401 }
402 }
403 }
404
405
406
407
408
409
410
411 private List<ThemisInstance> sortedChildren(final ThemisInstance pInstance) {
412 final List<ThemisInstance> myChildren = new ArrayList<>(pInstance.getChildren());
413 myChildren.sort(this::compareTo);
414 return myChildren;
415 }
416
417
418
419
420
421
422
423
424 private int compareTo(final ThemisInstance pFirst,
425 final ThemisInstance pSecond) {
426 if (pFirst instanceof ThemisClassInstance) {
427 return pSecond instanceof ThemisClassInstance ? 0 : 1;
428 }
429 return pSecond instanceof ThemisClassInstance ? -1 : 0;
430 }
431
432 @Override
433 public void close() {
434 theProject.close();
435 }
436 }