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.lethe.analysis;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
21  import io.github.tonywasher.joceanus.themis.exc.ThemisIOException;
22  import org.w3c.dom.Document;
23  import org.w3c.dom.Element;
24  import org.w3c.dom.Node;
25  import org.xml.sax.SAXException;
26  
27  import javax.xml.XMLConstants;
28  import javax.xml.parsers.DocumentBuilder;
29  import javax.xml.parsers.DocumentBuilderFactory;
30  import javax.xml.parsers.ParserConfigurationException;
31  import java.io.BufferedInputStream;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.util.ArrayList;
35  import java.util.List;
36  import java.util.Objects;
37  
38  /**
39   * Maven pom.xml parser.
40   */
41  public class ThemisAnalysisMaven {
42      /**
43       * Project filename.
44       */
45      public static final String POM = "pom.xml";
46  
47      /**
48       * Document name.
49       */
50      private static final String DOC_NAME = "project";
51  
52      /**
53       * Parent element.
54       */
55      private static final String EL_PARENT = "parent";
56  
57      /**
58       * Modules element.
59       */
60      private static final String EL_MODULES = "modules";
61  
62      /**
63       * Module element.
64       */
65      private static final String EL_MODULE = "module";
66  
67      /**
68       * Dependencies element.
69       */
70      private static final String EL_DEPENDENCIES = "dependencies";
71  
72      /**
73       * Dependency element.
74       */
75      private static final String EL_DEPENDENCY = "dependency";
76  
77      /**
78       * The Id.
79       */
80      private final ThemisAnalysisMavenId theId;
81  
82      /**
83       * The modules.
84       */
85      private final List<String> theModules;
86  
87      /**
88       * The dependencies.
89       */
90      private final List<ThemisAnalysisMavenId> theDependencies;
91  
92      /**
93       * Constructor.
94       *
95       * @param pInputStream the input stream to read
96       * @throws OceanusException on error
97       */
98      public ThemisAnalysisMaven(final InputStream pInputStream) throws OceanusException {
99          /* Create the module list */
100         theModules = new ArrayList<>();
101         theDependencies = new ArrayList<>();
102 
103         /* Protect against exceptions */
104         try (BufferedInputStream myInBuffer = new BufferedInputStream(pInputStream)) {
105             final DocumentBuilderFactory myFactory = DocumentBuilderFactory.newInstance();
106             myFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
107             myFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
108             myFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
109             final DocumentBuilder myBuilder = myFactory.newDocumentBuilder();
110 
111             /* Build the document from the input stream */
112             final Document myDocument = myBuilder.parse(myInBuffer);
113             theId = parseProjectFile(myDocument);
114 
115             /* Handle exceptions */
116         } catch (IOException
117                  | ParserConfigurationException
118                  | SAXException e) {
119             throw new ThemisIOException("Exception accessing Pom file", e);
120         }
121     }
122 
123     @Override
124     public String toString() {
125         return theId.toString();
126     }
127 
128     /**
129      * Obtain the list of modules.
130      *
131      * @return the list
132      */
133     public ThemisAnalysisMavenId getMavenId() {
134         return theId;
135     }
136 
137     /**
138      * Obtain the list of modules.
139      *
140      * @return the list
141      */
142     public List<String> getModules() {
143         return theModules;
144     }
145 
146     /**
147      * Parse the project file.
148      *
149      * @param pDocument the document
150      * @return the MavenId
151      * @throws OceanusException on error
152      */
153     public ThemisAnalysisMavenId parseProjectFile(final Document pDocument) throws OceanusException {
154         /* Access the document element */
155         final Element myDoc = pDocument.getDocumentElement();
156 
157         /* Check that the document name is correct */
158         if (!Objects.equals(myDoc.getNodeName(), DOC_NAME)) {
159             throw new ThemisDataException("Invalid document type");
160         }
161 
162         /* Obtain parent definition if any */
163         final Element myParentEl = getElement(myDoc, EL_PARENT);
164         final ThemisAnalysisMavenId myParent = myParentEl == null
165                 ? null
166                 : new ThemisAnalysisMavenId(myParentEl);
167 
168         /* Obtain our mavenId */
169         final ThemisAnalysisMavenId myId = new ThemisAnalysisMavenId(myDoc, myParent);
170 
171         /* Process modules */
172         final Element myModules = getElement(myDoc, EL_MODULES);
173         processModules(myModules);
174 
175         /* Process dependencies */
176         final Element myDependencies = getElement(myDoc, EL_DEPENDENCIES);
177         processDependencies(myDependencies, myId);
178 
179         /* Return the Id */
180         return myId;
181     }
182 
183     /**
184      * Obtain element value.
185      *
186      * @param pElement the element
187      * @param pValue   the value name
188      * @return the value
189      */
190     static String getElementValue(final Element pElement,
191                                   final String pValue) {
192         /* Return null if no element */
193         if (pElement == null) {
194             return null;
195         }
196 
197         /* Loop through the children */
198         for (Node myChild = pElement.getFirstChild();
199              myChild != null;
200              myChild = myChild.getNextSibling()) {
201             /* Return result if we have a match */
202             if (myChild instanceof Element
203                     && pValue.equals(myChild.getNodeName())) {
204                 return myChild.getTextContent();
205             }
206         }
207 
208         /* Not found */
209         return null;
210     }
211 
212     /**
213      * Obtain element value.
214      *
215      * @param pElement the element
216      * @param pValue   the value name
217      * @return the value
218      */
219     static Element getElement(final Element pElement,
220                               final String pValue) {
221         /* Return null if no element */
222         if (pElement == null) {
223             return null;
224         }
225 
226         /* Loop through the children */
227         for (Node myChild = pElement.getFirstChild();
228              myChild != null;
229              myChild = myChild.getNextSibling()) {
230             /* Return result if we have a match */
231             if (myChild instanceof Element myElement
232                     && pValue.equals(myChild.getNodeName())) {
233                 return myElement;
234             }
235         }
236 
237         /* Not found */
238         return null;
239     }
240 
241     /**
242      * Process modules.
243      *
244      * @param pModules the modules
245      */
246     private void processModules(final Element pModules) {
247         /* Return if no element */
248         if (pModules == null) {
249             return;
250         }
251 
252         /* Loop through the children */
253         for (Node myChild = pModules.getFirstChild();
254              myChild != null;
255              myChild = myChild.getNextSibling()) {
256             /* Return result if we have a match */
257             if (myChild instanceof Element
258                     && EL_MODULE.equals(myChild.getNodeName())) {
259                 theModules.add(myChild.getTextContent());
260             }
261         }
262     }
263 
264     /**
265      * Process dependencies.
266      *
267      * @param pDependencies the dependencies
268      * @param pParent       the parentId
269      */
270     private void processDependencies(final Element pDependencies,
271                                      final ThemisAnalysisMavenId pParent) {
272         /* Return if no element */
273         if (pDependencies == null) {
274             return;
275         }
276 
277         /* Loop through the children */
278         for (Node myChild = pDependencies.getFirstChild();
279              myChild != null;
280              myChild = myChild.getNextSibling()) {
281             /* Return result if we have a match */
282             if (myChild instanceof Element myElement
283                     && EL_DEPENDENCY.equals(myChild.getNodeName())) {
284                 theDependencies.add(new ThemisAnalysisMavenId(myElement, pParent));
285             }
286         }
287     }
288 }