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.themis.parser.base.ThemisChar;
21  import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisClassInstance;
22  import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisNodeInstance;
23  import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeImport;
24  import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverClass;
25  import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverFile;
26  import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverPackage;
27  
28  import java.util.ArrayList;
29  import java.util.LinkedHashMap;
30  import java.util.List;
31  import java.util.Map;
32  
33  /**
34   * File State.
35   */
36  public class ThemisMapperFileState {
37      /**
38       * The project state.
39       */
40      private final ThemisMapperProjectState theProject;
41  
42      /**
43       * Map of all known short names in file.
44       */
45      private final Map<String, ThemisClassInstance> theKnownClasses;
46  
47      /**
48       * The referenced classes.
49       */
50      private final List<ThemisSolverClass> theReferenced;
51  
52      /**
53       * Constructor.
54       *
55       * @param pProject the project
56       */
57      ThemisMapperFileState(final ThemisMapperProjectState pProject) {
58          /* Store the project */
59          theProject = pProject;
60  
61          /* Create the maps and lists */
62          theKnownClasses = new LinkedHashMap<>();
63          theReferenced = new ArrayList<>();
64      }
65  
66      /**
67       * Obtain the referenced classes.
68       *
69       * @return the referenced classes
70       */
71      List<ThemisSolverClass> getReferenced() {
72          return theReferenced;
73      }
74  
75      /**
76       * InitialiseForFile.
77       *
78       * @param pFile the file
79       */
80      void initForFile(final ThemisSolverFile pFile) {
81          /* Clear the maps and lists */
82          theKnownClasses.clear();
83          theReferenced.clear();
84  
85          /* Determine the possible references */
86          determineKnownClasses(pFile);
87      }
88  
89      /**
90       * Determine known classes for file.
91       *
92       * @param pFile the file to process.
93       */
94      private void determineKnownClasses(final ThemisSolverFile pFile) {
95          /* Add all the package top-level classes */
96          final ThemisSolverPackage myPackage = (ThemisSolverPackage) pFile.getOwningPackage();
97          for (ThemisSolverFile myFile : myPackage.getFiles()) {
98              final ThemisSolverClass myClass = myFile.getTopLevel();
99              if (myClass != null) {
100                 theKnownClasses.put(myClass.getName(), myClass.getUnderlyingClass());
101             }
102         }
103 
104         /* Process the imports */
105         for (ThemisNodeInstance myNode : pFile.getUnderlyingFile().getContents().getImports()) {
106             /* LookUp the import and record it */
107             final ThemisClassInstance myImport = lookUpImport((ThemisNodeImport) myNode);
108             theKnownClasses.put(myImport.getName(), myImport);
109         }
110 
111         /* Process the classes in the file */
112         for (ThemisSolverClass myClass : pFile.getClasses()) {
113             if (!myClass.getUnderlyingClass().isAnonClass()) {
114                 theKnownClasses.put(myClass.getName(), myClass.getUnderlyingClass());
115             }
116         }
117     }
118 
119     /**
120      * Process inherited.
121      *
122      * @param pClass the class to process
123      */
124     void processInherited(final ThemisSolverClass pClass) {
125         for (ThemisClassInstance myInherited : theProject.listAllInherited(pClass.getFullName())) {
126             theKnownClasses.put(myInherited.getName(), myInherited);
127         }
128     }
129 
130     /**
131      * Look up import.
132      *
133      * @param pImport the import definition.
134      * @return the import class
135      */
136     private ThemisClassInstance lookUpImport(final ThemisNodeImport pImport) {
137         /* Determine full name */
138         final String myFullName = pImport.getFullName();
139 
140         /* Look for project class of this name */
141         final ThemisSolverClass myClass = theProject.getProjectClassMap().get(myFullName);
142         return myClass != null
143                 ? myClass.getUnderlyingClass()
144                 : theProject.getExternalClassMap().get(myFullName);
145     }
146 
147     /**
148      * process possible reference.
149      *
150      * @param pReference the possible reference.
151      * @return the resolved class (if found)
152      */
153     ThemisClassInstance processPossibleReference(final String pReference) {
154         /* Look up the reference in the list of known classes */
155         ThemisClassInstance myReference = theKnownClasses.get(pReference);
156 
157         /* If it is not a known class */
158         if (myReference == null) {
159             /* If it contains a period */
160             final int iIndex = pReference.indexOf(ThemisChar.PERIOD);
161             if (iIndex != -1) {
162                 /* Look for a fully qualified class in external and project classes */
163                 myReference = lookUpFullyNamedClass(pReference);
164 
165                 /* If still not found */
166                 if (myReference == null) {
167                     /* Try just the first part */
168                     myReference = lookUpPartiallyNamedClass(pReference, iIndex);
169                 }
170 
171                 /* else just a simple name */
172             } else {
173                 myReference = lookUpJavaLangClass(pReference);
174             }
175         }
176 
177         /* If we found the reference, process it */
178         if (myReference != null) {
179             declareReferencedClass(myReference);
180         }
181         return myReference;
182     }
183 
184     /**
185      * lookUp a fullyNamed class.
186      *
187      * @param pReference the possible reference.
188      * @return the resolved class (if found)
189      */
190     private ThemisClassInstance lookUpFullyNamedClass(final String pReference) {
191         /* Look for a fully qualified class in external and project classes */
192         ThemisClassInstance myReference = theProject.getExternalClassMap().get(pReference);
193         if (myReference == null) {
194             final ThemisSolverClass myClass = theProject.getProjectClassMap().get(pReference);
195             if (myClass != null) {
196                 myReference = myClass.getUnderlyingClass();
197             }
198         }
199 
200         /* If not found, try to load the class */
201         if (myReference == null) {
202             myReference = theProject.tryNamedClass(pReference);
203         }
204 
205         /* If we have now found the class, add to knownClasses */
206         if (myReference != null) {
207             theKnownClasses.put(pReference, myReference);
208         }
209 
210         /* Return resolved reference (or null) */
211         return myReference;
212     }
213 
214     /**
215      * lookUp a partiallyNamed class.
216      *
217      * @param pReference the possible reference.
218      * @param pIndex     the index of the first period
219      * @return the resolved class (if found)
220      */
221     private ThemisClassInstance lookUpPartiallyNamedClass(final String pReference,
222                                                           final int pIndex) {
223         /* Try just the first part of the name */
224         final String myBase = pReference.substring(0, pIndex);
225         ThemisClassInstance myReference = theKnownClasses.get(myBase);
226 
227         /* If we found a parent */
228         if (myReference != null) {
229             /* Build the full name of the class */
230             final String myName = myReference.getFullName() + pReference.substring(pIndex);
231             final ThemisSolverClass myProjectClass = theProject.getProjectClassMap().get(myName);
232             myReference = myProjectClass == null ? theProject.tryNamedClass(myName) : myProjectClass.getUnderlyingClass();
233 
234             /* If we have now found the class, add to knownClasses */
235             if (myReference != null) {
236                 theKnownClasses.put(pReference, myReference);
237             }
238         }
239 
240         /* Return resolved reference (or null) */
241         return myReference;
242     }
243 
244     /**
245      * lookUp a javaLang class.
246      *
247      * @param pReference the possible reference.
248      * @return the resolved class (if found)
249      */
250     private ThemisClassInstance lookUpJavaLangClass(final String pReference) {
251         /* Look for a fully qualified class in external and project classes */
252         final ThemisClassInstance myReference = theProject.tryJavaLang(pReference);
253 
254         /* If we have now found the class, add to knownClasses */
255         if (myReference != null) {
256             theKnownClasses.put(pReference, myReference);
257         }
258 
259         /* Return resolved reference (or null) */
260         return myReference;
261     }
262 
263     /**
264      * Declare referenced class.
265      *
266      * @param pClass the class
267      */
268     private void declareReferencedClass(final ThemisClassInstance pClass) {
269         /* Lookup the project class and return if not in project */
270         final Map<String, ThemisSolverClass> myProjectClasses = theProject.getProjectClassMap();
271         ThemisSolverClass myClass = myProjectClasses.get(pClass.getFullName());
272         if (myClass == null) {
273             return;
274         }
275 
276         /* Convert to top-level class */
277         if (!myClass.isTopLevel()) {
278             myClass = ((ThemisSolverFile) myClass.getOwningFile()).getTopLevel();
279         }
280 
281         /* If this is the first instance of the reference */
282         if (myClass != null && !theReferenced.contains(myClass)) {
283             /* Add to the list of referenced classes */
284             theReferenced.add(myClass);
285         }
286     }
287 }