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.solver.proj;
18  
19  import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisClassInstance;
20  import io.github.tonywasher.joceanus.themis.parser.proj.ThemisFile;
21  import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverDef.ThemisSolverFileDef;
22  import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverDef.ThemisSolverPackageDef;
23  
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  /**
28   * Solver File.
29   */
30  public class ThemisSolverFile
31          implements ThemisSolverFileDef, Comparable<ThemisSolverFile> {
32      /**
33       * The owning package.
34       */
35      private final ThemisSolverPackageDef thePackage;
36  
37      /**
38       * The underlying file.
39       */
40      private final ThemisFile theFile;
41  
42      /**
43       * The top-level class.
44       */
45      private final ThemisSolverClass theTopLevel;
46  
47      /**
48       * The classes.
49       */
50      private final List<ThemisSolverClass> theClasses;
51  
52      /**
53       * The referenced classes in all packages.
54       */
55      private final List<ThemisSolverClass> theReferenced;
56  
57      /**
58       * The referenced classes in local package.
59       */
60      private final List<ThemisSolverClass> theLocalReferences;
61  
62      /**
63       * The implied referenced classes in local package.
64       */
65      private final List<ThemisSolverClass> theImpliedReferences;
66  
67      /**
68       * Does the file need preProcessing?
69       */
70      private boolean needsPreProcess;
71  
72      /**
73       * Is the reference list circular?
74       */
75      private boolean isCircular;
76  
77      /**
78       * Constructor.
79       *
80       * @param pPackage the owning package
81       * @param pFile    the parsed file
82       */
83      ThemisSolverFile(final ThemisSolverPackageDef pPackage,
84                       final ThemisFile pFile) {
85          /* Store the parameters */
86          thePackage = pPackage;
87          theFile = pFile;
88  
89          /* Note that we need preProcessing */
90          needsPreProcess = true;
91  
92          /* Populate the classList */
93          theClasses = new ArrayList<>();
94          for (ThemisClassInstance myClass : theFile.getClasses()) {
95              final ThemisSolverClass mySolverClass = new ThemisSolverClass(this, myClass);
96              theClasses.add(mySolverClass);
97          }
98  
99          /* Determine top-level class */
100         theTopLevel = theClasses.stream().filter(ThemisSolverClass::isTopLevel).findFirst().orElse(null);
101 
102         /* Create the reference lists */
103         theReferenced = new ArrayList<>();
104         theLocalReferences = new ArrayList<>();
105         theImpliedReferences = new ArrayList<>();
106     }
107 
108     @Override
109     public ThemisSolverPackageDef getOwningPackage() {
110         return thePackage;
111     }
112 
113     @Override
114     public ThemisFile getUnderlyingFile() {
115         return theFile;
116     }
117 
118     /**
119      * Obtain the top-level class.
120      *
121      * @return the top-level
122      */
123     public ThemisSolverClass getTopLevel() {
124         return theTopLevel;
125     }
126 
127     /**
128      * Obtain the classes.
129      *
130      * @return the classes
131      */
132     public List<ThemisSolverClass> getClasses() {
133         return theClasses;
134     }
135 
136     /**
137      * Obtain the referenced classes.
138      *
139      * @return the classes
140      */
141     public List<ThemisSolverClass> getReferenced() {
142         return theReferenced;
143     }
144 
145     /**
146      * Obtain the local references.
147      *
148      * @return the local references
149      */
150     public List<ThemisSolverClass> getLocalReferences() {
151         return theLocalReferences;
152     }
153 
154     /**
155      * Obtain the implied references.
156      *
157      * @return the implied references
158      */
159     public List<ThemisSolverClass> getImpliedReferences() {
160         return theImpliedReferences;
161     }
162 
163     /**
164      * Mark the file as pre-processed.
165      */
166     public void markPreProcessed() {
167         needsPreProcess = false;
168     }
169 
170     /**
171      * Does this file need preProcessing?
172      *
173      * @return true/false
174      */
175     public boolean needsPreProcess() {
176         return needsPreProcess;
177     }
178 
179     /**
180      * Is the reference list circular?
181      *
182      * @return true/false
183      */
184     public boolean isCircular() {
185         return isCircular;
186     }
187 
188     /**
189      * Obtain the location.
190      *
191      * @return the location
192      */
193     public String getLocation() {
194         return theFile.getLocation();
195     }
196 
197     @Override
198     public String toString() {
199         return theFile.toString();
200     }
201 
202     /**
203      * Set the referenced classes.
204      *
205      * @param pReferenced the referenced classes
206      */
207     public void setReferenced(final List<ThemisSolverClass> pReferenced) {
208         /* Add all references except for a self-reference */
209         theReferenced.addAll(pReferenced.stream().filter(s -> !s.equals(getTopLevel())).toList());
210 
211         /* Build local reference list */
212         final String myPackage = theTopLevel.getPackageName();
213         for (ThemisSolverClass myClass : theReferenced) {
214             /* Add reference if this is a local class */
215             if (myClass.getPackageName().equals(myPackage)) {
216                 theLocalReferences.add(myClass);
217             }
218         }
219     }
220 
221     /**
222      * process local references.
223      */
224     public void processLocalReferences() {
225         /* Loop through the referenced local classes */
226         for (ThemisSolverClass myClass : theLocalReferences) {
227             /* Process the class */
228             processLocalReferences(myClass);
229         }
230 
231         /* Determine whether we are circular */
232         isCircular = theImpliedReferences.contains(theTopLevel);
233     }
234 
235     /**
236      * process local references.
237      *
238      * @param pClass the class to process local references for
239      */
240     private void processLocalReferences(final ThemisSolverClass pClass) {
241         /* If this is not already in the local reference list */
242         if (!theImpliedReferences.contains(pClass)) {
243             /* Add the class */
244             theImpliedReferences.add(pClass);
245 
246             /* Only process further if we have not found circularity */
247             if (!pClass.equals(theTopLevel)) {
248                 /* Loop through the local references */
249                 for (ThemisSolverClass myClass : ((ThemisSolverFile) pClass.getOwningFile()).getLocalReferences()) {
250                     /* Process the local references */
251                     processLocalReferences(myClass);
252                 }
253             }
254         }
255     }
256 
257     @Override
258     public boolean equals(final Object pThat) {
259         /* Handle the trivial cases */
260         if (this == pThat) {
261             return true;
262         }
263         if (pThat == null) {
264             return false;
265         }
266 
267         /* Make sure that the object is a File */
268         if (!(pThat instanceof ThemisSolverFile myThat)) {
269             return false;
270         }
271 
272         /* Check name of package */
273         return getLocation().equals(myThat.getLocation());
274     }
275 
276     @Override
277     public int hashCode() {
278         return theFile.getLocation().hashCode();
279     }
280 
281     @Override
282     public int compareTo(final ThemisSolverFile pThat) {
283         /* Access top-level class */
284         final ThemisSolverClass myClass = pThat.getTopLevel();
285 
286         /* Handle simple dependency */
287         if (theImpliedReferences.contains(myClass)
288                 && !pThat.getImpliedReferences().contains(theTopLevel)) {
289             return -1;
290         }
291         if (pThat.getImpliedReferences().contains(theTopLevel)
292                 && !theImpliedReferences.contains(myClass)) {
293             return 1;
294         }
295 
296         /* Sort on number of dependencies */
297         final int iDiff = pThat.theImpliedReferences.size()
298                 - theImpliedReferences.size();
299         if (iDiff != 0) {
300             return iDiff;
301         }
302 
303         /* If all else fails rely on alphabetical */
304         return getLocation().compareTo(pThat.getLocation());
305     }
306 }