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 return switch (pElement) {
263 case ThemisClassInstance myInstance -> processClass(myInstance);
264 case ThemisTypeClassInterface myRef -> processClassReference(myRef);
265 case ThemisExprFieldAccess myAccess -> processFieldAccess(myAccess);
266 case ThemisExprMethodCall myCall -> processMethodCall(myCall);
267 case ThemisExprMethodRef myRef -> processMethodReference(myRef);
268 default -> true;
269 };
270 }
271
272
273
274
275
276
277
278 private boolean processClass(final ThemisClassInstance pInstance) {
279 final ThemisSolverClass myClass = theProject.getProjectClassMap().get(pInstance.getFullName());
280 theFile.processInherited(myClass);
281 return true;
282 }
283
284
285
286
287
288
289
290
291 private boolean processClassReference(final ThemisTypeClassInterface pReference) throws OceanusException {
292
293 final ThemisClassInstance myResolved = theFile.processPossibleReference(pReference.getFullName());
294
295
296 if (myResolved == null) {
297
298 final ThemisInstance myType = theType.lookUpType(pReference.getName());
299 final ThemisInstance myName = theName.lookUpName(pReference.getName());
300
301
302 final boolean bFound = (myType != null || myName != null);
303 if (!bFound) {
304 throw new ThemisDataException("Unresolved link: " + pReference.getFullName());
305 }
306 }
307
308
309 for (ThemisTypeInstance myParam : pReference.getTypeParams()) {
310 processInstance(myParam);
311 }
312 return false;
313 }
314
315
316
317
318
319
320
321
322 private boolean processFieldAccess(final ThemisExprFieldAccess pAccess) throws OceanusException {
323 final ThemisExpressionInstance myExpr = pAccess.getScope();
324 if (myExpr instanceof ThemisExprName myNameExpr) {
325
326 final String myFullName = pAccess.toString();
327 final String myNameRef = myNameExpr.toString();
328 ThemisClassInstance myResolved = theFile.processPossibleReference(myFullName);
329
330
331 if (myResolved == null) {
332 myResolved = theFile.processPossibleReference(myNameRef);
333 }
334
335
336 if (myResolved == null) {
337
338 final ThemisInstance myName = theName.lookUpName(myNameRef);
339
340
341 if (myName == null) {
342 throw new ThemisDataException("Unresolved fieldAccess: " + myNameRef);
343 }
344 }
345 }
346 return false;
347 }
348
349
350
351
352
353
354
355
356 private boolean processMethodCall(final ThemisExprMethodCall pCall) throws OceanusException {
357 final ThemisExpressionInstance myExpr = pCall.getScope();
358 if (myExpr instanceof ThemisExprName myNameExpr) {
359
360 final String myNameRef = myNameExpr.toString();
361 final ThemisClassInstance myResolved = theFile.processPossibleReference(myNameRef);
362
363
364 if (myResolved == null) {
365
366 final ThemisInstance myName = theName.lookUpName(myNameRef);
367
368
369 if (myName == null) {
370 throw new ThemisDataException("Unresolved method call using name: " + myNameRef);
371 }
372 }
373 } else if (myExpr instanceof ThemisExprFieldAccess myFieldExpr) {
374 processFieldAccess(myFieldExpr);
375 } else if (myExpr instanceof ThemisExprMethodCall myMethodCall) {
376 processMethodCall(myMethodCall);
377 }
378 return true;
379 }
380
381
382
383
384
385
386
387
388 private boolean processMethodReference(final ThemisExprMethodRef pReference) throws OceanusException {
389 final ThemisExpressionInstance myExpr = pReference.getScope();
390 if (myExpr instanceof ThemisExprName myNameExpr) {
391
392 final String myNameRef = myNameExpr.toString();
393 final ThemisClassInstance myResolved = theFile.processPossibleReference(myNameRef);
394
395
396 if (myResolved == null) {
397
398 final ThemisInstance myName = theName.lookUpName(myNameRef);
399
400
401 if (myName == null) {
402 throw new ThemisDataException("Unresolved methodRef: " + myNameRef);
403 }
404 }
405 }
406 return false;
407 }
408
409
410
411
412
413
414
415 private List<ThemisInstance> sortedChildren(final ThemisInstance pInstance) {
416 final List<ThemisInstance> myChildren = new ArrayList<>(pInstance.getChildren());
417 myChildren.sort(this::compareTo);
418 return myChildren;
419 }
420
421
422
423
424
425
426
427
428 private int compareTo(final ThemisInstance pFirst,
429 final ThemisInstance pSecond) {
430 if (pFirst instanceof ThemisClassInstance) {
431 return pSecond instanceof ThemisClassInstance ? 0 : 1;
432 }
433 return pSecond instanceof ThemisClassInstance ? -1 : 0;
434 }
435
436 @Override
437 public void close() {
438 theProject.close();
439 }
440 }