1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.lifecycle.internal.concurrent;
20
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.NoSuchElementException;
29 import java.util.Optional;
30 import java.util.Set;
31 import java.util.function.Function;
32 import java.util.stream.Collectors;
33 import java.util.stream.Stream;
34
35 import org.apache.maven.plugin.MojoExecution;
36 import org.apache.maven.project.MavenProject;
37
38 public class BuildPlan {
39
40 private final Map<MavenProject, Map<String, BuildStep>> plan = new LinkedHashMap<>();
41 private final Map<MavenProject, List<MavenProject>> projects;
42 private final Map<String, String> aliases = new HashMap<>();
43 private volatile Set<String> duplicateIds;
44 private volatile List<BuildStep> sortedNodes;
45
46 BuildPlan() {
47 this.projects = null;
48 }
49
50 public BuildPlan(Map<MavenProject, List<MavenProject>> projects) {
51 this.projects = projects;
52 }
53
54 public Map<MavenProject, List<MavenProject>> getAllProjects() {
55 return projects;
56 }
57
58 public Map<String, String> aliases() {
59 return aliases;
60 }
61
62 public Stream<MavenProject> projects() {
63 return plan.keySet().stream();
64 }
65
66 public void addProject(MavenProject project, Map<String, BuildStep> steps) {
67 plan.put(project, steps);
68 }
69
70 public void addStep(MavenProject project, String name, BuildStep step) {
71 plan.get(project).put(name, step);
72 }
73
74 public Stream<BuildStep> allSteps() {
75 return plan.values().stream().flatMap(m -> m.values().stream());
76 }
77
78 public Stream<BuildStep> steps(MavenProject project) {
79 return Optional.ofNullable(plan.get(project))
80 .map(m -> m.values().stream())
81 .orElse(Stream.empty());
82 }
83
84 public Optional<BuildStep> step(MavenProject project, String name) {
85 return Optional.ofNullable(plan.get(project)).map(m -> m.get(name));
86 }
87
88 public BuildStep requiredStep(MavenProject project, String name) {
89 return step(project, name).orElseThrow(() -> new NoSuchElementException("Step " + name + " not found"));
90 }
91
92
93 public void then(BuildPlan step) {
94 step.plan.forEach((k, v) -> plan.merge(k, v, this::merge));
95 aliases.putAll(step.aliases);
96 }
97
98 private Map<String, BuildStep> merge(Map<String, BuildStep> org, Map<String, BuildStep> add) {
99
100 List<BuildStep> lasts =
101 org.values().stream().filter(b -> b.successors.isEmpty()).collect(Collectors.toList());
102 List<BuildStep> firsts =
103 add.values().stream().filter(b -> b.predecessors.isEmpty()).collect(Collectors.toList());
104 firsts.stream()
105 .filter(addNode -> !org.containsKey(addNode.name))
106 .forEach(addNode -> lasts.forEach(orgNode -> addNode.executeAfter(orgNode)));
107 add.forEach((name, node) -> org.merge(name, node, this::merge));
108 return org;
109 }
110
111 private BuildStep merge(BuildStep node1, BuildStep node2) {
112 node1.predecessors.addAll(node2.predecessors);
113 node1.successors.addAll(node2.successors);
114 node2.mojos.forEach((k, v) -> node1.mojos.merge(k, v, this::mergeMojos));
115 return node1;
116 }
117
118 private Map<String, MojoExecution> mergeMojos(Map<String, MojoExecution> l1, Map<String, MojoExecution> l2) {
119 l2.forEach(l1::putIfAbsent);
120 return l1;
121 }
122
123
124 public Set<String> duplicateIds() {
125 if (duplicateIds == null) {
126 synchronized (this) {
127 if (duplicateIds == null) {
128 duplicateIds = projects()
129 .map(MavenProject::getArtifactId)
130 .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
131 .entrySet()
132 .stream()
133 .filter(p -> p.getValue() > 1)
134 .map(Map.Entry::getKey)
135 .collect(Collectors.toSet());
136 }
137 }
138 }
139 return duplicateIds;
140 }
141
142 public List<BuildStep> sortedNodes() {
143 if (sortedNodes == null) {
144 synchronized (this) {
145 if (sortedNodes == null) {
146 List<BuildStep> sortedNodes = new ArrayList<>();
147 Set<BuildStep> visited = new HashSet<>();
148
149 allSteps().forEach(node -> visitNode(node, visited, sortedNodes));
150
151 Collections.reverse(sortedNodes);
152 this.sortedNodes = sortedNodes;
153 }
154 }
155 }
156 return sortedNodes;
157 }
158
159
160 private static void visitNode(BuildStep node, Set<BuildStep> visited, List<BuildStep> sortedNodes) {
161 if (visited.add(node)) {
162
163 node.successors.forEach(successor -> visitNode(successor, visited, sortedNodes));
164 sortedNodes.add(node);
165 }
166 }
167 }