1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package job;
26
27 import org.w3c.dom.Attr;
28 import org.w3c.dom.Document;
29 import org.w3c.dom.Element;
30 import org.w3c.dom.Node;
31 import org.w3c.dom.NodeList;
32 import org.xml.sax.SAXException;
33
34 import javax.xml.parsers.DocumentBuilderFactory;
35 import javax.xml.parsers.ParserConfigurationException;
36 import javax.xml.transform.OutputKeys;
37 import javax.xml.transform.TransformerFactory;
38 import javax.xml.transform.dom.DOMSource;
39 import javax.xml.transform.stream.StreamResult;
40 import javax.xml.xpath.XPath;
41 import javax.xml.xpath.XPathConstants;
42 import javax.xml.xpath.XPathExpression;
43 import javax.xml.xpath.XPathExpressionException;
44 import javax.xml.xpath.XPathFactory;
45 import java.io.File;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.StringWriter;
49 import java.net.URI;
50 import java.net.URL;
51 import java.nio.file.Files;
52 import java.nio.file.Path;
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.Optional;
58 import java.util.function.BiConsumer;
59 import java.util.function.Consumer;
60 import java.util.function.Function;
61 import java.util.stream.Stream;
62
63 public class XMLNode {
64 Element element;
65 List<XMLNode> children = new ArrayList<>();
66 Map<String, String> attrMap = new HashMap<>();
67
68 public static class AbstractXMLBuilder<T extends AbstractXMLBuilder<T>> {
69 final public Element element;
70
71 @SuppressWarnings("unchecked")
72 public T self() {
73 return (T) this;
74 }
75
76 public T attr(String name, String value) {
77 element.setAttribute(name, value);
78 return self();
79 }
80
81 public T attr(URI uri, String name, String value) {
82 element.setAttributeNS(uri.toString(), name, value);
83 return self();
84 }
85
86 public T element(String name, Function<Element, T> factory, Consumer<T> xmlBuilderConsumer) {
87 var node = element.getOwnerDocument().createElement(name);
88 element.appendChild(node);
89 var builder = factory.apply(node);
90 xmlBuilderConsumer.accept(builder);
91 return self();
92 }
93
94 public T element(
95 URI uri, String name, Function<Element, T> factory, Consumer<T> xmlBuilderConsumer) {
96 var node = element.getOwnerDocument().createElementNS(uri.toString(), name);
97 element.appendChild(node);
98 var builder = factory.apply(node);
99 xmlBuilderConsumer.accept(builder);
100 return self();
101 }
102
103 AbstractXMLBuilder(Element element) {
104 this.element = element;
105 }
106
107 public T text(String thisText) {
108 var node = element.getOwnerDocument().createTextNode(thisText);
109 element.appendChild(node);
110 return self();
111 }
112
113 public T comment(String thisComment) {
114 var node = element.getOwnerDocument().createComment(thisComment);
115 element.appendChild(node);
116 return self();
117 }
118
119 <L> T forEach(List<L> list, BiConsumer<T, L> biConsumer) {
120 list.forEach(l -> biConsumer.accept(self(), l));
121 return self();
122 }
123
124 <L> T forEach(Stream<L> stream, BiConsumer<T, L> biConsumer) {
125 stream.forEach(l -> biConsumer.accept(self(), l));
126 return self();
127 }
128
129 <L> T forEach(Stream<L> stream, Consumer<L> consumer) {
130 stream.forEach(consumer);
131 return self();
132 }
133
134 protected T then(Consumer<T> xmlBuilderConsumer) {
135 xmlBuilderConsumer.accept(self());
136 return self();
137 }
138 }
139
140 public static class PomXmlBuilder extends AbstractXMLBuilder<PomXmlBuilder> {
141 PomXmlBuilder(Element element) {
142 super(element);
143 }
144
145 public PomXmlBuilder element(String name, Consumer<PomXmlBuilder> xmlBuilderConsumer) {
146 return element(name, PomXmlBuilder::new, xmlBuilderConsumer);
147 }
148
149 public PomXmlBuilder element(URI uri, String name, Consumer<PomXmlBuilder> xmlBuilderConsumer) {
150 return element(uri, name, PomXmlBuilder::new, xmlBuilderConsumer);
151 }
152
153 public PomXmlBuilder modelVersion(String s) {
154 return element("modelVersion", $ -> $.text(s));
155 }
156
157 public PomXmlBuilder pom(String groupId, String artifactId, String version) {
158 return modelVersion("4.0.0").packaging("pom").ref(groupId, artifactId, version);
159 }
160
161 public PomXmlBuilder jar(String groupId, String artifactId, String version) {
162 return modelVersion("4.0.0").packaging("jar").ref(groupId, artifactId, version);
163 }
164
165 public PomXmlBuilder groupId(String s) {
166 return element("groupId", $ -> $.text(s));
167 }
168
169 public PomXmlBuilder artifactId(String s) {
170 return element("artifactId", $ -> $.text(s));
171 }
172
173 public PomXmlBuilder packaging(String s) {
174 return element("packaging", $ -> $.text(s));
175 }
176
177 public PomXmlBuilder version(String s) {
178 return element("version", $ -> $.text(s));
179 }
180
181 public PomXmlBuilder build(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
182 return element("build", pomXmlBuilderConsumer);
183 }
184
185 public PomXmlBuilder plugins(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
186 return element("plugins", pomXmlBuilderConsumer);
187 }
188
189 public PomXmlBuilder plugin(
190 String groupId,
191 String artifactId,
192 String version,
193 Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
194 return element(
195 "plugin", $ -> $.ref(groupId, artifactId, version).then(pomXmlBuilderConsumer));
196 }
197
198 public PomXmlBuilder antPlugin(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
199 return plugin(
200 "org.apache.maven.plugins",
201 "maven-antrun-plugin",
202 "1.8",
203 pomXmlBuilderConsumer);
204 }
205
206 public PomXmlBuilder surefirePlugin(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
207 return plugin(
208 "org.apache.maven.plugins",
209 "maven-surefire-plugin",
210 "3.1.2",
211 pomXmlBuilderConsumer);
212 }
213
214 public PomXmlBuilder compilerPlugin(
215 Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
216 return plugin(
217 "org.apache.maven.plugins",
218 "maven-compiler-plugin",
219 "3.11.0", pomXmlBuilderConsumer
220 );
221 }
222
223 public PomXmlBuilder execPlugin(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
224 return plugin("org.codehaus.mojo", "exec-maven-plugin", "3.1.0", pomXmlBuilderConsumer);
225 }
226
227
228 public PomXmlBuilder plugin(
229 String groupId, String artifactId, Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
230 return element("plugin", $ -> $.groupIdArtifactId(groupId, artifactId).then(pomXmlBuilderConsumer));
231 }
232
233 public PomXmlBuilder plugin(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
234 return element("plugin", pomXmlBuilderConsumer);
235 }
236
237 public PomXmlBuilder parent(String groupId, String artifactId, String version) {
238 return parent(parent -> parent.ref(groupId, artifactId, version));
239 }
240
241 public PomXmlBuilder parent(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
242 return element("parent", pomXmlBuilderConsumer);
243 }
244
245 public PomXmlBuilder pluginManagement(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
246 return element("pluginManagement", pomXmlBuilderConsumer);
247 }
248
249 public PomXmlBuilder file(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
250 return element("file", pomXmlBuilderConsumer);
251 }
252
253 public PomXmlBuilder activation(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
254 return element("activation", pomXmlBuilderConsumer);
255 }
256
257 public PomXmlBuilder profiles(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
258 return element("profiles", pomXmlBuilderConsumer);
259 }
260
261 public PomXmlBuilder profile(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
262 return element("profile", pomXmlBuilderConsumer);
263 }
264
265 public PomXmlBuilder arguments(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
266 return element("arguments", pomXmlBuilderConsumer);
267 }
268
269 public PomXmlBuilder executions(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
270 return element("executions", pomXmlBuilderConsumer);
271 }
272
273 public PomXmlBuilder execution(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
274 return element("execution", pomXmlBuilderConsumer);
275 }
276
277 public PomXmlBuilder execIdPhaseConf(
278 String id, String phase, Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
279 return execution(execution -> execution.id(id).phase(phase).goals(gs -> gs.goal("exec")).configuration(pomXmlBuilderConsumer));
280 }
281
282 public PomXmlBuilder exec(
283 String phase, String executable, Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
284 return execIdPhaseConf(
285 executable + "-" + phase,
286 phase,
287 conf -> conf.executable(executable).arguments(pomXmlBuilderConsumer));
288 }
289
290 public PomXmlBuilder cmake(
291 String id, String phase, Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
292 return execIdPhaseConf(
293 id, phase, conf -> conf.executable("cmake").arguments(pomXmlBuilderConsumer));
294 }
295
296 public PomXmlBuilder cmake(String id, String phase, String... args) {
297 return execIdPhaseConf(
298 id,
299 phase,
300 conf ->
301 conf.executable("cmake")
302 .arguments(arguments -> arguments.forEach(Stream.of(args), arguments::argument)));
303 }
304
305 public PomXmlBuilder jextract(String id, String phase, String... args) {
306 return execIdPhaseConf(
307 id,
308 phase,
309 conf ->
310 conf.executable("jextract")
311 .arguments(arguments -> arguments.forEach(Stream.of(args), arguments::argument)));
312 }
313
314 public PomXmlBuilder ant(
315 String id, String phase, String goal, Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
316 return execution(execution -> execution
317 .id(id)
318 .phase(phase)
319 .goals(gs -> gs.goal(goal))
320 .configuration(configuration -> configuration.target(pomXmlBuilderConsumer)));
321 }
322
323 public PomXmlBuilder goals(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
324 return element("goals", pomXmlBuilderConsumer);
325 }
326
327 public PomXmlBuilder target(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
328 return element("target", pomXmlBuilderConsumer);
329 }
330
331 public PomXmlBuilder configuration(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
332 return element("configuration", pomXmlBuilderConsumer);
333 }
334
335 public PomXmlBuilder compilerArgs(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
336 return element("compilerArgs", pomXmlBuilderConsumer);
337 }
338
339 public PomXmlBuilder compilerArgs(String... args) {
340 return element("compilerArgs", $ -> $.forEach(Stream.of(args), $::arg));
341 }
342
343 public PomXmlBuilder properties(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
344 return element("properties", pomXmlBuilderConsumer);
345 }
346
347 public PomXmlBuilder dependencies(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
348 return element("dependencies", pomXmlBuilderConsumer);
349 }
350
351 public PomXmlBuilder dependsOn(String groupId, String artifactId, String version) {
352 return element("dependencies", $ -> $.dependency(groupId, artifactId, version));
353 }
354
355 public PomXmlBuilder dependsOn(String groupId, String artifactId, String version, String phase) {
356 return element("dependencies", $ -> $.dependency(groupId, artifactId, version, phase));
357 }
358
359 public PomXmlBuilder dependency(String groupId, String artifactId, String version) {
360 return dependency($ -> $.ref(groupId, artifactId, version));
361 }
362
363 public PomXmlBuilder dependency(
364 String groupId, String artifactId, String version, String scope) {
365 return dependency($ -> $.ref(groupId, artifactId, version).scope(scope));
366 }
367
368 public PomXmlBuilder dependency(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
369 return element("dependency", pomXmlBuilderConsumer);
370 }
371
372 public PomXmlBuilder modules(Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
373 return element("modules", pomXmlBuilderConsumer);
374 }
375
376 public PomXmlBuilder modules(List<String> modules) {
377 return element("modules", $ -> $.forEach(modules.stream(), $::module));
378 }
379
380 public PomXmlBuilder modules(String... modules) {
381 return modules(List.of(modules));
382 }
383
384 public PomXmlBuilder module(String name) {
385 return element("module", $ -> $.text(name));
386 }
387
388 public PomXmlBuilder property(String name, String value) {
389 return element(name, $ -> $.text(value));
390 }
391
392 public PomXmlBuilder antproperty(String name, String value) {
393 return element("property", $ -> $.attr("name", name).attr("value", value));
394 }
395
396 public PomXmlBuilder scope(String s) {
397 return element("scope", $ -> $.text(s));
398 }
399
400 public PomXmlBuilder phase(String s) {
401 return element("phase", $ -> $.text(s));
402 }
403
404 public PomXmlBuilder argument(String s) {
405 return element("argument", $ -> $.text(s));
406 }
407
408 public PomXmlBuilder goal(String s) {
409 return element("goal", $ -> $.text(s));
410 }
411
412 public PomXmlBuilder copy(String file, String toDir) {
413 return element("copy", $ -> $.attr("file", file).attr("toDir", toDir));
414 }
415
416 public PomXmlBuilder antjar(String basedir, String include, String destfile) {
417 return element("jar", $ -> $.attr("basedir", basedir).attr("includes", include + "/**").attr("destfile", destfile));
418 }
419
420 public PomXmlBuilder echo(String message) {
421 return element("echo", $ -> $.attr("message", message));
422 }
423
424 public PomXmlBuilder echo(String filename, String message) {
425 return element("echo", $ -> $.attr("message", message).attr("file", filename));
426 }
427
428 public PomXmlBuilder mkdir(String dirName) {
429 return element("mkdir", $ -> $.attr("dir", dirName));
430 }
431
432 public PomXmlBuilder groupIdArtifactId(String groupId, String artifactId) {
433 return groupId(groupId).artifactId(artifactId);
434 }
435
436 public PomXmlBuilder ref(String groupId, String artifactId, String version) {
437 return groupIdArtifactId(groupId, artifactId).version(version);
438 }
439
440 public PomXmlBuilder skip(String string) {
441 return element("skip", $ -> $.text(string));
442 }
443
444 public PomXmlBuilder id(String s) {
445 return element("id", $ -> $.text(s));
446 }
447
448 public PomXmlBuilder arg(String s) {
449 return element("arg", $ -> $.text(s));
450 }
451
452 public PomXmlBuilder argLine(String s) {
453 return element("argLine", $ -> $.text(s));
454 }
455
456 public PomXmlBuilder source(String s) {
457 return element("source", $ -> $.text(s));
458 }
459
460 public PomXmlBuilder target(String s) {
461 return element("target", $ -> $.text(s));
462 }
463
464 public PomXmlBuilder showWarnings(String s) {
465 return element("showWarnings", $ -> $.text(s));
466 }
467
468 public PomXmlBuilder showDeprecation(String s) {
469 return element("showDeprecation", $ -> $.text(s));
470 }
471
472 public PomXmlBuilder failOnError(String s) {
473 return element("failOnError", $ -> $.text(s));
474 }
475
476 public PomXmlBuilder exists(String s) {
477 return element("exists", $ -> $.text(s));
478 }
479
480 public PomXmlBuilder activeByDefault(String s) {
481 return element("activeByDefault", $ -> $.text(s));
482 }
483
484 public PomXmlBuilder executable(String s) {
485 return element("executable", $ -> $.text(s));
486 }
487
488 public PomXmlBuilder workingDirectory(String s) {
489 return element("workingDirectory", $ -> $.text(s));
490 }
491 }
492
493 public static class ImlBuilder extends AbstractXMLBuilder<ImlBuilder> {
494
495 ImlBuilder(Element element) {
496 super(element);
497 }
498
499 public ImlBuilder element(String name, Consumer<ImlBuilder> xmlBuilderConsumer) {
500 return element(name, ImlBuilder::new, xmlBuilderConsumer);
501 }
502
503 public ImlBuilder element(URI uri, String name, Consumer<ImlBuilder> xmlBuilderConsumer) {
504 return element(uri, name, ImlBuilder::new, xmlBuilderConsumer);
505 }
506
507 public ImlBuilder modelVersion(String s) {
508 return element("modelVersion", $ -> $.text(s));
509 }
510
511 public ImlBuilder groupId(String s) {
512 return element("groupId", $ -> $.text(s));
513 }
514
515 public ImlBuilder artifactId(String s) {
516 return element("artifactId", $ -> $.text(s));
517 }
518
519 public ImlBuilder packaging(String s) {
520 return element("packaging", $ -> $.text(s));
521 }
522
523 public ImlBuilder version(String s) {
524 return element("version", $ -> $.text(s));
525 }
526
527 public ImlBuilder build(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
528 return element("build", pomXmlBuilderConsumer);
529 }
530
531 public ImlBuilder plugins(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
532 return element("plugins", pomXmlBuilderConsumer);
533 }
534
535 public ImlBuilder plugin(
536 String groupId,
537 String artifactId,
538 String version,
539 Consumer<ImlBuilder> pomXmlBuilderConsumer) {
540 return element(
541 "plugin",
542 $ ->
543 $.groupIdArtifactIdVersion(groupId, artifactId, version).then(pomXmlBuilderConsumer));
544 }
545
546 public ImlBuilder plugin(
547 String groupId, String artifactId, Consumer<ImlBuilder> pomXmlBuilderConsumer) {
548 return element(
549 "plugin", $ -> $.groupIdArtifactId(groupId, artifactId).then(pomXmlBuilderConsumer));
550 }
551
552 public ImlBuilder plugin(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
553 return element("plugin", pomXmlBuilderConsumer);
554 }
555
556 public ImlBuilder parent(String groupId, String artifactId, String version) {
557 return parent(parent -> parent.groupIdArtifactIdVersion(groupId, artifactId, version));
558 }
559
560 public ImlBuilder parent(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
561 return element("parent", pomXmlBuilderConsumer);
562 }
563
564 public ImlBuilder pluginManagement(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
565 return element("pluginManagement", pomXmlBuilderConsumer);
566 }
567
568 public ImlBuilder file(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
569 return element("file", pomXmlBuilderConsumer);
570 }
571
572 public ImlBuilder activation(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
573 return element("activation", pomXmlBuilderConsumer);
574 }
575
576 public ImlBuilder profiles(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
577 return element("profiles", pomXmlBuilderConsumer);
578 }
579
580 public ImlBuilder profile(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
581 return element("profile", pomXmlBuilderConsumer);
582 }
583
584 public ImlBuilder arguments(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
585 return element("arguments", pomXmlBuilderConsumer);
586 }
587
588 public ImlBuilder executions(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
589 return element("executions", pomXmlBuilderConsumer);
590 }
591
592 public ImlBuilder execution(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
593 return element("execution", pomXmlBuilderConsumer);
594 }
595
596 public ImlBuilder goals(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
597 return element("goals", pomXmlBuilderConsumer);
598 }
599
600 public ImlBuilder target(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
601 return element("target", pomXmlBuilderConsumer);
602 }
603
604 public ImlBuilder configuration(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
605 return element("configuration", pomXmlBuilderConsumer);
606 }
607
608 public ImlBuilder compilerArgs(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
609 return element("compilerArgs", pomXmlBuilderConsumer);
610 }
611
612 public ImlBuilder properties(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
613 return element("properties", pomXmlBuilderConsumer);
614 }
615
616 public ImlBuilder dependencies(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
617 return element("dependencies", pomXmlBuilderConsumer);
618 }
619
620 public ImlBuilder dependency(String groupId, String artifactId, String version) {
621 return dependency($ -> $.groupIdArtifactIdVersion(groupId, artifactId, version));
622 }
623
624 public ImlBuilder dependency(String groupId, String artifactId, String version, String scope) {
625 return dependency($ -> $.groupIdArtifactIdVersion(groupId, artifactId, version).scope(scope));
626 }
627
628 public ImlBuilder dependency(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
629 return element("dependency", pomXmlBuilderConsumer);
630 }
631
632 public ImlBuilder modules(Consumer<ImlBuilder> pomXmlBuilderConsumer) {
633 return element("modules", pomXmlBuilderConsumer);
634 }
635
636 public ImlBuilder module(String name) {
637 return element("module", $ -> $.text(name));
638 }
639
640 public ImlBuilder property(String name, String value) {
641 return element(name, $ -> $.text(value));
642 }
643
644 public ImlBuilder scope(String s) {
645 return element("scope", $ -> $.text(s));
646 }
647
648 public ImlBuilder phase(String s) {
649 return element("phase", $ -> $.text(s));
650 }
651
652 public ImlBuilder argument(String s) {
653 return element("argument", $ -> $.text(s));
654 }
655
656 public ImlBuilder goal(String s) {
657 return element("goal", $ -> $.text(s));
658 }
659
660 public ImlBuilder copy(String file, String toDir) {
661 return element("copy", $ -> $.attr("file", file).attr("toDir", toDir));
662 }
663
664 public ImlBuilder groupIdArtifactId(String groupId, String artifactId) {
665 return groupId(groupId).artifactId(artifactId);
666 }
667
668 public ImlBuilder groupIdArtifactIdVersion(String groupId, String artifactId, String version) {
669 return groupIdArtifactId(groupId, artifactId).version(version);
670 }
671
672 public ImlBuilder skip(String string) {
673 return element("skip", $ -> $.text(string));
674 }
675
676 public ImlBuilder id(String s) {
677 return element("id", $ -> $.text(s));
678 }
679
680 public ImlBuilder arg(String s) {
681 return element("arg", $ -> $.text(s));
682 }
683
684 public ImlBuilder argLine(String s) {
685 return element("argLine", $ -> $.text(s));
686 }
687
688 public ImlBuilder source(String s) {
689 return element("source", $ -> $.text(s));
690 }
691
692 public ImlBuilder target(String s) {
693 return element("target", $ -> $.text(s));
694 }
695
696 public ImlBuilder showWarnings(String s) {
697 return element("showWarnings", $ -> $.text(s));
698 }
699
700 public ImlBuilder showDeprecation(String s) {
701 return element("showDeprecation", $ -> $.text(s));
702 }
703
704 public ImlBuilder failOnError(String s) {
705 return element("failOnError", $ -> $.text(s));
706 }
707
708 public ImlBuilder exists(String s) {
709 return element("exists", $ -> $.text(s));
710 }
711
712 public ImlBuilder activeByDefault(String s) {
713 return element("activeByDefault", $ -> $.text(s));
714 }
715
716 public ImlBuilder executable(String s) {
717 return element("executable", $ -> $.text(s));
718 }
719 }
720
721 public static class XMLBuilder extends AbstractXMLBuilder<XMLBuilder> {
722 XMLBuilder(Element element) {
723 super(element);
724 }
725
726 public XMLBuilder element(String name, Consumer<XMLBuilder> xmlBuilderConsumer) {
727 return element(name, XMLBuilder::new, xmlBuilderConsumer);
728 }
729
730 public XMLBuilder element(URI uri, String name, Consumer<XMLBuilder> xmlBuilderConsumer) {
731 return element(uri, name, XMLBuilder::new, xmlBuilderConsumer);
732 }
733 }
734
735 static XMLNode create(String nodeName, Consumer<XMLBuilder> xmlBuilderConsumer) {
736
737 try {
738 var doc =
739 DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
740 var element = doc.createElement(nodeName);
741 doc.appendChild(element);
742 XMLBuilder xmlBuilder = new XMLBuilder(element);
743 xmlBuilderConsumer.accept(xmlBuilder);
744 return new XMLNode(element);
745 } catch (ParserConfigurationException e) {
746 throw new RuntimeException(e);
747 }
748 }
749
750 static XMLNode createIml(String commentText, Consumer<ImlBuilder> imlBuilderConsumer) {
751 try {
752 var doc =
753 DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
754 var uri1 = URI.create("http://maven.apache.org/POM/4.0.0");
755 var uri2 = URI.create("http://www.w3.org/2001/XMLSchema-instance");
756 var uri3 = URI.create("http://maven.apache.org/xsd/maven-4.0.0.xsd");
757 var comment = doc.createComment(commentText);
758 doc.appendChild(comment);
759 var element = doc.createElementNS(uri1.toString(), "project");
760 doc.appendChild(element);
761 element.setAttributeNS(uri2.toString(), "xsi:schemaLocation", uri1 + " " + uri3);
762 ImlBuilder imlBuilder = new ImlBuilder(element);
763 imlBuilderConsumer.accept(imlBuilder);
764 return new XMLNode(element);
765 } catch (ParserConfigurationException e) {
766 throw new RuntimeException(e);
767 }
768 }
769
770 public static XMLNode createPom(
771 String commentText, Consumer<PomXmlBuilder> pomXmlBuilderConsumer) {
772 try {
773 var doc =
774 DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
775
776 var uri1 = URI.create("http://maven.apache.org/POM/4.0.0");
777 var uri2 = URI.create("http://www.w3.org/2001/XMLSchema-instance");
778 var uri3 = URI.create("http://maven.apache.org/xsd/maven-4.0.0.xsd");
779 var comment = doc.createComment(commentText);
780 doc.appendChild(comment);
781 var element = doc.createElementNS(uri1.toString(), "project");
782 doc.appendChild(element);
783 element.setAttributeNS(uri2.toString(), "xsi:schemaLocation", uri1 + " " + uri3);
784 PomXmlBuilder pomXmlBuilder = new PomXmlBuilder(element);
785 pomXmlBuilderConsumer.accept(pomXmlBuilder);
786 return new XMLNode(element);
787 } catch (ParserConfigurationException e) {
788 throw new RuntimeException(e);
789 }
790 }
791
792 static XMLNode create(URI uri, String nodeName, Consumer<XMLBuilder> xmlBuilderConsumer) {
793 try {
794 var doc =
795 DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
796 var element = doc.createElementNS(uri.toString(), nodeName);
797 doc.appendChild(element);
798 XMLBuilder xmlBuilder = new XMLBuilder(element);
799 xmlBuilderConsumer.accept(xmlBuilder);
800 return new XMLNode(element);
801 } catch (ParserConfigurationException e) {
802 throw new RuntimeException(e);
803 }
804 }
805
806 XMLNode(Element element) {
807 this.element = element;
808 this.element.normalize();
809 NodeList nodeList = element.getChildNodes();
810 for (int i = 0; i < nodeList.getLength(); i++) {
811 if (nodeList.item(i) instanceof Element e) {
812 this.children.add(new XMLNode(e));
813 }
814 }
815 for (int i = 0; i < element.getAttributes().getLength(); i++) {
816 if (element.getAttributes().item(i) instanceof Attr attr) {
817 this.attrMap.put(attr.getName(), attr.getValue());
818 }
819 }
820 }
821
822 public boolean hasAttr(String name) {
823 return attrMap.containsKey(name);
824 }
825
826 public String attr(String name) {
827 return attrMap.get(name);
828 }
829
830 static Document parse(InputStream is) {
831 try {
832 return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
833 } catch (ParserConfigurationException | SAXException | IOException e) {
834 throw new RuntimeException(e);
835 }
836 }
837
838 static Document parse(Path path) {
839 try {
840 return parse(Files.newInputStream(path));
841 } catch (IOException e) {
842 throw new RuntimeException(e);
843 }
844 }
845
846 XMLNode(Path path) {
847 this(parse(path).getDocumentElement());
848 }
849
850 XMLNode(File file) {
851 this(parse(file.toPath()).getDocumentElement());
852 }
853
854 XMLNode(URL url) throws Throwable {
855 this(parse(url.openStream()).getDocumentElement());
856 }
857
858 void write(StreamResult streamResult) throws Throwable {
859 var transformer = TransformerFactory.newInstance().newTransformer();
860 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
861 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
862 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
863 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
864 transformer.transform(new DOMSource(element.getOwnerDocument()), streamResult);
865 }
866
867 void write(File file) {
868 try {
869 write(new StreamResult(file));
870 } catch (Throwable t) {
871 throw new RuntimeException(t);
872 }
873 }
874
875 public void write(Path xmlFile) {
876 try {
877 write(new StreamResult(xmlFile.toFile()));
878 } catch (Throwable t) {
879 throw new RuntimeException(t);
880 }
881 }
882
883 @Override
884 public String toString() {
885 var stringWriter = new StringWriter();
886 try {
887 var transformer = TransformerFactory.newInstance().newTransformer();
888 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
889 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
890 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
891 transformer.transform(new DOMSource(element), new StreamResult(stringWriter));
892 return stringWriter.toString();
893 } catch (Throwable e) {
894 throw new RuntimeException(e);
895 }
896 }
897
898 XPathExpression xpath(String expression) {
899 XPath xpath = XPathFactory.newInstance().newXPath();
900 try {
901 return xpath.compile(expression);
902 } catch (XPathExpressionException e) {
903 throw new RuntimeException(e);
904 }
905 }
906
907 Node node(XPathExpression xPathExpression) {
908 try {
909 return (Node) xPathExpression.evaluate(this.element, XPathConstants.NODE);
910 } catch (XPathExpressionException e) {
911 throw new RuntimeException(e);
912 }
913 }
914
915 Optional<Node> optionalNode(XPathExpression xPathExpression) {
916 var nodes = nodes(xPathExpression).toList();
917 return switch (nodes.size()) {
918 case 0 -> Optional.empty();
919 case 1 -> Optional.of(nodes.getFirst());
920 default -> throw new IllegalStateException("Expected 0 or 1 but got more");
921 };
922 }
923
924 String str(XPathExpression xPathExpression) {
925 try {
926 return (String) xPathExpression.evaluate(this.element, XPathConstants.STRING);
927 } catch (XPathExpressionException e) {
928 throw new RuntimeException(e);
929 }
930 }
931
932 String xpathQueryString(String xpathString) {
933 try {
934 return (String) xpath(xpathString).evaluate(this.element, XPathConstants.STRING);
935 } catch (XPathExpressionException e) {
936 throw new RuntimeException(e);
937 }
938 }
939
940 NodeList nodeList(XPathExpression xPathExpression) {
941 try {
942 return (NodeList) xPathExpression.evaluate(this.element, XPathConstants.NODESET);
943 } catch (XPathExpressionException e) {
944 throw new RuntimeException(e);
945 }
946 }
947
948 Stream<Node> nodes(XPathExpression xPathExpression) {
949 var nodeList = nodeList(xPathExpression);
950 List<Node> nodes = new ArrayList<>();
951 for (int i = 0; i < nodeList.getLength(); i++) {
952 nodes.add(nodeList.item(i));
953 }
954 return nodes.stream();
955 }
956
957 Stream<Element> elements(XPathExpression xPathExpression) {
958 return nodes(xPathExpression)
959 .filter(n -> n instanceof Element)
960 .map(n -> (Element) n);
961 }
962
963 Stream<XMLNode> xmlNodes(XPathExpression xPathExpression) {
964 return elements(xPathExpression).map(e -> new XMLNode(e));
965 }
966 }