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