ThemisMapperFileState.java
/*
* Themis: Java Project Framework
* Copyright 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.solver.mapper;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisChar;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisClassInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisNodeInstance;
import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeImport;
import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverClass;
import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverFile;
import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverPackage;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* File State.
*/
public class ThemisMapperFileState {
/**
* The project state.
*/
private final ThemisMapperProjectState theProject;
/**
* Map of all known short names in file.
*/
private final Map<String, ThemisClassInstance> theKnownClasses;
/**
* The referenced classes.
*/
private final List<ThemisSolverClass> theReferenced;
/**
* Constructor.
*
* @param pProject the project
*/
ThemisMapperFileState(final ThemisMapperProjectState pProject) {
/* Store the project */
theProject = pProject;
/* Create the maps and lists */
theKnownClasses = new LinkedHashMap<>();
theReferenced = new ArrayList<>();
}
/**
* Obtain the referenced classes.
*
* @return the referenced classes
*/
List<ThemisSolverClass> getReferenced() {
return theReferenced;
}
/**
* InitialiseForFile.
*
* @param pFile the file
*/
void initForFile(final ThemisSolverFile pFile) {
/* Clear the maps and lists */
theKnownClasses.clear();
theReferenced.clear();
/* Determine the possible references */
determineKnownClasses(pFile);
}
/**
* Determine known classes for file.
*
* @param pFile the file to process.
*/
private void determineKnownClasses(final ThemisSolverFile pFile) {
/* Add all the package top-level classes */
final ThemisSolverPackage myPackage = (ThemisSolverPackage) pFile.getOwningPackage();
for (ThemisSolverFile myFile : myPackage.getFiles()) {
final ThemisSolverClass myClass = myFile.getTopLevel();
if (myClass != null) {
theKnownClasses.put(myClass.getName(), myClass.getUnderlyingClass());
}
}
/* Process the imports */
for (ThemisNodeInstance myNode : pFile.getUnderlyingFile().getContents().getImports()) {
/* LookUp the import and record it */
final ThemisClassInstance myImport = lookUpImport((ThemisNodeImport) myNode);
theKnownClasses.put(myImport.getName(), myImport);
}
/* Process the classes in the file */
for (ThemisSolverClass myClass : pFile.getClasses()) {
if (!myClass.getUnderlyingClass().isAnonClass()) {
theKnownClasses.put(myClass.getName(), myClass.getUnderlyingClass());
}
}
}
/**
* Process inherited.
*
* @param pClass the class to process
*/
void processInherited(final ThemisSolverClass pClass) {
for (ThemisClassInstance myInherited : theProject.listAllInherited(pClass.getFullName())) {
theKnownClasses.put(myInherited.getName(), myInherited);
}
}
/**
* Look up import.
*
* @param pImport the import definition.
* @return the import class
*/
private ThemisClassInstance lookUpImport(final ThemisNodeImport pImport) {
/* Determine full name */
final String myFullName = pImport.getFullName();
/* Look for project class of this name */
final ThemisSolverClass myClass = theProject.getProjectClassMap().get(myFullName);
return myClass != null
? myClass.getUnderlyingClass()
: theProject.getExternalClassMap().get(myFullName);
}
/**
* process possible reference.
*
* @param pReference the possible reference.
* @return the resolved class (if found)
*/
ThemisClassInstance processPossibleReference(final String pReference) {
/* Look up the reference in the list of known classes */
ThemisClassInstance myReference = theKnownClasses.get(pReference);
/* If it is not a known class */
if (myReference == null) {
/* If it contains a period */
final int iIndex = pReference.indexOf(ThemisChar.PERIOD);
if (iIndex != -1) {
/* Look for a fully qualified class in external and project classes */
myReference = lookUpFullyNamedClass(pReference);
/* If still not found */
if (myReference == null) {
/* Try just the first part */
myReference = lookUpPartiallyNamedClass(pReference, iIndex);
}
/* else just a simple name */
} else {
myReference = lookUpJavaLangClass(pReference);
}
}
/* If we found the reference, process it */
if (myReference != null) {
declareReferencedClass(myReference);
}
return myReference;
}
/**
* lookUp a fullyNamed class.
*
* @param pReference the possible reference.
* @return the resolved class (if found)
*/
private ThemisClassInstance lookUpFullyNamedClass(final String pReference) {
/* Look for a fully qualified class in external and project classes */
ThemisClassInstance myReference = theProject.getExternalClassMap().get(pReference);
if (myReference == null) {
final ThemisSolverClass myClass = theProject.getProjectClassMap().get(pReference);
if (myClass != null) {
myReference = myClass.getUnderlyingClass();
}
}
/* If not found, try to load the class */
if (myReference == null) {
myReference = theProject.tryNamedClass(pReference);
}
/* If we have now found the class, add to knownClasses */
if (myReference != null) {
theKnownClasses.put(pReference, myReference);
}
/* Return resolved reference (or null) */
return myReference;
}
/**
* lookUp a partiallyNamed class.
*
* @param pReference the possible reference.
* @param pIndex the index of the first period
* @return the resolved class (if found)
*/
private ThemisClassInstance lookUpPartiallyNamedClass(final String pReference,
final int pIndex) {
/* Try just the first part of the name */
final String myBase = pReference.substring(0, pIndex);
ThemisClassInstance myReference = theKnownClasses.get(myBase);
/* If we found a parent */
if (myReference != null) {
/* Build the full name of the class */
final String myName = myReference.getFullName() + pReference.substring(pIndex);
final ThemisSolverClass myProjectClass = theProject.getProjectClassMap().get(myName);
myReference = myProjectClass == null ? theProject.tryNamedClass(myName) : myProjectClass.getUnderlyingClass();
/* If we have now found the class, add to knownClasses */
if (myReference != null) {
theKnownClasses.put(pReference, myReference);
}
}
/* Return resolved reference (or null) */
return myReference;
}
/**
* lookUp a javaLang class.
*
* @param pReference the possible reference.
* @return the resolved class (if found)
*/
private ThemisClassInstance lookUpJavaLangClass(final String pReference) {
/* Look for a fully qualified class in external and project classes */
final ThemisClassInstance myReference = theProject.tryJavaLang(pReference);
/* If we have now found the class, add to knownClasses */
if (myReference != null) {
theKnownClasses.put(pReference, myReference);
}
/* Return resolved reference (or null) */
return myReference;
}
/**
* Declare referenced class.
*
* @param pClass the class
*/
private void declareReferencedClass(final ThemisClassInstance pClass) {
/* Lookup the project class and return if not in project */
final Map<String, ThemisSolverClass> myProjectClasses = theProject.getProjectClassMap();
ThemisSolverClass myClass = myProjectClasses.get(pClass.getFullName());
if (myClass == null) {
return;
}
/* Convert to top-level class */
if (!myClass.isTopLevel()) {
myClass = ((ThemisSolverFile) 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);
}
}
}