1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.model.inheritance;
20
21 import javax.inject.Named;
22 import javax.inject.Singleton;
23
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29
30 import org.apache.maven.api.model.InputLocation;
31 import org.apache.maven.api.model.Model;
32 import org.apache.maven.api.model.ModelBase;
33 import org.apache.maven.api.model.Plugin;
34 import org.apache.maven.api.model.PluginContainer;
35 import org.apache.maven.api.model.ReportPlugin;
36 import org.apache.maven.api.model.Reporting;
37 import org.apache.maven.model.building.ModelBuildingRequest;
38 import org.apache.maven.model.building.ModelProblemCollector;
39 import org.apache.maven.model.merge.MavenModelMerger;
40 import org.codehaus.plexus.util.StringUtils;
41
42
43
44
45
46
47 @SuppressWarnings({"checkstyle:methodname"})
48 @Named
49 @Singleton
50 public class DefaultInheritanceAssembler implements InheritanceAssembler {
51
52 private InheritanceModelMerger merger = new InheritanceModelMerger();
53
54 private static final String CHILD_DIRECTORY = "child-directory";
55
56 private static final String CHILD_DIRECTORY_PROPERTY = "project.directory";
57
58 @Override
59 public Model assembleModelInheritance(
60 Model child, Model parent, ModelBuildingRequest request, ModelProblemCollector problems) {
61 Map<Object, Object> hints = new HashMap<>();
62 String childPath = child.getProperties().getOrDefault(CHILD_DIRECTORY_PROPERTY, child.getArtifactId());
63 hints.put(CHILD_DIRECTORY, childPath);
64 hints.put(MavenModelMerger.CHILD_PATH_ADJUSTMENT, getChildPathAdjustment(child, parent, childPath));
65 return merger.merge(child, parent, false, hints);
66 }
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 private String getChildPathAdjustment(Model child, Model parent, String childDirectory) {
88 String adjustment = "";
89
90 if (parent != null) {
91 String childName = child.getArtifactId();
92
93
94
95
96
97
98
99
100 if (child.getProjectDirectory() != null) {
101 childName = child.getProjectDirectory().getFileName().toString();
102 }
103
104 for (String module : parent.getModules()) {
105 module = module.replace('\\', '/');
106
107 if (module.regionMatches(true, module.length() - 4, ".xml", 0, 4)) {
108 module = module.substring(0, module.lastIndexOf('/') + 1);
109 }
110
111 String moduleName = module;
112 if (moduleName.endsWith("/")) {
113 moduleName = moduleName.substring(0, moduleName.length() - 1);
114 }
115
116 int lastSlash = moduleName.lastIndexOf('/');
117
118 moduleName = moduleName.substring(lastSlash + 1);
119
120 if ((moduleName.equals(childName) || (moduleName.equals(childDirectory))) && lastSlash >= 0) {
121 adjustment = module.substring(0, lastSlash);
122 break;
123 }
124 }
125 }
126
127 return adjustment;
128 }
129
130
131
132
133 protected static class InheritanceModelMerger extends MavenModelMerger {
134
135 @Override
136 protected String extrapolateChildUrl(String parentUrl, boolean appendPath, Map<Object, Object> context) {
137 Object childDirectory = context.get(CHILD_DIRECTORY);
138 Object childPathAdjustment = context.get(CHILD_PATH_ADJUSTMENT);
139
140 if (StringUtils.isBlank(parentUrl)
141 || childDirectory == null
142 || childPathAdjustment == null
143 || !appendPath) {
144 return parentUrl;
145 }
146
147
148 return appendPath(parentUrl, childDirectory.toString(), childPathAdjustment.toString());
149 }
150
151 private String appendPath(String parentUrl, String childPath, String pathAdjustment) {
152 StringBuilder url = new StringBuilder(parentUrl.length()
153 + pathAdjustment.length()
154 + childPath.length()
155 + ((pathAdjustment.length() == 0) ? 1 : 2));
156
157 url.append(parentUrl);
158 concatPath(url, pathAdjustment);
159 concatPath(url, childPath);
160
161 return url.toString();
162 }
163
164 private void concatPath(StringBuilder url, String path) {
165 if (path.length() > 0) {
166 boolean initialUrlEndsWithSlash = url.charAt(url.length() - 1) == '/';
167 boolean pathStartsWithSlash = path.charAt(0) == '/';
168
169 if (pathStartsWithSlash) {
170 if (initialUrlEndsWithSlash) {
171
172 url.setLength(url.length() - 1);
173 }
174 } else if (!initialUrlEndsWithSlash) {
175
176 url.append('/');
177 }
178
179 url.append(path);
180
181
182 if (initialUrlEndsWithSlash && !path.endsWith("/")) {
183 url.append('/');
184 }
185 }
186 }
187
188 @Override
189 protected void mergeModelBase_Properties(
190 ModelBase.Builder builder,
191 ModelBase target,
192 ModelBase source,
193 boolean sourceDominant,
194 Map<Object, Object> context) {
195 Map<String, String> merged = new HashMap<>();
196 if (sourceDominant) {
197 merged.putAll(target.getProperties());
198 putAll(merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY);
199 } else {
200 putAll(merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY);
201 merged.putAll(target.getProperties());
202 }
203 builder.properties(merged);
204 builder.location(
205 "properties",
206 InputLocation.merge(
207 target.getLocation("properties"), source.getLocation("properties"), sourceDominant));
208 }
209
210 private void putAll(Map<String, String> s, Map<String, String> t, Object excludeKey) {
211 for (Map.Entry<String, String> e : t.entrySet()) {
212 if (!e.getKey().equals(excludeKey)) {
213 s.put(e.getKey(), e.getValue());
214 }
215 }
216 }
217
218 @Override
219 protected void mergePluginContainer_Plugins(
220 PluginContainer.Builder builder,
221 PluginContainer target,
222 PluginContainer source,
223 boolean sourceDominant,
224 Map<Object, Object> context) {
225 List<Plugin> src = source.getPlugins();
226 if (!src.isEmpty()) {
227 List<Plugin> tgt = target.getPlugins();
228 Map<Object, Plugin> master = new LinkedHashMap<>(src.size() * 2);
229
230 for (Plugin element : src) {
231 if (element.isInherited() || !element.getExecutions().isEmpty()) {
232
233 Plugin plugin = Plugin.newInstance(false);
234 plugin = mergePlugin(plugin, element, sourceDominant, context);
235
236 Object key = getPluginKey().apply(plugin);
237
238 master.put(key, plugin);
239 }
240 }
241
242 Map<Object, List<Plugin>> predecessors = new LinkedHashMap<>();
243 List<Plugin> pending = new ArrayList<>();
244 for (Plugin element : tgt) {
245 Object key = getPluginKey().apply(element);
246 Plugin existing = master.get(key);
247 if (existing != null) {
248 element = mergePlugin(element, existing, sourceDominant, context);
249
250 master.put(key, element);
251
252 if (!pending.isEmpty()) {
253 predecessors.put(key, pending);
254 pending = new ArrayList<>();
255 }
256 } else {
257 pending.add(element);
258 }
259 }
260
261 List<Plugin> result = new ArrayList<>(src.size() + tgt.size());
262 for (Map.Entry<Object, Plugin> entry : master.entrySet()) {
263 List<Plugin> pre = predecessors.get(entry.getKey());
264 if (pre != null) {
265 result.addAll(pre);
266 }
267 result.add(entry.getValue());
268 }
269 result.addAll(pending);
270
271 builder.plugins(result);
272 }
273 }
274
275 @Override
276 protected Plugin mergePlugin(
277 Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
278 Plugin.Builder builder = Plugin.newBuilder(target);
279 if (source.isInherited()) {
280 mergeConfigurationContainer(builder, target, source, sourceDominant, context);
281 }
282 mergePlugin_GroupId(builder, target, source, sourceDominant, context);
283 mergePlugin_ArtifactId(builder, target, source, sourceDominant, context);
284 mergePlugin_Version(builder, target, source, sourceDominant, context);
285 mergePlugin_Extensions(builder, target, source, sourceDominant, context);
286 mergePlugin_Executions(builder, target, source, sourceDominant, context);
287 mergePlugin_Dependencies(builder, target, source, sourceDominant, context);
288 return builder.build();
289 }
290
291 @Override
292 protected void mergeReporting_Plugins(
293 Reporting.Builder builder,
294 Reporting target,
295 Reporting source,
296 boolean sourceDominant,
297 Map<Object, Object> context) {
298 List<ReportPlugin> src = source.getPlugins();
299 if (!src.isEmpty()) {
300 List<ReportPlugin> tgt = target.getPlugins();
301 Map<Object, ReportPlugin> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
302
303 for (ReportPlugin element : src) {
304 if (element.isInherited()) {
305
306 ReportPlugin plugin = ReportPlugin.newInstance(false);
307 plugin = mergeReportPlugin(plugin, element, sourceDominant, context);
308
309 merged.put(getReportPluginKey().apply(element), plugin);
310 }
311 }
312
313 for (ReportPlugin element : tgt) {
314 Object key = getReportPluginKey().apply(element);
315 ReportPlugin existing = merged.get(key);
316 if (existing != null) {
317 element = mergeReportPlugin(element, existing, sourceDominant, context);
318 }
319 merged.put(key, element);
320 }
321
322 builder.plugins(merged.values());
323 }
324 }
325 }
326 }