ThemisXAnalysisSolverProjectState.java
/*
* Themis: Java Project Framework
* Copyright 2012-2026. Tony Washer
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.github.tonywasher.joceanus.themis.xanalysis.solver;
import com.github.javaparser.ast.Node;
import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisInstance;
import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisInstance.ThemisXAnalysisClassInstance;
import io.github.tonywasher.joceanus.themis.xanalysis.parser.base.ThemisXAnalysisInstance.ThemisXAnalysisNodeInstance;
import io.github.tonywasher.joceanus.themis.xanalysis.parser.node.ThemisXAnalysisNode;
import io.github.tonywasher.joceanus.themis.xanalysis.parser.node.ThemisXAnalysisNodeImport;
import io.github.tonywasher.joceanus.themis.xanalysis.parser.node.ThemisXAnalysisNodeName;
import io.github.tonywasher.joceanus.themis.xanalysis.parser.type.ThemisXAnalysisType;
import io.github.tonywasher.joceanus.themis.xanalysis.parser.type.ThemisXAnalysisTypeClassInterface;
import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverClass;
import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverFile;
import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverModule;
import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverPackage;
import io.github.tonywasher.joceanus.themis.xanalysis.solver.proj.ThemisXAnalysisSolverProject;
import io.github.tonywasher.joceanus.themis.xanalysis.solver.reflect.ThemisXAnalysisReflectExternal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* State for solver.
*/
public class ThemisXAnalysisSolverProjectState {
/**
* Map of all java.lang classes.
*/
private final Map<String, ThemisXAnalysisReflectExternal> theJavaLang;
/**
* Map of all classes defined in the project.
*/
private final Map<String, ThemisXAnalysisSolverClass> theProjectClasses;
/**
* Map of all external classes referenced in the project.
*/
private final Map<String, ThemisXAnalysisReflectExternal> theExternalClasses;
/**
* Map of all known short names in file.
*/
private final Map<String, ThemisXAnalysisClassInstance> theKnownClasses;
/**
* The referenced classes.
*/
private final List<ThemisXAnalysisSolverClass> theReferenced;
/**
* Constructor.
*
* @param pProject the project
*/
ThemisXAnalysisSolverProjectState(final ThemisXAnalysisSolverProject pProject) {
/* Build the javaLang map */
theJavaLang = ThemisXAnalysisReflectExternal.getJavaLangMap();
/* build the project classMap */
theProjectClasses = new LinkedHashMap<>();
buildProjectClassMap(pProject);
/* build the external classMap */
theExternalClasses = new LinkedHashMap<>();
buildExternalClassMap(pProject);
/* Create the maps and lists */
theKnownClasses = new LinkedHashMap<>();
theReferenced = new ArrayList<>();
}
/**
* Obtain the external classes.
*
* @return the external classes.
*/
Map<String, ThemisXAnalysisReflectExternal> getExternalClassMap() {
return theExternalClasses;
}
/**
* Build project classMap.
*
* @param pProject the project
*/
private void buildProjectClassMap(final ThemisXAnalysisSolverProject pProject) {
/* Loop through all modules */
for (ThemisXAnalysisSolverModule myModule : pProject.getModules()) {
/* Loop through all packages */
for (ThemisXAnalysisSolverPackage myPackage : myModule.getPackages()) {
buildProjectClassMap(myPackage);
}
}
}
/**
* Build project classMap.
*
* @param pPackage the package
*/
private void buildProjectClassMap(final ThemisXAnalysisSolverPackage pPackage) {
/* Loop through all files */
for (ThemisXAnalysisSolverFile myFile : pPackage.getFiles()) {
/* Loop through all classes */
for (ThemisXAnalysisSolverClass myClass : myFile.getClasses()) {
final ThemisXAnalysisClassInstance myInstance = myClass.getUnderlyingClass();
/* Ignore local and anonymous classes */
if (!myInstance.isLocalDeclaration() && !myInstance.isAnonClass()) {
theProjectClasses.put(myClass.getFullName(), myClass);
}
}
}
}
/**
* Build external classMap.
*
* @param pProject the project
*/
private void buildExternalClassMap(final ThemisXAnalysisSolverProject pProject) {
/* Initialise the map with the javaLang classes */
for (ThemisXAnalysisReflectExternal myClass : theJavaLang.values()) {
theExternalClasses.put(myClass.getFullName(), myClass);
}
/* Loop through all modules */
for (ThemisXAnalysisSolverModule myModule : pProject.getModules()) {
/* Loop through all packages */
for (ThemisXAnalysisSolverPackage myPackage : myModule.getPackages()) {
buildExternalClassMap(myPackage);
}
}
}
/**
* Build external classMap.
*
* @param pPackage the package to process.
*/
private void buildExternalClassMap(final ThemisXAnalysisSolverPackage pPackage) {
/* Loop through all files */
for (ThemisXAnalysisSolverFile myFile : pPackage.getFiles()) {
/* Process the imports */
for (ThemisXAnalysisNodeInstance myInstance : myFile.getUnderlyingFile().getContents().getImports()) {
/* Determine full name */
final ThemisXAnalysisNodeImport myImport = (ThemisXAnalysisNodeImport) myInstance;
final String myFullName = myImport.getFullName();
/* If this is a previously unseen class */
if (!theProjectClasses.containsKey(myFullName)) {
/* Ensure the external class map */
theExternalClasses.computeIfAbsent(myFullName, n -> new ThemisXAnalysisReflectExternal(myImport));
}
}
}
}
/**
* Look up import.
*
* @param pImport the import definition.
* @return the import class
*/
private ThemisXAnalysisClassInstance lookUpImport(final ThemisXAnalysisNodeImport pImport) {
/* Determine full name */
final String myFullName = pImport.getFullName();
/* Look for project class of this name */
final ThemisXAnalysisSolverClass myClass = theProjectClasses.get(myFullName);
return myClass != null
? myClass.getUnderlyingClass()
: theExternalClasses.get(myFullName);
}
/**
* Process package.
*
* @param pPackage the package
*/
void processPackage(final ThemisXAnalysisSolverPackage pPackage) {
/* Loop through the files in the package */
for (ThemisXAnalysisSolverFile myFile : pPackage.getFiles()) {
/* Determine the possible references */
determineKnownClasses(myFile);
/* Detect references */
detectClassOrInterfaceTypes(myFile);
detectNameReferences(myFile);
/* Store the references into the file */
myFile.setReferenced(theReferenced);
}
/* Loop through the files in the package */
for (ThemisXAnalysisSolverFile myFile : pPackage.getFiles()) {
/* Process local references */
myFile.processLocalReferences();
}
}
/**
* Determine known classes for file.
*
* @param pFile the file to process.
*/
private void determineKnownClasses(final ThemisXAnalysisSolverFile pFile) {
/* Initialise the map with the javaLang classes */
theKnownClasses.clear();
theReferenced.clear();
for (ThemisXAnalysisClassInstance myClass : theJavaLang.values()) {
theKnownClasses.put(myClass.getName(), myClass);
}
/* Add all the package top-level classes */
final ThemisXAnalysisSolverPackage myPackage = (ThemisXAnalysisSolverPackage) pFile.getOwningPackage();
for (ThemisXAnalysisSolverFile myFile : myPackage.getFiles()) {
final ThemisXAnalysisSolverClass myClass = myFile.getTopLevel();
if (myClass != null) {
theKnownClasses.put(myClass.getName(), myClass.getUnderlyingClass());
}
}
/* Process the imports */
for (ThemisXAnalysisNodeInstance myNode : pFile.getUnderlyingFile().getContents().getImports()) {
/* LookUp the import and record it */
final ThemisXAnalysisClassInstance myImport = lookUpImport((ThemisXAnalysisNodeImport) myNode);
theKnownClasses.put(myImport.getName(), myImport);
}
/* Process the classes in the file */
for (ThemisXAnalysisSolverClass myClass : pFile.getClasses()) {
if (!myClass.getUnderlyingClass().isAnonClass()) {
theKnownClasses.put(myClass.getName(), myClass.getUnderlyingClass());
}
}
}
/**
* Detect Class or Interface References in a file.
*
* @param pFile the file
*/
private void detectClassOrInterfaceTypes(final ThemisXAnalysisSolverFile pFile) {
/* Access class */
final ThemisXAnalysisInstance myClass = (ThemisXAnalysisInstance) pFile.getTopLevel().getUnderlyingClass();
/* Obtain all ClassOrInterface references */
final List<ThemisXAnalysisInstance> myReferences = myClass.discoverNodes(ThemisXAnalysisType.CLASSINTERFACE);
/* Loop through the references */
for (ThemisXAnalysisInstance myNode : myReferences) {
final ThemisXAnalysisTypeClassInterface myReference = (ThemisXAnalysisTypeClassInterface) myNode;
final ThemisXAnalysisClassInstance myResolved = processPossibleReference(myReference.getName());
if (myResolved != null) {
myReference.setClassInstance(myResolved);
} else {
System.out.println(myReference.getName() + ":" + pFile);
}
}
}
/**
* Detect Class or Interface References in a file.
*
* @param pFile the file
*/
private void detectNameReferences(final ThemisXAnalysisSolverFile pFile) {
/* Access class */
final ThemisXAnalysisInstance myClass = (ThemisXAnalysisInstance) pFile.getTopLevel().getUnderlyingClass();
/* Obtain all Name expressions */
final List<ThemisXAnalysisInstance> myReferences = myClass.discoverNodes(ThemisXAnalysisNode.NAME);
/* Loop through the references */
for (ThemisXAnalysisInstance myNode : myReferences) {
final ThemisXAnalysisNodeName myReference = (ThemisXAnalysisNodeName) myNode;
if (myReference.getQualifier() == null) {
final ThemisXAnalysisClassInstance myResolved = processPossibleReference(myReference.getName());
if (myResolved == null) {
look4Name(myReference);
}
}
}
}
/**
* Look4Name.
*
* @param pName the name
*/
private void look4Name(final ThemisXAnalysisNodeName pName) {
final String myName = pName.getName();
final Node myParent = pName.getNode().getParentNode().orElse(null);
//if (myParent != null) {
//
//}
}
/**
* process possible reference.
*
* @param pReference the possible reference.
* @return the resolved class (if found)
*/
private ThemisXAnalysisClassInstance processPossibleReference(final String pReference) {
/* If the reference is interesting */
final ThemisXAnalysisClassInstance myReference = theKnownClasses.get(pReference);
if (myReference != null) {
declareReferencedClass(myReference);
}
return myReference;
}
/**
* Declare referenced class.
*
* @param pClass the class
*/
private void declareReferencedClass(final ThemisXAnalysisClassInstance pClass) {
/* Lookup the project class and return if not in project */
ThemisXAnalysisSolverClass myClass = theProjectClasses.get(pClass.getFullName());
if (myClass == null) {
return;
}
/* Convert to top-level class */
if (!myClass.isTopLevel()) {
myClass = ((ThemisXAnalysisSolverFile) myClass.getOwningFile()).getTopLevel();
}
/* If this is the first instance of the reference */
if (myClass != null && !theReferenced.contains(myClass)) {
/* Add to the list of referenced classes */
theReferenced.add(myClass);
}
}
}