View Javadoc
1   /*
2    * Themis: Java Project Framework
3    * Copyright 2012-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  package io.github.tonywasher.joceanus.themis.xanalysis.solver;
18  
19  import com.github.javaparser.ast.Node;
20  import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisInstance;
21  import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisInstance.ThemisXAnalysisClassInstance;
22  import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisInstance.ThemisXAnalysisNodeInstance;
23  import io.github.tonywasher.joceanus.themis.xanalysis.parser.node.ThemisXAnalysisNode;
24  import io.github.tonywasher.joceanus.themis.xanalysis.parser.node.ThemisXAnalysisNodeImport;
25  import io.github.tonywasher.joceanus.themis.xanalysis.parser.node.ThemisXAnalysisNodeName;
26  import io.github.tonywasher.joceanus.themis.xanalysis.parser.type.ThemisXAnalysisType;
27  import io.github.tonywasher.joceanus.themis.xanalysis.parser.type.ThemisXAnalysisTypeClassInterface;
28  import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverClass;
29  import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverFile;
30  import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverModule;
31  import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverPackage;
32  import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverProject;
33  import io.github.tonywasher.joceanus.themis.xanalysis.solver.reflect.ThemisXAnalysisReflectExternal;
34  
35  import java.util.ArrayList;
36  import java.util.LinkedHashMap;
37  import java.util.List;
38  import java.util.Map;
39  
40  /**
41   * State for solver.
42   */
43  public class ThemisXAnalysisSolverProjectState {
44      /**
45       * Map of all java.lang classes.
46       */
47      private final Map<String, ThemisXAnalysisReflectExternal> theJavaLang;
48  
49      /**
50       * Map of all classes defined in the project.
51       */
52      private final Map<String, ThemisXAnalysisSolverClass> theProjectClasses;
53  
54      /**
55       * Map of all external classes referenced in the project.
56       */
57      private final Map<String, ThemisXAnalysisReflectExternal> theExternalClasses;
58  
59      /**
60       * Map of all known short names in file.
61       */
62      private final Map<String, ThemisXAnalysisClassInstance> theKnownClasses;
63  
64      /**
65       * The referenced classes.
66       */
67      private final List<ThemisXAnalysisSolverClass> theReferenced;
68  
69      /**
70       * Constructor.
71       *
72       * @param pProject the project
73       */
74      ThemisXAnalysisSolverProjectState(final ThemisXAnalysisSolverProject pProject) {
75          /* Build the javaLang map */
76          theJavaLang = ThemisXAnalysisReflectExternal.getJavaLangMap();
77  
78          /* build the project classMap */
79          theProjectClasses = new LinkedHashMap<>();
80          buildProjectClassMap(pProject);
81  
82          /* build the external classMap */
83          theExternalClasses = new LinkedHashMap<>();
84          buildExternalClassMap(pProject);
85  
86          /* Create the maps and lists */
87          theKnownClasses = new LinkedHashMap<>();
88          theReferenced = new ArrayList<>();
89      }
90  
91      /**
92       * Obtain the external classes.
93       *
94       * @return the external classes.
95       */
96      Map<String, ThemisXAnalysisReflectExternal> getExternalClassMap() {
97          return theExternalClasses;
98      }
99  
100     /**
101      * Build project classMap.
102      *
103      * @param pProject the project
104      */
105     private void buildProjectClassMap(final ThemisXAnalysisSolverProject pProject) {
106         /* Loop through all modules */
107         for (ThemisXAnalysisSolverModule myModule : pProject.getModules()) {
108             /* Loop through all packages */
109             for (ThemisXAnalysisSolverPackage myPackage : myModule.getPackages()) {
110                 buildProjectClassMap(myPackage);
111             }
112         }
113     }
114 
115     /**
116      * Build project classMap.
117      *
118      * @param pPackage the package
119      */
120     private void buildProjectClassMap(final ThemisXAnalysisSolverPackage pPackage) {
121         /* Loop through all files */
122         for (ThemisXAnalysisSolverFile myFile : pPackage.getFiles()) {
123             /* Loop through all classes */
124             for (ThemisXAnalysisSolverClass myClass : myFile.getClasses()) {
125                 final ThemisXAnalysisClassInstance myInstance = myClass.getUnderlyingClass();
126                 /* Ignore local and anonymous classes */
127                 if (!myInstance.isLocalDeclaration() && !myInstance.isAnonClass()) {
128                     theProjectClasses.put(myClass.getFullName(), myClass);
129                 }
130             }
131         }
132     }
133 
134     /**
135      * Build external classMap.
136      *
137      * @param pProject the project
138      */
139     private void buildExternalClassMap(final ThemisXAnalysisSolverProject pProject) {
140         /* Initialise the map with the javaLang classes */
141         for (ThemisXAnalysisReflectExternal myClass : theJavaLang.values()) {
142             theExternalClasses.put(myClass.getFullName(), myClass);
143         }
144 
145         /* Loop through all modules */
146         for (ThemisXAnalysisSolverModule myModule : pProject.getModules()) {
147             /* Loop through all packages */
148             for (ThemisXAnalysisSolverPackage myPackage : myModule.getPackages()) {
149                 buildExternalClassMap(myPackage);
150             }
151         }
152     }
153 
154     /**
155      * Build external classMap.
156      *
157      * @param pPackage the package to process.
158      */
159     private void buildExternalClassMap(final ThemisXAnalysisSolverPackage pPackage) {
160         /* Loop through all files */
161         for (ThemisXAnalysisSolverFile myFile : pPackage.getFiles()) {
162             /* Process the imports */
163             for (ThemisXAnalysisNodeInstance myInstance : myFile.getUnderlyingFile().getContents().getImports()) {
164                 /* Determine full name */
165                 final ThemisXAnalysisNodeImport myImport = (ThemisXAnalysisNodeImport) myInstance;
166                 final String myFullName = myImport.getFullName();
167 
168                 /* If this is a previously unseen class */
169                 if (!theProjectClasses.containsKey(myFullName)) {
170                     /* Ensure the external class map */
171                     theExternalClasses.computeIfAbsent(myFullName, n -> new ThemisXAnalysisReflectExternal(myImport));
172                 }
173             }
174         }
175     }
176 
177     /**
178      * Look up import.
179      *
180      * @param pImport the import definition.
181      * @return the import class
182      */
183     private ThemisXAnalysisClassInstance lookUpImport(final ThemisXAnalysisNodeImport pImport) {
184         /* Determine full name */
185         final String myFullName = pImport.getFullName();
186 
187         /* Look for project class of this name */
188         final ThemisXAnalysisSolverClass myClass = theProjectClasses.get(myFullName);
189         return myClass != null
190                 ? myClass.getUnderlyingClass()
191                 : theExternalClasses.get(myFullName);
192     }
193 
194     /**
195      * Process package.
196      *
197      * @param pPackage the package
198      */
199     void processPackage(final ThemisXAnalysisSolverPackage pPackage) {
200         /* Loop through the files in the package */
201         for (ThemisXAnalysisSolverFile myFile : pPackage.getFiles()) {
202             /* Determine the possible references */
203             determineKnownClasses(myFile);
204 
205             /* Detect references */
206             detectClassOrInterfaceTypes(myFile);
207             detectNameReferences(myFile);
208 
209             /* Store the references into the file */
210             myFile.setReferenced(theReferenced);
211         }
212 
213         /* Loop through the files in the package */
214         for (ThemisXAnalysisSolverFile myFile : pPackage.getFiles()) {
215             /* Process local references */
216             myFile.processLocalReferences();
217         }
218     }
219 
220     /**
221      * Determine known classes for file.
222      *
223      * @param pFile the file to process.
224      */
225     private void determineKnownClasses(final ThemisXAnalysisSolverFile pFile) {
226         /* Initialise the map with the javaLang classes */
227         theKnownClasses.clear();
228         theReferenced.clear();
229         for (ThemisXAnalysisClassInstance myClass : theJavaLang.values()) {
230             theKnownClasses.put(myClass.getName(), myClass);
231         }
232 
233         /* Add all the package top-level classes */
234         final ThemisXAnalysisSolverPackage myPackage = (ThemisXAnalysisSolverPackage) pFile.getOwningPackage();
235         for (ThemisXAnalysisSolverFile myFile : myPackage.getFiles()) {
236             final ThemisXAnalysisSolverClass myClass = myFile.getTopLevel();
237             if (myClass != null) {
238                 theKnownClasses.put(myClass.getName(), myClass.getUnderlyingClass());
239             }
240         }
241 
242         /* Process the imports */
243         for (ThemisXAnalysisNodeInstance myNode : pFile.getUnderlyingFile().getContents().getImports()) {
244             /* LookUp the import and record it */
245             final ThemisXAnalysisClassInstance myImport = lookUpImport((ThemisXAnalysisNodeImport) myNode);
246             theKnownClasses.put(myImport.getName(), myImport);
247         }
248 
249         /* Process the classes in the file */
250         for (ThemisXAnalysisSolverClass myClass : pFile.getClasses()) {
251             if (!myClass.getUnderlyingClass().isAnonClass()) {
252                 theKnownClasses.put(myClass.getName(), myClass.getUnderlyingClass());
253             }
254         }
255     }
256 
257     /**
258      * Detect Class or Interface References in a file.
259      *
260      * @param pFile the file
261      */
262     private void detectClassOrInterfaceTypes(final ThemisXAnalysisSolverFile pFile) {
263         /* Access class */
264         final ThemisXAnalysisInstance myClass = (ThemisXAnalysisInstance) pFile.getTopLevel().getUnderlyingClass();
265 
266         /* Obtain all ClassOrInterface references */
267         final List<ThemisXAnalysisInstance> myReferences = myClass.discoverNodes(ThemisXAnalysisType.CLASSINTERFACE);
268 
269         /* Loop through the references */
270         for (ThemisXAnalysisInstance myNode : myReferences) {
271             final ThemisXAnalysisTypeClassInterface myReference = (ThemisXAnalysisTypeClassInterface) myNode;
272             final ThemisXAnalysisClassInstance myResolved = processPossibleReference(myReference.getName());
273             if (myResolved != null) {
274                 myReference.setClassInstance(myResolved);
275             } else {
276                 System.out.println(myReference.getName() + ":" + pFile);
277             }
278         }
279     }
280 
281     /**
282      * Detect Class or Interface References in a file.
283      *
284      * @param pFile the file
285      */
286     private void detectNameReferences(final ThemisXAnalysisSolverFile pFile) {
287         /* Access class */
288         final ThemisXAnalysisInstance myClass = (ThemisXAnalysisInstance) pFile.getTopLevel().getUnderlyingClass();
289 
290         /* Obtain all Name expressions */
291         final List<ThemisXAnalysisInstance> myReferences = myClass.discoverNodes(ThemisXAnalysisNode.NAME);
292 
293         /* Loop through the references */
294         for (ThemisXAnalysisInstance myNode : myReferences) {
295             final ThemisXAnalysisNodeName myReference = (ThemisXAnalysisNodeName) myNode;
296             if (myReference.getQualifier() == null) {
297                 final ThemisXAnalysisClassInstance myResolved = processPossibleReference(myReference.getName());
298                 if (myResolved == null) {
299                     look4Name(myReference);
300                 }
301             }
302         }
303     }
304 
305     /**
306      * Look4Name.
307      *
308      * @param pName the name
309      */
310     private void look4Name(final ThemisXAnalysisNodeName pName) {
311         final String myName = pName.getName();
312         final Node myParent = pName.getNode().getParentNode().orElse(null);
313         //if (myParent != null) {
314 //
315         //}
316     }
317 
318     /**
319      * process possible reference.
320      *
321      * @param pReference the possible reference.
322      * @return the resolved class (if found)
323      */
324     private ThemisXAnalysisClassInstance processPossibleReference(final String pReference) {
325         /* If the reference is interesting */
326         final ThemisXAnalysisClassInstance myReference = theKnownClasses.get(pReference);
327         if (myReference != null) {
328             declareReferencedClass(myReference);
329         }
330         return myReference;
331     }
332 
333     /**
334      * Declare referenced class.
335      *
336      * @param pClass the class
337      */
338     private void declareReferencedClass(final ThemisXAnalysisClassInstance pClass) {
339         /* Lookup the project class and return if not in project */
340         ThemisXAnalysisSolverClass myClass = theProjectClasses.get(pClass.getFullName());
341         if (myClass == null) {
342             return;
343         }
344 
345         /* Convert to top-level class */
346         if (!myClass.isTopLevel()) {
347             myClass = ((ThemisXAnalysisSolverFile) myClass.getOwningFile()).getTopLevel();
348         }
349 
350         /* If this is the first instance of the reference */
351         if (myClass != null && !theReferenced.contains(myClass)) {
352             /* Add to the list of referenced classes */
353             theReferenced.add(myClass);
354         }
355     }
356 }