ThemisSolverFile.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.solver.proj;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisClassInstance;
import io.github.tonywasher.joceanus.themis.parser.proj.ThemisFile;
import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverDef.ThemisSolverFileDef;
import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverDef.ThemisSolverPackageDef;
import java.util.ArrayList;
import java.util.List;
/**
* Solver File.
*/
public class ThemisSolverFile
implements ThemisSolverFileDef, Comparable<ThemisSolverFile> {
/**
* The owning package.
*/
private final ThemisSolverPackageDef thePackage;
/**
* The underlying file.
*/
private final ThemisFile theFile;
/**
* The top-level class.
*/
private final ThemisSolverClass theTopLevel;
/**
* The classes.
*/
private final List<ThemisSolverClass> theClasses;
/**
* The referenced classes in all packages.
*/
private final List<ThemisSolverClass> theReferenced;
/**
* The referenced classes in local package.
*/
private final List<ThemisSolverClass> theLocalReferences;
/**
* The implied referenced classes in local package.
*/
private final List<ThemisSolverClass> theImpliedReferences;
/**
* Does the file need preProcessing?
*/
private boolean needsPreProcess;
/**
* Is the reference list circular?
*/
private boolean isCircular;
/**
* Constructor.
*
* @param pPackage the owning package
* @param pFile the parsed file
*/
ThemisSolverFile(final ThemisSolverPackageDef pPackage,
final ThemisFile pFile) {
/* Store the parameters */
thePackage = pPackage;
theFile = pFile;
/* Note that we need preProcessing */
needsPreProcess = true;
/* Populate the classList */
theClasses = new ArrayList<>();
for (ThemisClassInstance myClass : theFile.getClasses()) {
final ThemisSolverClass mySolverClass = new ThemisSolverClass(this, myClass);
theClasses.add(mySolverClass);
}
/* Determine top-level class */
theTopLevel = theClasses.stream().filter(ThemisSolverClass::isTopLevel).findFirst().orElse(null);
/* Create the reference lists */
theReferenced = new ArrayList<>();
theLocalReferences = new ArrayList<>();
theImpliedReferences = new ArrayList<>();
}
@Override
public ThemisSolverPackageDef getOwningPackage() {
return thePackage;
}
@Override
public ThemisFile getUnderlyingFile() {
return theFile;
}
/**
* Obtain the top-level class.
*
* @return the top-level
*/
public ThemisSolverClass getTopLevel() {
return theTopLevel;
}
/**
* Obtain the classes.
*
* @return the classes
*/
public List<ThemisSolverClass> getClasses() {
return theClasses;
}
/**
* Obtain the referenced classes.
*
* @return the classes
*/
public List<ThemisSolverClass> getReferenced() {
return theReferenced;
}
/**
* Obtain the local references.
*
* @return the local references
*/
public List<ThemisSolverClass> getLocalReferences() {
return theLocalReferences;
}
/**
* Obtain the implied references.
*
* @return the implied references
*/
public List<ThemisSolverClass> getImpliedReferences() {
return theImpliedReferences;
}
/**
* Mark the file as pre-processed.
*/
public void markPreProcessed() {
needsPreProcess = false;
}
/**
* Does this file need preProcessing?
*
* @return true/false
*/
public boolean needsPreProcess() {
return needsPreProcess;
}
/**
* Is the reference list circular?
*
* @return true/false
*/
public boolean isCircular() {
return isCircular;
}
/**
* Obtain the location.
*
* @return the location
*/
public String getLocation() {
return theFile.getLocation();
}
@Override
public String toString() {
return theFile.toString();
}
/**
* Set the referenced classes.
*
* @param pReferenced the referenced classes
*/
public void setReferenced(final List<ThemisSolverClass> pReferenced) {
/* Add all references except for a self-reference */
theReferenced.addAll(pReferenced.stream().filter(s -> !s.equals(getTopLevel())).toList());
/* Build local reference list */
final String myPackage = theTopLevel.getPackageName();
for (ThemisSolverClass myClass : theReferenced) {
/* Add reference if this is a local class */
if (myClass.getPackageName().equals(myPackage)) {
theLocalReferences.add(myClass);
}
}
}
/**
* process local references.
*/
public void processLocalReferences() {
/* Loop through the referenced local classes */
for (ThemisSolverClass myClass : theLocalReferences) {
/* Process the class */
processLocalReferences(myClass);
}
/* Determine whether we are circular */
isCircular = theImpliedReferences.contains(theTopLevel);
}
/**
* process local references.
*
* @param pClass the class to process local references for
*/
private void processLocalReferences(final ThemisSolverClass pClass) {
/* If this is not already in the local reference list */
if (!theImpliedReferences.contains(pClass)) {
/* Add the class */
theImpliedReferences.add(pClass);
/* Only process further if we have not found circularity */
if (!pClass.equals(theTopLevel)) {
/* Loop through the local references */
for (ThemisSolverClass myClass : ((ThemisSolverFile) pClass.getOwningFile()).getLocalReferences()) {
/* Process the local references */
processLocalReferences(myClass);
}
}
}
}
@Override
public boolean equals(final Object pThat) {
/* Handle the trivial cases */
if (this == pThat) {
return true;
}
if (pThat == null) {
return false;
}
/* Make sure that the object is a File */
if (!(pThat instanceof ThemisSolverFile myThat)) {
return false;
}
/* Check name of package */
return getLocation().equals(myThat.getLocation());
}
@Override
public int hashCode() {
return theFile.getLocation().hashCode();
}
@Override
public int compareTo(final ThemisSolverFile pThat) {
/* Access top-level class */
final ThemisSolverClass myClass = pThat.getTopLevel();
/* Handle simple dependency */
if (theImpliedReferences.contains(myClass)
&& !pThat.getImpliedReferences().contains(theTopLevel)) {
return -1;
}
if (pThat.getImpliedReferences().contains(theTopLevel)
&& !theImpliedReferences.contains(myClass)) {
return 1;
}
/* Sort on number of dependencies */
final int iDiff = pThat.theImpliedReferences.size()
- theImpliedReferences.size();
if (iDiff != 0) {
return iDiff;
}
/* If all else fails rely on alphabetical */
return getLocation().compareTo(pThat.getLocation());
}
}