1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.themis.parser.proj;
18
19 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20 import io.github.tonywasher.joceanus.oceanus.base.OceanusSystem;
21 import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
22 import io.github.tonywasher.joceanus.themis.exc.ThemisIOException;
23 import io.github.tonywasher.joceanus.themis.parser.proj.ThemisMavenId.ThemisElementParser;
24 import org.w3c.dom.Document;
25 import org.w3c.dom.Element;
26 import org.w3c.dom.Node;
27 import org.xml.sax.SAXException;
28
29 import javax.xml.XMLConstants;
30 import javax.xml.parsers.DocumentBuilder;
31 import javax.xml.parsers.DocumentBuilderFactory;
32 import javax.xml.parsers.ParserConfigurationException;
33 import javax.xml.xpath.XPath;
34 import javax.xml.xpath.XPathConstants;
35 import javax.xml.xpath.XPathExpressionException;
36 import javax.xml.xpath.XPathFactory;
37 import java.io.BufferedInputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.util.ArrayList;
41 import java.util.LinkedHashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Objects;
45
46
47
48
49 public class ThemisMaven
50 implements ThemisElementParser {
51
52
53
54 public static final String POM = "pom.xml";
55
56
57
58
59 private static final String DOC_NAME = "project";
60
61
62
63
64 private static final String XPATH_PROPERTIES = "/project/properties";
65
66
67
68
69 private static final String XPATH_PARENT = "/project/parent";
70
71
72
73
74 private static final String XPATH_MODULES = "/project/modules";
75
76
77
78
79 private static final String XPATH_DEPENDENCIES = "/project/dependencies";
80
81
82
83
84 private static final String XPATH_XTRADIRS = "/project/build/plugins/plugin[artifactId='build-helper-maven-plugin']"
85 + "/executions/execution/configuration/sources";
86
87
88
89
90 private static final String EL_MODULE = "module";
91
92
93
94
95 private static final String EL_DEPENDENCY = "dependency";
96
97
98
99
100 private static final String EL_SOURCE = "source";
101
102
103
104
105 private static final String PARENT_GROUP = "${parent.project.groupId}";
106
107
108
109
110 private static final String PARENT_VERSION = "${parent.parent.version}";
111
112
113
114
115 private static final String PROJECT_GROUP = "${project.groupId}";
116
117
118
119
120 private static final String PROJECT_VERSION = "${project.version}";
121
122
123
124
125 private final XPath theXPath;
126
127
128
129
130 private final Document theDoc;
131
132
133
134
135 private final ThemisMavenId theId;
136
137
138
139
140 private final List<String> theModules;
141
142
143
144
145 private final List<ThemisMavenId> theDependencies;
146
147
148
149
150 private final List<String> theXtraDirs;
151
152
153
154
155 private final ThemisMaven theParent;
156
157
158
159
160 private final Map<String, String> theProperties;
161
162
163
164
165
166
167
168
169 ThemisMaven(final ThemisMaven pParent,
170 final InputStream pInputStream) throws OceanusException {
171
172 theParent = pParent;
173
174
175 theModules = new ArrayList<>();
176 theDependencies = new ArrayList<>();
177 theXtraDirs = new ArrayList<>();
178 theProperties = new LinkedHashMap<>();
179 theProperties.put("${javafx.platform}", OceanusSystem.determineSystem().getClassifier());
180
181
182 try (BufferedInputStream myInBuffer = new BufferedInputStream(pInputStream)) {
183 final DocumentBuilderFactory myFactory = DocumentBuilderFactory.newInstance();
184 myFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
185 myFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
186 myFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
187 final DocumentBuilder myBuilder = myFactory.newDocumentBuilder();
188
189
190 theXPath = XPathFactory.newInstance().newXPath();
191
192
193 theDoc = myBuilder.parse(myInBuffer);
194 theId = parseProjectFile();
195
196
197 } catch (IOException
198 | ParserConfigurationException
199 | SAXException e) {
200 throw new ThemisIOException("Exception accessing Pom file", e);
201 }
202 }
203
204 @Override
205 public String toString() {
206 return theId.toString();
207 }
208
209
210
211
212
213
214 public ThemisMavenId getMavenId() {
215 return theId;
216 }
217
218
219
220
221
222
223 public List<String> getModules() {
224 return theModules;
225 }
226
227
228
229
230
231
232 public List<ThemisMavenId> getDependencies() {
233 return theDependencies;
234 }
235
236
237
238
239
240
241 public List<String> getXtraDirs() {
242 return theXtraDirs;
243 }
244
245
246
247
248
249
250
251 public ThemisMavenId parseProjectFile() throws OceanusException {
252
253 final Element myDoc = theDoc.getDocumentElement();
254
255
256 if (!Objects.equals(myDoc.getNodeName(), DOC_NAME)) {
257 throw new ThemisDataException("Invalid document type");
258 }
259
260
261 processProperties();
262
263
264 final Element myParentEl = (Element) findNode(XPATH_PARENT);
265 final ThemisMavenId myParent = myParentEl == null
266 ? null
267 : new ThemisMavenId(this, myParentEl);
268 storeParentProperties(myParent);
269
270
271 final ThemisMavenId myId = new ThemisMavenId(this, myDoc, myParent);
272 storeProjectProperties(myId);
273
274
275 processModules();
276
277
278 processDependencies();
279
280
281 processXtraDirs();
282
283
284 return myId;
285 }
286
287 @Override
288 public String getElementValue(final Element pElement,
289 final String pValue) {
290
291 if (pElement == null) {
292 return null;
293 }
294
295
296 for (Node myChild = pElement.getFirstChild();
297 myChild != null;
298 myChild = myChild.getNextSibling()) {
299
300 if (myChild instanceof Element
301 && pValue.equals(myChild.getNodeName())) {
302 return replaceProperty(myChild.getTextContent());
303 }
304 }
305
306
307 return null;
308 }
309
310
311
312
313
314
315
316
317 private Node findNode(final String pPath) throws OceanusException {
318
319 try {
320 return (Node) theXPath.compile(pPath).evaluate(theDoc, XPathConstants.NODE);
321 } catch (XPathExpressionException e) {
322 throw new ThemisDataException("Exception locating XPath: " + pPath, e);
323 }
324 }
325
326
327
328
329
330
331 private void processProperties() throws OceanusException {
332
333 final Node myProps = findNode(XPATH_PROPERTIES);
334 if (myProps != null) {
335 for (Node myNode = myProps.getFirstChild(); myNode != null; myNode = myNode.getNextSibling()) {
336 if (myNode instanceof Element myElement) {
337 theProperties.put("${" + myElement.getNodeName() + "}", myElement.getTextContent());
338 }
339 }
340 }
341 }
342
343
344
345
346
347
348 private void storeParentProperties(final ThemisMavenId pParent) {
349
350 theProperties.put(PARENT_GROUP, pParent == null ? null : pParent.getGroupId());
351 theProperties.put(PARENT_VERSION, pParent == null ? null : pParent.getVersion());
352 }
353
354
355
356
357
358
359 private void storeProjectProperties(final ThemisMavenId pProject) {
360
361 String myGroupId = pProject.getGroupId();
362 myGroupId = myGroupId != null ? myGroupId : theProperties.get(PARENT_GROUP);
363
364
365 String myVersion = pProject.getVersion();
366 myVersion = myVersion != null ? myVersion : theProperties.get(PARENT_VERSION);
367
368
369 theProperties.put(PROJECT_GROUP, myGroupId);
370 theProperties.put(PROJECT_VERSION, myVersion);
371 }
372
373
374
375
376
377
378 private void processModules() throws OceanusException {
379
380 final Node myModules = findNode(XPATH_MODULES);
381 if (myModules != null) {
382
383 for (Node myChild = myModules.getFirstChild();
384 myChild != null;
385 myChild = myChild.getNextSibling()) {
386
387 if (myChild instanceof Element
388 && EL_MODULE.equals(myChild.getNodeName())) {
389 theModules.add(myChild.getTextContent());
390 }
391 }
392 }
393 }
394
395
396
397
398
399
400 private void processDependencies() throws OceanusException {
401
402 final Node myDependencies = findNode(XPATH_DEPENDENCIES);
403 if (myDependencies != null) {
404
405 for (Node myChild = myDependencies.getFirstChild();
406 myChild != null;
407 myChild = myChild.getNextSibling()) {
408
409 if (myChild instanceof Element myElement
410 && EL_DEPENDENCY.equals(myChild.getNodeName())) {
411 final ThemisMavenId myId = new ThemisMavenId(this, myElement);
412 if (!myId.isSkippable()) {
413 theDependencies.add(myId);
414 }
415 }
416 }
417 }
418 }
419
420
421
422
423
424
425 private void processXtraDirs() throws OceanusException {
426
427 final Node myXtraDirs = findNode(XPATH_XTRADIRS);
428 if (myXtraDirs != null) {
429
430 for (Node myChild = myXtraDirs.getFirstChild();
431 myChild != null;
432 myChild = myChild.getNextSibling()) {
433
434 if (myChild instanceof Element
435 && EL_SOURCE.equals(myChild.getNodeName())) {
436 theXtraDirs.add(myChild.getTextContent());
437 }
438 }
439 }
440 }
441
442
443
444
445
446
447
448 private String replaceProperty(final String pValue) {
449 String myResult = pValue;
450 for (Map.Entry<String, String> myEntry : theProperties.entrySet()) {
451 if (myResult.contains(myEntry.getKey())) {
452 myResult = myResult.replace(myEntry.getKey(), myEntry.getValue());
453 }
454 }
455 return theParent != null ? theParent.replaceProperty(myResult) : myResult;
456 }
457 }