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.ThemisChar;
20  import io.github.tonywasher.joceanus.themis.parser.proj.ThemisFile;
21  import io.github.tonywasher.joceanus.themis.parser.proj.ThemisPackage;
22  import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverDef.ThemisSolverModuleDef;
23  import io.github.tonywasher.joceanus.themis.solver.proj.ThemisSolverDef.ThemisSolverPackageDef;
24  
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  /**
29   * Solver Package.
30   */
31  public class ThemisSolverPackage
32          implements ThemisSolverPackageDef, Comparable<ThemisSolverPackage> {
33      /**
34       * The owning module.
35       */
36      private final ThemisSolverModuleDef theModule;
37  
38      /**
39       * The underlying package.
40       */
41      private final ThemisPackage thePackage;
42  
43      /**
44       * The shortName.
45       */
46      private final String theShortName;
47  
48      /**
49       * The files.
50       */
51      private final List<ThemisSolverFile> theFiles;
52  
53      /**
54       * The list of child packages.
55       */
56      private final List<ThemisSolverPackage> theChildren;
57  
58      /**
59       * The referenceMap.
60       */
61      private final ThemisSolverReference theReferenceMap;
62  
63      /**
64       * The referenced sibling packages in local package.
65       */
66      private final List<ThemisSolverPackageDef> theLocalReferences;
67  
68      /**
69       * The implied referenced sibling packages in local package.
70       */
71      private final List<ThemisSolverPackageDef> theImpliedReferences;
72  
73      /**
74       * Is this a standard package?
75       */
76      private final boolean isStandard;
77  
78      /**
79       * The parent package.
80       */
81      private ThemisSolverPackage theParent;
82  
83      /**
84       * Is the reference list circular?
85       */
86      private boolean isCircular;
87  
88      /**
89       * Is the package incestuous viz. its parent?
90       */
91      private boolean isIncestuous;
92  
93      /**
94       * Constructor.
95       *
96       * @param pModule  the owning module
97       * @param pPackage the parsed package
98       */
99      ThemisSolverPackage(final ThemisSolverModuleDef pModule,
100                         final ThemisPackage pPackage) {
101         /* Store the package and register with parser */
102         theModule = pModule;
103         thePackage = pPackage;
104         isStandard = pPackage.isStandard();
105 
106         /* Determine the short name */
107         final String myName = getPackageName();
108         final int iIndex = myName.lastIndexOf(ThemisChar.PERIOD);
109         theShortName = iIndex == -1 ? myName : myName.substring(iIndex + 1);
110 
111         /* Create the referenceMap and lists */
112         theReferenceMap = new ThemisSolverReference();
113         theLocalReferences = new ArrayList<>();
114         theImpliedReferences = new ArrayList<>();
115 
116         /* Populate the fileList */
117         theFiles = new ArrayList<>();
118         theChildren = new ArrayList<>();
119         for (ThemisFile myFile : thePackage.getFiles()) {
120             final ThemisSolverFile mySolverFile = new ThemisSolverFile(this, myFile);
121             theFiles.add(mySolverFile);
122         }
123     }
124 
125     @Override
126     public ThemisSolverModuleDef getOwningModule() {
127         return theModule;
128     }
129 
130     @Override
131     public ThemisPackage getUnderlyingPackage() {
132         return thePackage;
133     }
134 
135     @Override
136     public String getPackageName() {
137         return thePackage.getPackage();
138     }
139 
140     /**
141      * Obtain the short package name.
142      *
143      * @return the shirt package name
144      */
145     public String getShortName() {
146         return theShortName;
147     }
148 
149     /**
150      * Obtain the file list.
151      *
152      * @return the file list
153      */
154     public List<ThemisSolverFile> getFiles() {
155         return theFiles;
156     }
157 
158     /**
159      * Obtain the list of child packages.
160      *
161      * @return the list of child packages
162      */
163     public List<ThemisSolverPackage> getChildren() {
164         return theChildren;
165     }
166 
167     /**
168      * Obtain the referenceMap.
169      *
170      * @return the referenceMap
171      */
172     public ThemisSolverReference getReferenceMap() {
173         return theReferenceMap;
174     }
175 
176     @Override
177     public List<ThemisSolverPackageDef> getLocalReferences() {
178         return theLocalReferences;
179     }
180 
181     /**
182      * Obtain the implied references.
183      *
184      * @return the implied references
185      */
186     public List<ThemisSolverPackageDef> getImpliedReferences() {
187         return theImpliedReferences;
188     }
189 
190     /**
191      * Add the child package.
192      *
193      * @param pChild the child package
194      */
195     public void addChild(final ThemisSolverPackage pChild) {
196         theChildren.add(pChild);
197         pChild.setParent(this);
198     }
199 
200     /**
201      * Set parent package.
202      *
203      * @param pParent the parent package
204      */
205     private void setParent(final ThemisSolverPackage pParent) {
206         theParent = pParent;
207     }
208 
209     /**
210      * Obtain the parent package.
211      *
212      * @return the parent
213      */
214     public ThemisSolverPackage getParent() {
215         return theParent;
216     }
217 
218     @Override
219     public boolean isStandard() {
220         return isStandard;
221     }
222 
223     /**
224      * Is the reference list circular?
225      *
226      * @return true/false
227      */
228     public boolean isCircular() {
229         return isCircular;
230     }
231 
232     /**
233      * Mark the package as incestuous.
234      */
235     public void markIncestuous() {
236         isIncestuous = true;
237     }
238 
239     /**
240      * Is the reference list incestuous?
241      *
242      * @return true/false
243      */
244     public boolean isIncestuous() {
245         return isIncestuous;
246     }
247 
248     /**
249      * Is the package a placeHolder?
250      *
251      * @return true/false
252      */
253     public boolean isPlaceHolder() {
254         return thePackage.isPlaceHolder();
255     }
256 
257     /**
258      * Set the referenced classes.
259      *
260      * @param pReferenced the referenced classes
261      */
262     public void setReferenced(final List<ThemisSolverPackageDef> pReferenced) {
263         /* Add all references except for a self-reference */
264         theLocalReferences.addAll(pReferenced.stream().filter(s -> !s.equals(this)).toList());
265     }
266 
267     /**
268      * process local references.
269      */
270     public void processLocalReferences() {
271         /* Loop through the referenced local classes */
272         for (ThemisSolverPackageDef myPackage : theLocalReferences) {
273             /* Process the class */
274             processLocalReferences(myPackage);
275         }
276 
277         /* Determine whether we are circular */
278         isCircular = theImpliedReferences.contains(this);
279     }
280 
281     /**
282      * process local references.
283      *
284      * @param pPackage the class to process local references for
285      */
286     private void processLocalReferences(final ThemisSolverPackageDef pPackage) {
287         /* If this is not already in the local reference list */
288         if (!theImpliedReferences.contains(pPackage)) {
289             /* Add the class */
290             theImpliedReferences.add(pPackage);
291 
292             /* Only process further if we have not found circularity */
293             if (!pPackage.equals(this)) {
294                 /* Loop through the local references */
295                 for (ThemisSolverPackageDef myPackage : pPackage.getLocalReferences()) {
296                     /* Process the local references */
297                     processLocalReferences(myPackage);
298                 }
299             }
300         }
301     }
302 
303 
304     @Override
305     public boolean equals(final Object pThat) {
306         /* Handle the trivial cases */
307         if (this == pThat) {
308             return true;
309         }
310         if (pThat == null) {
311             return false;
312         }
313 
314         /* Make sure that the object is a Package */
315         if (!(pThat instanceof ThemisSolverPackage myThat)) {
316             return false;
317         }
318 
319         /* Check name of package */
320         return getPackageName().equals(myThat.getPackageName());
321     }
322 
323     @Override
324     public int hashCode() {
325         return getPackageName().hashCode();
326     }
327 
328     @Override
329     public String toString() {
330         return thePackage.toString();
331     }
332 
333     @Override
334     public int compareTo(final ThemisSolverPackage pThat) {
335         /* Handle simple dependency */
336         if (theImpliedReferences.contains(pThat)
337                 && !pThat.getImpliedReferences().contains(this)) {
338             return -1;
339         }
340         if (pThat.getImpliedReferences().contains(this)
341                 && !theImpliedReferences.contains(pThat)) {
342             return 1;
343         }
344 
345         /* Sort on number of dependencies */
346         final int iDiff = pThat.theImpliedReferences.size()
347                 - theImpliedReferences.size();
348         if (iDiff != 0) {
349             return iDiff;
350         }
351 
352         /* If all else fails rely on alphabetical */
353         return getPackageName().compareTo(pThat.getPackageName());
354     }
355 }