View Javadoc
1   /*
2    * Themis: Java Project Framework
3    * Copyright 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  
18  package io.github.tonywasher.joceanus.themis.parser.maven;
19  
20  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
21  import io.github.tonywasher.joceanus.oceanus.profile.OceanusProfile;
22  import io.github.tonywasher.joceanus.tethys.api.thread.TethysUIThreadStatusReport;
23  import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
24  import io.github.tonywasher.joceanus.themis.exc.ThemisLogicException;
25  import io.github.tonywasher.joceanus.themis.parser.base.ThemisDataResource;
26  import io.github.tonywasher.joceanus.themis.parser.maven.ThemisMavenPom.ThemisXMavenControl;
27  
28  import java.io.File;
29  import java.util.ArrayList;
30  import java.util.LinkedHashMap;
31  import java.util.List;
32  import java.util.Map;
33  
34  /**
35   * Maven Parser.
36   */
37  public class ThemisMavenParser
38          implements ThemisXMavenControl {
39      /**
40       * The POM file name.
41       */
42      private static final String POM_NAME = "pom.xml";
43  
44      /**
45       * The map of maven artifacts.
46       */
47      private final Map<ThemisMavenId, ThemisMavenPom> thePoms;
48  
49      /**
50       * The list of local modules.
51       */
52      private final List<ThemisMavenPom> theModules;
53  
54      /**
55       * The dependencies.
56       */
57      private final List<ThemisMavenId> theDependencies;
58  
59      /**
60       * The pom.
61       */
62      private final ThemisMavenPom thePom;
63  
64      /**
65       * Have we processed local definitions?
66       */
67      private boolean processedLocal;
68  
69      /**
70       * Constructor.
71       *
72       * @param pReport   the reporter
73       * @param pLocation the location of the project
74       * @throws OceanusException on error
75       */
76      public ThemisMavenParser(final TethysUIThreadStatusReport pReport,
77                               final File pLocation) throws OceanusException {
78          /* Obtain the active profile */
79          OceanusProfile myTask = pReport.getActiveTask();
80          myTask = myTask.startTask(ThemisDataResource.TASK_DISCOVER);
81          pReport.initTask(ThemisDataResource.TASK_DISCOVER);
82          pReport.setNumStages(2);
83  
84          /* Create the map */
85          thePoms = new LinkedHashMap<>();
86          processedLocal = false;
87  
88          /* Parse the local project */
89          thePom = loadPomAtLocation(pLocation);
90          processedLocal = true;
91  
92          /* Loop through the local modules */
93          OceanusProfile mySubTask = myTask.startTask(ThemisDataResource.TASK_DISCOVERLOCAL);
94          final List<ThemisMavenPom> myModules = new ArrayList<>(thePoms.values());
95          pReport.setNewStage(ThemisDataResource.TASK_DISCOVERLOCAL);
96          pReport.setNumSteps(myModules.size());
97          for (ThemisMavenPom myModule : myModules) {
98              /* Process the module */
99              final String myName = myModule.getId().toString();
100             mySubTask.startTask(myName);
101             pReport.setNextStep();
102             myModule.getVersions();
103             myModule.getDirectDependencies();
104         }
105 
106         /* Loop through the local modules */
107         mySubTask = myTask.startTask(ThemisDataResource.TASK_DISCOVERDEPENDENCY);
108         pReport.setNewStage(ThemisDataResource.TASK_DISCOVERDEPENDENCY);
109         pReport.setNumSteps(myModules.size());
110         for (ThemisMavenPom myModule : myModules) {
111             /* Process the module */
112             final String myName = myModule.getId().toString();
113             mySubTask.startTask(myName);
114             pReport.setNextStep();
115             myModule.getDependencies();
116         }
117 
118         /* Create list of local modules */
119         theModules = getModuleList(myModules);
120         theDependencies = thePom.getDependencies();
121         myTask.end();
122     }
123 
124     /**
125      * Obtain the name.
126      *
127      * @return the name
128      */
129     public String getName() {
130         return thePom.getArtifactId();
131     }
132 
133     /**
134      * Obtain the local modules.
135      *
136      * @return the local modules
137      */
138     public List<ThemisMavenId> getModules() {
139         return theModules.stream().map(ThemisMavenPom::getId).toList();
140     }
141 
142     /**
143      * Obtain the parsed modules.
144      *
145      * @return the parsed modules
146      */
147     public List<ThemisMavenPom> getParsedModules() {
148         return theModules;
149     }
150 
151     /**
152      * Obtain the dependencies.
153      *
154      * @return the dependencies
155      */
156     public List<ThemisMavenId> getDependencies() {
157         return theDependencies;
158     }
159 
160     @Override
161     public ThemisMavenPom loadPomViaId(final ThemisMavenId pId) throws OceanusException {
162         /* Look for already known Pom */
163         final ThemisMavenPom myExisting = thePoms.get(pId);
164         if (myExisting != null) {
165             return myExisting;
166         }
167 
168         /* Reject if we have not fully processed local poms */
169         if (!processedLocal) {
170             throw new ThemisLogicException("Processed pom via id before finished local processing");
171         }
172 
173         /* Load the pom file and store into cache */
174         final ThemisMavenPom myPom = doLoadPomViaId(pId);
175         thePoms.put(pId, myPom);
176 
177         /* If the pom has an associated jar */
178         if (myPom.isJarPackaging()) {
179             /* Make sure that the pom has been downloaded */
180             ThemisMavenLocation.ensureJarArtifact(pId);
181         }
182 
183         /* Process dependencies */
184         myPom.getVersions();
185         myPom.getDirectDependencies();
186         myPom.getDependencies();
187 
188         /* Return the pom */
189         return myPom;
190     }
191 
192     /**
193      * Do load pom via Id.
194      *
195      * @param pId the id of the pom
196      * @return the loaded pom
197      * @throws OceanusException on error
198      */
199     private ThemisMavenPom doLoadPomViaId(final ThemisMavenId pId) throws OceanusException {
200         /* If the id has a classifier */
201         if (pId.getClassifier() != null) {
202             /* There is no pom , so just fake it */
203             return new ThemisMavenPom(pId);
204         }
205 
206         /* Make sure that the pom has been downloaded */
207         ThemisMavenLocation.ensurePomArtifact(pId);
208 
209         /* Just do a normal load */
210         return new ThemisMavenPom(this, new File(ThemisMavenLocation.getLocalPomFileName(pId)));
211     }
212 
213     @Override
214     public ThemisMavenPom loadPomAtLocation(final File pLocation) throws OceanusException {
215         /* Parse the pom and obtain the id */
216         final ThemisMavenPom myPom = new ThemisMavenPom(this, new File(pLocation, POM_NAME));
217         final ThemisMavenId myId = myPom.getId();
218 
219         /* Make sure that the pom has not been seen before */
220         final ThemisMavenPom myExisting = thePoms.get(myId);
221         if (myExisting != null) {
222             throw new ThemisDataException("Duplicate POM - " + myId);
223         }
224 
225         /* Store into map */
226         thePoms.put(myId, myPom);
227 
228         /* Process local details */
229         myPom.processLocalDetails();
230 
231         /* Return the Pom */
232         return myPom;
233     }
234 
235     /**
236      * Create list of local jar modules.
237      *
238      * @param pLocal the list of local modules
239      * @return the list.
240      * @throws OceanusException on error
241      */
242     private List<ThemisMavenPom> getModuleList(final List<ThemisMavenPom> pLocal) throws OceanusException {
243         /* Loop through the local modules */
244         final List<ThemisMavenPom> myModules = new ArrayList<>();
245         for (ThemisMavenPom myModule : pLocal) {
246             if (myModule.isJarPackaging()) {
247                 myModules.add(myModule);
248             }
249         }
250         return myModules;
251     }
252 
253     /**
254      * Combine module dependencies.
255      *
256      * @return the dependencies
257      * @throws OceanusException on error
258      */
259     public List<ThemisMavenId> getProjectDependencies() throws OceanusException {
260         /* Allocate the dependencies */
261         final List<ThemisMavenId> myIds = new ArrayList<>();
262 
263         /* Loop through the modules */
264         for (ThemisMavenPom myModule : theModules) {
265             /* Loop through direct dependencies */
266             for (ThemisMavenId myDependency : myModule.getDependencies()) {
267                 /* Check the dependency and add if required */
268                 ThemisMavenPom.checkDependencyId(myIds, myDependency);
269             }
270         }
271 
272         /* Return the id */
273         return myIds;
274     }
275 
276 }