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