1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.themis.lethe.analysis;
18
19 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20 import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogManager;
21 import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogger;
22 import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
23 import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisFile.ThemisAnalysisObject;
24 import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisGeneric.ThemisAnalysisGenericVar;
25 import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisImports.ThemisAnalysisImport;
26
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Map.Entry;
33
34
35
36
37 public class ThemisAnalysisDataMap {
38
39
40
41 interface ThemisAnalysisDataType {
42 }
43
44
45
46
47 interface ThemisAnalysisIntermediate extends ThemisAnalysisDataType {
48 }
49
50
51
52
53 private static final OceanusLogger LOGGER = OceanusLogManager.getLogger(ThemisAnalysisDataMap.class);
54
55
56
57
58 private static final Map<String, ThemisAnalysisDataType> BASETYPES = createDataTypeMap();
59
60
61
62
63 private static final Map<String, String> HIDDENTYPES = createHiddenTypeMap();
64
65
66
67
68 private final Map<String, ThemisAnalysisDataType> theLocalTypes;
69
70
71
72
73 private final Map<String, ThemisAnalysisObject> theClassMap;
74
75
76
77
78 private final Map<String, ThemisAnalysisObject> theShortClassMap;
79
80
81
82
83 private final Map<String, Integer> theLocalIdMap;
84
85
86
87
88 private Map<String, ThemisAnalysisDataType> theFileTypes;
89
90
91
92
93 private List<ThemisAnalysisObject> theFileClasses;
94
95
96
97
98 private List<ThemisAnalysisReference> theReferences;
99
100
101
102
103 private ThemisAnalysisDataMap theParent;
104
105
106
107
108 ThemisAnalysisDataMap() {
109 theParent = null;
110 theClassMap = new LinkedHashMap<>();
111 theShortClassMap = new LinkedHashMap<>();
112 theLocalTypes = new HashMap<>();
113 theLocalIdMap = new HashMap<>();
114 }
115
116
117
118
119
120
121 ThemisAnalysisDataMap(final ThemisAnalysisDataMap pParent) {
122 theParent = pParent;
123 theClassMap = pParent.theClassMap;
124 theShortClassMap = pParent.theShortClassMap;
125 theLocalTypes = new HashMap<>();
126 theFileTypes = pParent.theFileTypes;
127 theFileClasses = pParent.theFileClasses;
128 theReferences = pParent.theReferences;
129 theLocalIdMap = new HashMap<>();
130 }
131
132
133
134
135
136
137
138
139 ThemisAnalysisDataType lookUpDataType(final String pToken,
140 final boolean pMethod) {
141
142 ThemisAnalysisDataType myDataType = lookUpTheDataType(pToken);
143
144
145 if (myDataType == null && !pMethod) {
146
147 final int myIndex = pToken.indexOf(ThemisAnalysisChar.PERIOD);
148 if (myIndex != -1) {
149
150 final String myStart = pToken.substring(0, myIndex);
151 final String myEnd = pToken.substring(myIndex + 1);
152 final ThemisAnalysisDataType myParent = lookUpTheDataType(myStart);
153
154
155 if (myParent != null) {
156 myDataType = new ThemisAnalysisDataTypeChild(myParent, myEnd);
157 theFileTypes.put(pToken, myDataType);
158 }
159 }
160 }
161
162
163 return myDataType;
164 }
165
166
167
168
169
170
171
172 ThemisAnalysisDataType lookUpTheDataType(final String pToken) {
173
174 final ThemisAnalysisDataType myDataType = theLocalTypes.get(pToken);
175 if (myDataType != null) {
176 return myDataType;
177 }
178
179
180 return theParent == null
181 ? BASETYPES.get(pToken)
182 : theParent.lookUpTheDataType(pToken);
183 }
184
185
186
187
188
189
190
191 void declareObject(final ThemisAnalysisObject pObject) throws OceanusException {
192
193 final ThemisAnalysisProperties myProps = pObject.getProperties();
194 final String myShortName = pObject.getShortName();
195
196
197 if (!myProps.hasModifier(ThemisAnalysisModifier.PRIVATE)) {
198
199 if (theShortClassMap.get(myShortName) != null) {
200 throw new ThemisDataException("Duplicate class shortName: " + myShortName);
201 }
202
203
204 theShortClassMap.put(myShortName, pObject);
205 theClassMap.put(pObject.getFullName(), pObject);
206 }
207
208
209 theFileTypes.put(myShortName, pObject);
210 theFileClasses.add(pObject);
211 }
212
213
214
215
216 void setUpFileResources() {
217
218 theFileTypes = theLocalTypes;
219
220
221 theFileClasses = new ArrayList<>();
222
223
224 theReferences = new ArrayList<>();
225 }
226
227
228
229
230
231
232 void setParent(final ThemisAnalysisDataMap pParent) {
233 theParent = pParent;
234 }
235
236
237
238
239
240
241 void declareFile(final ThemisAnalysisFile pFile) {
242 theLocalTypes.put(pFile.getName(), pFile);
243 }
244
245
246
247
248
249
250 void declareImport(final ThemisAnalysisImport pImport) {
251 theLocalTypes.put(pImport.getSimpleName(), pImport);
252 }
253
254
255
256
257
258
259 void declareGenericVar(final ThemisAnalysisGenericVar pVar) {
260 theLocalTypes.put(pVar.getName(), pVar);
261 }
262
263
264
265
266
267
268
269 ThemisAnalysisDataType declareUnknown(final String pName) {
270 final ThemisAnalysisDataType myType = new ThemisAnalysisDataTypeUnknown(pName);
271 theFileTypes.put(pName, myType);
272 return myType;
273 }
274
275
276
277
278
279
280 void declareReference(final ThemisAnalysisReference pRef) {
281 theReferences.add(pRef);
282 }
283
284
285
286
287
288
289 void consolidateMap() throws OceanusException {
290
291 updateIntermediates();
292
293
294 resolveImmediateAncestors();
295 }
296
297
298
299
300 private void updateIntermediates() {
301
302 for (Entry<String, ThemisAnalysisDataType> myEntry : theLocalTypes.entrySet()) {
303
304 final ThemisAnalysisDataType myType = myEntry.getValue();
305
306
307 if (myType instanceof ThemisAnalysisIntermediate myIntermediate) {
308
309 final ThemisAnalysisDataType myActual = lookUpActualDataType(myIntermediate);
310 if (myActual != null) {
311 myEntry.setValue(myActual);
312 }
313 }
314 }
315
316
317 for (ThemisAnalysisReference myRef : theReferences) {
318
319 final ThemisAnalysisDataType myType = myRef.getDataType();
320
321
322 if (myType instanceof ThemisAnalysisIntermediate myIntermediate) {
323
324 final ThemisAnalysisDataType myActual = lookUpActualDataType(myIntermediate);
325 if (myActual != null) {
326 myRef.updateDataType(myActual);
327 }
328 }
329 }
330 }
331
332
333
334
335
336
337
338 ThemisAnalysisObject lookUpActualDataType(final ThemisAnalysisIntermediate pIntermediate) {
339
340 if (pIntermediate instanceof ThemisAnalysisImport myImport) {
341
342 return theClassMap.get(myImport.getFullName());
343 }
344
345
346 if (pIntermediate instanceof ThemisAnalysisFile myFile) {
347
348 final String myFullName = myFile.getPackageName() + ThemisAnalysisChar.PERIOD + myFile.getName();
349 return theClassMap.get(myFullName);
350 }
351
352
353 return null;
354 }
355
356
357
358
359
360
361 private void resolveImmediateAncestors() throws OceanusException {
362
363 for (ThemisAnalysisObject myClass : theFileClasses) {
364
365 for (ThemisAnalysisReference myRef : myClass.getAncestors()) {
366
367 resolveAncestor(myRef);
368 }
369 }
370 }
371
372
373
374
375
376
377
378 private void resolveAncestor(final ThemisAnalysisReference pAncestor) throws OceanusException {
379
380 final ThemisAnalysisDataType myDataType = pAncestor.getDataType();
381 if (!(myDataType instanceof ThemisAnalysisDataTypeUnknown)) {
382 return;
383 }
384
385
386 final String myName = ((ThemisAnalysisDataTypeUnknown) myDataType).theName;
387 final ThemisAnalysisObject myActual = theShortClassMap.get(myName);
388 if (myActual == null) {
389 throw new ThemisDataException("Unknown ancestor: " + myName);
390 }
391
392
393 pAncestor.updateDataType(myActual);
394 }
395
396
397
398
399 void resolveReferences() {
400
401 processImplicit();
402
403
404 for (ThemisAnalysisReference myRef : theReferences) {
405
406 final ThemisAnalysisDataType myType = myRef.getDataType();
407
408
409 if (myType instanceof ThemisAnalysisDataTypeUnknown) {
410
411 final ThemisAnalysisDataType myActual = theLocalTypes.get(myType.toString());
412 if (!(myActual instanceof ThemisAnalysisDataTypeUnknown)) {
413 myRef.updateDataType(myActual);
414 }
415 }
416 }
417 }
418
419
420
421
422 private void processImplicit() {
423
424 for (ThemisAnalysisObject myClass : theFileClasses) {
425
426 processAncestors(myClass);
427 }
428 }
429
430
431
432
433
434
435 private void processAncestors(final ThemisAnalysisObject pClass) {
436
437 for (ThemisAnalysisReference myRef : pClass.getAncestors()) {
438
439 final ThemisAnalysisDataType myDataType = myRef.getDataType();
440 if (myDataType instanceof ThemisAnalysisObject myObject) {
441 processAncestor(myObject);
442 processAncestors(myObject);
443 }
444 }
445 }
446
447
448
449
450
451
452 private void processAncestor(final ThemisAnalysisObject pAncestor) {
453
454 for (ThemisAnalysisObject myClass : theClassMap.values()) {
455
456 final String myName = pAncestor.getFullName() + ThemisAnalysisChar.PERIOD + myClass.getShortName();
457 if (myName.equals(myClass.getFullName())) {
458
459 theLocalTypes.put(myClass.getShortName(), myClass);
460 }
461 }
462 }
463
464
465
466
467 void reportUnknown() {
468
469 for (Entry<String, ThemisAnalysisDataType> myEntry : theLocalTypes.entrySet()) {
470
471 final ThemisAnalysisDataType myType = myEntry.getValue();
472
473
474 if (myType instanceof ThemisAnalysisDataTypeUnknown myUnknown) {
475
476 processUnknown(myEntry, myUnknown);
477 }
478 }
479 }
480
481
482
483
484
485
486
487 private void processUnknown(final Entry<String, ThemisAnalysisDataType> pEntry,
488 final ThemisAnalysisDataTypeUnknown pUnknown) {
489
490 final String myName = pEntry.getKey();
491 if (HIDDENTYPES.containsKey(myName)) {
492
493 final ThemisAnalysisDataType myParent = theLocalTypes.get(HIDDENTYPES.get(myName));
494 if (myParent instanceof ThemisAnalysisImport myImport) {
495
496 final String myFullName = myImport.getFullName() + ThemisAnalysisChar.PERIOD + myName;
497 final ThemisAnalysisImport myChild = new ThemisAnalysisImport(myFullName);
498 pEntry.setValue(myChild);
499 return;
500 }
501 }
502
503
504 LOGGER.info("Unknown: " + pUnknown.toString());
505 }
506
507
508
509
510
511
512 private static Map<String, ThemisAnalysisDataType> createDataTypeMap() {
513
514 final Map<String, ThemisAnalysisDataType> myMap = new HashMap<>();
515
516
517 for (ThemisAnalysisPrimitive myPrimitive : ThemisAnalysisPrimitive.values()) {
518 myMap.put(myPrimitive.toString(), myPrimitive);
519 if (myPrimitive.getBoxed() != null) {
520 myMap.put(myPrimitive.getBoxed(), myPrimitive);
521 }
522 }
523
524
525 for (ThemisAnalysisJavaLang myClass : ThemisAnalysisJavaLang.values()) {
526 myMap.put(myClass.toString(), myClass);
527 }
528
529
530 return myMap;
531 }
532
533
534
535
536
537
538 private static Map<String, String> createHiddenTypeMap() {
539 final Map<String, String> myMap = new HashMap<>();
540 myMap.put("StateChangeNotification", "Preloader");
541 myMap.put("SortKey", "RowSorter");
542 myMap.put("Entry", "RowFilter");
543 return myMap;
544 }
545
546
547
548
549
550
551
552 public int getLocalId(final String pName) {
553 final int myId = 1 + theLocalIdMap.computeIfAbsent(pName, s -> 0);
554 theLocalIdMap.put(pName, myId);
555 return myId;
556 }
557
558
559
560
561 public static class ThemisAnalysisDataTypeUnknown
562 implements ThemisAnalysisDataType {
563
564
565
566 private final String theName;
567
568
569
570
571
572
573 ThemisAnalysisDataTypeUnknown(final String pName) {
574 theName = pName;
575 }
576
577 @Override
578 public String toString() {
579 return theName;
580 }
581 }
582
583
584
585
586 public static class ThemisAnalysisDataTypeChild
587 implements ThemisAnalysisDataType {
588
589
590
591 private final ThemisAnalysisDataType theParent;
592
593
594
595
596 private final String theChild;
597
598
599
600
601
602
603
604 ThemisAnalysisDataTypeChild(final ThemisAnalysisDataType pParent,
605 final String pChild) {
606 theParent = pParent;
607 theChild = pChild;
608 }
609
610 @Override
611 public String toString() {
612 return theParent.toString() + ThemisAnalysisChar.PERIOD + theChild;
613 }
614 }
615 }