Skip to content

Commit 0299f54

Browse files
Add list-files task to stager-maven-plugin (#1006)
- Add a variant of FileUtils.walk that can follow symlinks (with FileVisitOption) - Fix StagingElementFactory/ConfigReader to preserve order
1 parent 5ec56cb commit 0299f54

File tree

21 files changed

+701
-302
lines changed

21 files changed

+701
-302
lines changed

common/common/src/main/java/io/helidon/build/common/FileUtils.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2019, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
import java.nio.file.FileSystem;
2828
import java.nio.file.FileSystemAlreadyExistsException;
2929
import java.nio.file.FileSystems;
30+
import java.nio.file.FileVisitOption;
3031
import java.nio.file.FileVisitResult;
3132
import java.nio.file.FileVisitor;
3233
import java.nio.file.Files;
@@ -271,11 +272,28 @@ public static List<Path> list(Path directory, int maxDepth) {
271272
* @param directory The directory
272273
* @param predicate predicate used to filter files and directories
273274
* @return The normalized, absolute file paths.
275+
* @see Files#walkFileTree(java.nio.file.Path, java.util.Set, int, java.nio.file.FileVisitor)
274276
*/
275277
public static List<Path> walk(Path directory, BiPredicate<Path, BasicFileAttributes> predicate) {
278+
return walk(directory, Set.of(), predicate);
279+
}
280+
281+
/**
282+
* Walk the directory and return the files that match the given predicate.
283+
* If a directory is filtered out by the predicate its subtree is skipped.
284+
*
285+
* @param directory The directory
286+
* @param options options to configure the traversal
287+
* @param predicate predicate used to filter files and directories
288+
* @return The normalized, absolute file paths.
289+
* @see Files#walkFileTree(java.nio.file.Path, java.util.Set, int, java.nio.file.FileVisitor)
290+
*/
291+
public static List<Path> walk(Path directory,
292+
Set<FileVisitOption> options,
293+
BiPredicate<Path, BasicFileAttributes> predicate) {
276294
try {
277295
List<Path> files = new ArrayList<>();
278-
Files.walkFileTree(directory, new FileVisitor<>() {
296+
Files.walkFileTree(directory, options, Integer.MAX_VALUE, new FileVisitor<>() {
279297
@Override
280298
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
281299
if (predicate.test(dir, attrs)) {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.helidon.build.common;
17+
18+
import java.util.Set;
19+
import java.util.regex.Pattern;
20+
import java.util.stream.Collectors;
21+
22+
/**
23+
* {@link Pattern} utility.
24+
*/
25+
public final class Patterns {
26+
27+
private static final Pattern NAMED_GROUP_PATTERN = Pattern.compile("\\(\\?<([^!=].*?)>");
28+
29+
private Patterns() {
30+
// cannot be instanciated
31+
}
32+
33+
/**
34+
* Get the group names defined in a regular expression.
35+
*
36+
* @param regex regular expression
37+
* @return group names
38+
*/
39+
public static Set<String> groupNames(String regex) {
40+
return NAMED_GROUP_PATTERN.matcher(regex)
41+
.results()
42+
.map(r -> r.group(1))
43+
.collect(Collectors.toSet());
44+
}
45+
}

common/common/src/main/java/io/helidon/build/common/SourcePath.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2018, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,8 @@
3131
import java.util.Objects;
3232
import java.util.stream.Collectors;
3333

34+
import static io.helidon.build.common.Strings.normalizePath;
35+
3436
/**
3537
* Utility class to parse and match path segments.
3638
*/
@@ -62,7 +64,16 @@ public SourcePath(File dir, File file) {
6264
* @param file the filed contained in the directory
6365
*/
6466
public SourcePath(Path dir, Path file) {
65-
segments = parseSegments(getRelativePath(dir, file));
67+
this(dir.relativize(file));
68+
}
69+
70+
/**
71+
* Create a new {@link SourcePath} instance for the given path.
72+
*
73+
* @param path the path to use
74+
*/
75+
public SourcePath(Path path) {
76+
this(normalizePath(path));
6677
}
6778

6879
/**
@@ -95,10 +106,6 @@ public SourcePath(List<String> paths) {
95106
.toArray(String[]::new);
96107
}
97108

98-
private static String getRelativePath(Path sourceDir, Path source) {
99-
return Strings.normalizePath(sourceDir.relativize(source));
100-
}
101-
102109
/**
103110
* Parse a {@code '/'} separated path into segments. Collapses empty or {@code '.'} only segments.
104111
*

maven-plugins/stager-maven-plugin/pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright (c) 2020, 2023 Oracle and/or its affiliates.
4+
Copyright (c) 2020, 2024 Oracle and/or its affiliates.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
<packaging>maven-plugin</packaging>
3232

3333
<properties>
34+
<maven.compiler.release>17</maven.compiler.release>
3435
<spotbugs.exclude>etc/spotbugs/exclude.xml</spotbugs.exclude>
3536
</properties>
3637

maven-plugins/stager-maven-plugin/src/it/projects/file/pom.xml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!--
22
3-
Copyright (c) 2020, 2022 Oracle and/or its affiliates.
3+
Copyright (c) 2020, 2024 Oracle and/or its affiliates.
44
55
Licensed under the Apache License, Version 2.0 (the "License");
66
you may not use this file except in compliance with the License.
@@ -43,6 +43,27 @@
4343
<files>
4444
<file target="CNAME">${cname}</file>
4545
<file target="cli-data/latest">${cli.data.latest.version}</file>
46+
<file target="docs/4.0.2/index.html"/>
47+
<file target="docs/4.0.2/apidocs/index.html"/>
48+
<file target="docs/4.0.2/images/foo.svg"/>
49+
</files>
50+
<symlinks join="true">
51+
<symlink source="docs/4.0.2" target="docs/v4"/>
52+
</symlinks>
53+
<files join="true">
54+
<file target="sitemap.txt">
55+
<list-files dir=".">
56+
<includes>
57+
<include>**/docs/**/*.html</include>
58+
</includes>
59+
<excludes>
60+
<exclude>**/images/**</exclude>
61+
</excludes>
62+
<substitutions>
63+
<substitution match="^(.*)/index.html$" replace="$1" />
64+
</substitutions>
65+
</list-files>
66+
</file>
4667
</files>
4768
</directory>
4869
</directories>

maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/ConfigReader.java

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,10 +16,10 @@
1616
package io.helidon.build.maven.stager;
1717

1818
import java.util.ArrayDeque;
19+
import java.util.ArrayList;
1920
import java.util.Deque;
2021
import java.util.HashMap;
2122
import java.util.LinkedHashMap;
22-
import java.util.LinkedList;
2323
import java.util.List;
2424
import java.util.Map;
2525

@@ -28,7 +28,7 @@
2828
*/
2929
final class ConfigReader implements PlexusConfigNode.Visitor {
3030

31-
private final Map<PlexusConfigNode, Map<String, List<StagingElement>>> mappings;
31+
private final Map<PlexusConfigNode, List<StagingElement>> mappings;
3232
private final Deque<Scope> scopes;
3333
private final StagingElementFactory factory;
3434

@@ -46,9 +46,7 @@ final class ConfigReader implements PlexusConfigNode.Visitor {
4646
*/
4747
StagingTasks read(PlexusConfigNode node) {
4848
node.visit(this);
49-
StagingAction action = (StagingAction) mappings.get(node.parent())
50-
.get(node.name())
51-
.get(0);
49+
StagingAction action = (StagingAction) mappings.get(node.parent()).iterator().next();
5250
if (action instanceof StagingTasks) {
5351
return (StagingTasks) action;
5452
}
@@ -62,28 +60,26 @@ public void visitNode(PlexusConfigNode node) {
6260

6361
@Override
6462
public void postVisitNode(PlexusConfigNode node) {
65-
PlexusConfigNode nodeParent = node.parent();
66-
String nodeName = node.name();
67-
mappings.computeIfAbsent(node, n -> new LinkedHashMap<>());
68-
mappings.computeIfAbsent(nodeParent, n -> new LinkedHashMap<>());
63+
PlexusConfigNode parent = node.parent();
64+
String name = node.name();
65+
mappings.computeIfAbsent(node, n -> new ArrayList<>());
66+
mappings.computeIfAbsent(parent, n -> new ArrayList<>());
6967
Scope scope = scopes.peek();
7068
if (scope == null) {
7169
throw new IllegalStateException("Scope is not available");
7270
}
73-
StagingElement element = factory.create(
74-
nodeName,
75-
node.attributes(),
76-
mappings.get(node),
77-
node.value(),
78-
scope);
79-
if (element instanceof Variables) {
80-
for (Variable variable : ((Variables) element)) {
71+
StagingElement element = factory.create(name, node.attributes(), mappings.get(node), node.value(), scope);
72+
if (element instanceof Variables variables) {
73+
for (Variable variable : variables) {
8174
scope.parent.variables.put(variable.name(), variable);
8275
}
8376
}
84-
mappings.get(nodeParent)
85-
.computeIfAbsent(nodeName, n -> new LinkedList<>())
86-
.add(element);
77+
List<StagingElement> siblings = mappings.computeIfAbsent(parent, n -> new ArrayList<>());
78+
if (element instanceof StagingElements elements) {
79+
siblings.addAll(elements.nested());
80+
} else {
81+
siblings.add(element);
82+
}
8783
scopes.pop();
8884
}
8985

maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/DryRunStagingElementFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ final class DryRunStagingElementFactory extends StagingElementFactory {
3232
@Override
3333
StagingAction createAction(String name,
3434
Map<String, String> attrs,
35-
Map<String, List<StagingElement>> children,
35+
List<StagingElement> children,
3636
String text) {
3737

3838
StagingAction action = super.createAction(name, attrs, children, text);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.helidon.build.maven.stager;
17+
18+
/**
19+
* Exclude.
20+
*/
21+
record Exclude(String value) implements StagingElement {
22+
23+
static final String ELEMENT_NAME = "exclude";
24+
25+
@Override
26+
public String elementName() {
27+
return ELEMENT_NAME;
28+
}
29+
}

maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/FileTask.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2022 Oracle and/or its affiliates.
2+
* Copyright (c) 2020, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,9 +15,11 @@
1515
*/
1616
package io.helidon.build.maven.stager;
1717

18+
import java.io.BufferedWriter;
1819
import java.io.IOException;
1920
import java.nio.file.Files;
2021
import java.nio.file.Path;
22+
import java.util.List;
2123
import java.util.Map;
2224

2325
/**
@@ -30,8 +32,8 @@ final class FileTask extends StagingTask {
3032
private final String content;
3133
private final String source;
3234

33-
FileTask(ActionIterators iterators, Map<String, String> attrs, String content) {
34-
super(ELEMENT_NAME, null, iterators, attrs);
35+
FileTask(ActionIterators iterators, List<TextAction> nested, Map<String, String> attrs, String content) {
36+
super(ELEMENT_NAME, nested, iterators, attrs);
3537
this.content = content;
3638
this.source = attrs.get("source");
3739
}
@@ -50,10 +52,16 @@ String source() {
5052
*
5153
* @return content, may be {@code null}
5254
*/
53-
public String content() {
55+
String content() {
5456
return content;
5557
}
5658

59+
@Override
60+
@SuppressWarnings("unchecked")
61+
List<TextAction> tasks() {
62+
return (List<TextAction>) super.tasks();
63+
}
64+
5765
@Override
5866
protected void doExecute(StagingContext ctx, Path dir, Map<String, String> vars) throws IOException {
5967
String resolvedTarget = resolveVar(target(), vars);
@@ -72,6 +80,12 @@ protected void doExecute(StagingContext ctx, Path dir, Map<String, String> vars)
7280
Files.createFile(targetFile);
7381
if (resolvedContent != null && !resolvedContent.isEmpty()) {
7482
Files.writeString(targetFile, resolvedContent);
83+
} else {
84+
try (BufferedWriter writer = Files.newBufferedWriter(targetFile)) {
85+
for (TextAction task : tasks()) {
86+
writer.write(task.text());
87+
}
88+
}
7589
}
7690
}
7791
}

0 commit comments

Comments
 (0)