View Javadoc
1   /*
2    * Themis: Java Project Framework
3    * Copyright 2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
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.ThemisDeclarationInstance;
25  import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisId;
26  import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisNodeInstance;
27  import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisTypeInstance;
28  import io.github.tonywasher.joceanus.themis.parser.base.ThemisModifierList;
29  import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclEnum;
30  import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclEnumValue;
31  import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclField;
32  import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclaration;
33  import io.github.tonywasher.joceanus.themis.parser.expr.ThemisExprTypePattern;
34  import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeParameter;
35  import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeVariable;
36  import io.github.tonywasher.joceanus.themis.parser.stmt.ThemisStatement;
37  import io.github.tonywasher.joceanus.themis.parser.type.ThemisTypeClassInterface;
38  import io.github.tonywasher.joceanus.themis.solver.reflect.ThemisReflectExternal;
39  
40  import java.util.ArrayDeque;
41  import java.util.Deque;
42  import java.util.HashMap;
43  import java.util.Map;
44  
45  /**
46   * Name state.
47   */
48  public class ThemisMapperNameState {
49      /**
50       * The nameState stack.
51       */
52      private final Deque<ThemisMapperNameMap> theStack;
53  
54      /**
55       * The active nameState.
56       */
57      private ThemisMapperNameMap theName;
58  
59      /**
60       * Constructor.
61       */
62      ThemisMapperNameState() {
63          /* create stack */
64          theStack = new ArrayDeque<>();
65  
66          /* Create initial Name Map */
67          theName = new ThemisMapperNameMap();
68      }
69  
70      /**
71       * Reset.
72       */
73      void reset() {
74          theStack.clear();
75          theName = new ThemisMapperNameMap();
76      }
77  
78      /**
79       * Process the start of an instance.
80       *
81       * @param pInstance the instance
82       * @return should cleanUp be called after processing this instance?
83       * @throws OceanusException on error
84       */
85      boolean processInstance(final ThemisInstance pInstance) throws OceanusException {
86          /* Determine whether we should bump the stack */
87          final boolean doBump = bumpStack(pInstance);
88          if (doBump) {
89              bumpStack();
90          }
91  
92          /* Process interesting elements */
93          if (pInstance instanceof ThemisClassInstance myClass) {
94              processClass(myClass);
95          }
96          if (pInstance instanceof ThemisDeclEnum myEnum) {
97              return processEnum(myEnum);
98          }
99          if (pInstance instanceof ThemisNodeVariable myVar) {
100             return processVariable(myVar);
101         }
102         if (pInstance instanceof ThemisNodeParameter myParam) {
103             return processParameter(myParam);
104         }
105         if (pInstance instanceof ThemisExprTypePattern myPattern) {
106             return processPattern(myPattern);
107         }
108 
109         /* Return bump indication */
110         return doBump;
111     }
112 
113     /**
114      * Bump the stack.
115      */
116     private void bumpStack() {
117         theStack.push(theName);
118         theName = new ThemisMapperNameMap(theName);
119     }
120 
121     /**
122      * cleanUp after stacked instance.
123      */
124     void cleanUpAfterInstance() {
125         theName = theStack.pop();
126     }
127 
128     /**
129      * Look up type.
130      *
131      * @param pName the name of the type
132      * @return the type
133      */
134     ThemisTypeInstance lookUpName(final String pName) {
135         return theName.lookUpName(pName);
136     }
137 
138     /**
139      * Process enumValue.
140      *
141      * @param pElement the element
142      * @return false
143      */
144     private boolean processEnum(final ThemisDeclEnum pElement) {
145         final ThemisMapperTypeRef myRef = new ThemisMapperTypeRef(pElement);
146         for (ThemisDeclarationInstance myInstance : pElement.getValues()) {
147             final ThemisDeclEnumValue myValue = (ThemisDeclEnumValue) myInstance;
148             theName.declareEnum(myRef, myValue);
149         }
150         return false;
151     }
152 
153     /**
154      * Process variableDecl element.
155      *
156      * @param pElement the element
157      * @return false
158      */
159     private boolean processVariable(final ThemisNodeVariable pElement) {
160         theName.declareVariable(pElement);
161         return false;
162     }
163 
164     /**
165      * Process parameter.
166      *
167      * @param pElement the parameter
168      * @return false
169      */
170     private boolean processParameter(final ThemisNodeParameter pElement) {
171         theName.declareParameter(pElement);
172         return false;
173     }
174 
175     /**
176      * Process pattern element.
177      *
178      * @param pElement the element
179      * @return false
180      */
181     private boolean processPattern(final ThemisExprTypePattern pElement) {
182         theName.declarePattern(pElement);
183         return false;
184     }
185 
186     /**
187      * Process class element.
188      *
189      * @param pClass the element
190      * @throws OceanusException on error
191      */
192     private void processClass(final ThemisClassInstance pClass) throws OceanusException {
193         for (ThemisTypeInstance myClass : pClass.getExtends()) {
194             final ThemisTypeClassInterface myExtend = (ThemisTypeClassInterface) myClass;
195             ThemisClassInstance myClassInstance = myExtend.getClassInstance();
196             if (myClassInstance instanceof ThemisReflectExternal myExternal) {
197                 myClassInstance = myExternal.getClassInstance();
198             }
199             if (myClassInstance == null) {
200                 throw new ThemisDataException("Class Instance not found: " + pClass.getFullName());
201             }
202 
203             /* Loop through all the field declarations */
204             for (ThemisInstance myChild : myClassInstance.getBody()) {
205                 if (myChild instanceof ThemisDeclField myField) {
206                     processField(myField);
207                 }
208             }
209 
210             /* Follow the chain */
211             processClass(myClassInstance);
212         }
213     }
214 
215     /**
216      * Process field element.
217      *
218      * @param pField the element
219      */
220     private void processField(final ThemisDeclField pField) {
221         /* If the field is public or protected */
222         final ThemisModifierList myModifiers = pField.getModifiers();
223         if (myModifiers.isProtected() || myModifiers.isPublic()) {
224             for (ThemisNodeInstance myVarNode : pField.getVariables()) {
225                 final ThemisNodeVariable myVar = (ThemisNodeVariable) myVarNode;
226                 processVariable(myVar);
227             }
228         }
229     }
230 
231     /**
232      * Should an instance bump the name stack?
233      *
234      * @param pInstance the instance
235      * @return true/false
236      */
237     private boolean bumpStack(final ThemisInstance pInstance) {
238         /* Process interesting ids */
239         final ThemisId myId = pInstance.getId();
240         if (myId instanceof ThemisDeclaration myDeclType) {
241             return bumpStack(myDeclType);
242         }
243         if (myId instanceof ThemisStatement myStmtType) {
244             return bumpStack(myStmtType);
245         }
246         return false;
247     }
248 
249     /**
250      * Should a declaration bump the name stack?
251      *
252      * @param pDeclType the declaration type
253      * @return true/false
254      */
255     private boolean bumpStack(final ThemisDeclaration pDeclType) {
256         return switch (pDeclType) {
257             case CLASSINTERFACE, ENUM, RECORD, METHOD, CONSTRUCTOR -> true;
258             default -> false;
259         };
260     }
261 
262     /**
263      * Should a statement bump the name stack?
264      *
265      * @param pStmtType the declaration type
266      * @return true/false
267      */
268     private boolean bumpStack(final ThemisStatement pStmtType) {
269         return switch (pStmtType) {
270             case BLOCK, FOR, FOREACH, TRY -> true;
271             default -> false;
272         };
273     }
274 
275     /**
276      * The cascading map of names.
277      */
278     private static class ThemisMapperNameMap {
279         /**
280          * The parent map.
281          */
282         private final ThemisMapperNameMap theParent;
283 
284         /**
285          * The type variables map.
286          */
287         private final Map<String, ThemisTypeInstance> theNames;
288 
289         /**
290          * Constructor.
291          */
292         ThemisMapperNameMap() {
293             this(null);
294         }
295 
296         /**
297          * Constructor.
298          *
299          * @param pParent the parent
300          */
301         ThemisMapperNameMap(final ThemisMapperNameMap pParent) {
302             theParent = pParent;
303             theNames = new HashMap<>();
304         }
305 
306         /**
307          * Declare enumValue.
308          *
309          * @param pRef  the reference
310          * @param pEnum the enumValue
311          */
312         void declareEnum(final ThemisMapperTypeRef pRef,
313                          final ThemisDeclEnumValue pEnum) {
314             theNames.put(pEnum.getName().toString(), pRef);
315         }
316 
317         /**
318          * Declare variable.
319          *
320          * @param pVar the variable declarator
321          */
322         void declareVariable(final ThemisNodeVariable pVar) {
323             theNames.put(pVar.getName().toString(), pVar.getType());
324         }
325 
326         /**
327          * Declare parameter.
328          *
329          * @param pParam the parameter
330          */
331         void declareParameter(final ThemisNodeParameter pParam) {
332             theNames.put(pParam.getName().toString(), pParam.getType());
333         }
334 
335         /**
336          * Declare pattern.
337          *
338          * @param pPattern the pattern
339          */
340         void declarePattern(final ThemisExprTypePattern pPattern) {
341             theNames.put(pPattern.getName().toString(), pPattern.getType());
342         }
343 
344         /**
345          * Look up name.
346          *
347          * @param pName the name of the variable
348          * @return the type
349          */
350         ThemisTypeInstance lookUpName(final String pName) {
351             /* Look up name in map */
352             final ThemisTypeInstance myType = theNames.get(pName);
353             if (myType != null) {
354                 return myType;
355             }
356 
357             /* If we did not find the name, try the parent map */
358             return theParent == null ? null : theParent.lookUpName(pName);
359         }
360     }
361 }