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