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
41
42
43
44
45
46 @SuppressWarnings({"checkstyle:methodname"})
47 @Named
48 @Singleton
49 @Deprecated(since = "4.0.0")
50 public class DefaultInheritanceAssembler implements InheritanceAssembler {
51
52 private static final String CHILD_DIRECTORY = "child-directory";
53
54 private static final String CHILD_DIRECTORY_PROPERTY = "project.directory";
55
56 private final InheritanceModelMerger merger = new InheritanceModelMerger();
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 boolean isBlankParentUrl = true;
141
142 if (parentUrl != null) {
143 for (int i = 0; i < parentUrl.length(); i++) {
144 if (!Character.isWhitespace(parentUrl.charAt(i))) {
145 isBlankParentUrl = false;
146 }
147 }
148 }
149
150 if (isBlankParentUrl || childDirectory == null || childPathAdjustment == null || !appendPath) {
151 return parentUrl;
152 }
153
154
155 return appendPath(parentUrl, childDirectory.toString(), childPathAdjustment.toString());
156 }
157
158 private String appendPath(String parentUrl, String childPath, String pathAdjustment) {
159 StringBuilder url = new StringBuilder(parentUrl.length()
160 + pathAdjustment.length()
161 + childPath.length()
162 + ((pathAdjustment.length() == 0) ? 1 : 2));
163
164 url.append(parentUrl);
165 concatPath(url, pathAdjustment);
166 concatPath(url, childPath);
167
168 return url.toString();
169 }
170
171 private void concatPath(StringBuilder url, String path) {
172 if (!path.isEmpty()) {
173 boolean initialUrlEndsWithSlash = url.charAt(url.length() - 1) == '/';
174 boolean pathStartsWithSlash = path.charAt(0) == '/';
175
176 if (pathStartsWithSlash) {
177 if (initialUrlEndsWithSlash) {
178
179 url.setLength(url.length() - 1);
180 }
181 } else if (!initialUrlEndsWithSlash) {
182
183 url.append('/');
184 }
185
186 url.append(path);
187
188
189 if (initialUrlEndsWithSlash && !path.endsWith("/")) {
190 url.append('/');
191 }
192 }
193 }
194
195 @Override
196 protected void mergeModelBase_Properties(
197 ModelBase.Builder builder,
198 ModelBase target,
199 ModelBase source,
200 boolean sourceDominant,
201 Map<Object, Object> context) {
202 Map<String, String> merged = new HashMap<>();
203 if (sourceDominant) {
204 merged.putAll(target.getProperties());
205 putAll(merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY);
206 } else {
207 putAll(merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY);
208 merged.putAll(target.getProperties());
209 }
210 builder.properties(merged);
211 builder.location(
212 "properties",
213 InputLocation.merge(
214 target.getLocation("properties"), source.getLocation("properties"), sourceDominant));
215 }
216
217 private void putAll(Map<String, String> s, Map<String, String> t, Object excludeKey) {
218 for (Map.Entry<String, String> e : t.entrySet()) {
219 if (!e.getKey().equals(excludeKey)) {
220 s.put(e.getKey(), e.getValue());
221 }
222 }
223 }
224
225 @Override
226 protected void mergePluginContainer_Plugins(
227 PluginContainer.Builder builder,
228 PluginContainer target,
229 PluginContainer source,
230 boolean sourceDominant,
231 Map<Object, Object> context) {
232 List<Plugin> src = source.getPlugins();
233 if (!src.isEmpty()) {
234 List<Plugin> tgt = target.getPlugins();
235 Map<Object, Plugin> master = new LinkedHashMap<>(src.size() * 2);
236
237 for (Plugin element : src) {
238 if (element.isInherited() || !element.getExecutions().isEmpty()) {
239
240 Plugin plugin = Plugin.newInstance(false);
241 plugin = mergePlugin(plugin, element, sourceDominant, context);
242
243 Object key = getPluginKey().apply(plugin);
244
245 master.put(key, plugin);
246 }
247 }
248
249 Map<Object, List<Plugin>> predecessors = new LinkedHashMap<>();
250 List<Plugin> pending = new ArrayList<>();
251 for (Plugin element : tgt) {
252 Object key = getPluginKey().apply(element);
253 Plugin existing = master.get(key);
254 if (existing != null) {
255 element = mergePlugin(element, existing, sourceDominant, context);
256
257 master.put(key, element);
258
259 if (!pending.isEmpty()) {
260 predecessors.put(key, pending);
261 pending = new ArrayList<>();
262 }
263 } else {
264 pending.add(element);
265 }
266 }
267
268 List<Plugin> result = new ArrayList<>(src.size() + tgt.size());
269 for (Map.Entry<Object, Plugin> entry : master.entrySet()) {
270 List<Plugin> pre = predecessors.get(entry.getKey());
271 if (pre != null) {
272 result.addAll(pre);
273 }
274 result.add(entry.getValue());
275 }
276 result.addAll(pending);
277
278 builder.plugins(result);
279 }
280 }
281
282 @Override
283 protected Plugin mergePlugin(
284 Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
285 Plugin.Builder builder = Plugin.newBuilder(target);
286 if (source.isInherited()) {
287 mergeConfigurationContainer(builder, target, source, sourceDominant, context);
288 }
289 mergePlugin_GroupId(builder, target, source, sourceDominant, context);
290 mergePlugin_ArtifactId(builder, target, source, sourceDominant, context);
291 mergePlugin_Version(builder, target, source, sourceDominant, context);
292 mergePlugin_Extensions(builder, target, source, sourceDominant, context);
293 mergePlugin_Executions(builder, target, source, sourceDominant, context);
294 mergePlugin_Dependencies(builder, target, source, sourceDominant, context);
295 return builder.build();
296 }
297
298 @Override
299 protected void mergeReporting_Plugins(
300 Reporting.Builder builder,
301 Reporting target,
302 Reporting source,
303 boolean sourceDominant,
304 Map<Object, Object> context) {
305 List<ReportPlugin> src = source.getPlugins();
306 if (!src.isEmpty()) {
307 List<ReportPlugin> tgt = target.getPlugins();
308 Map<Object, ReportPlugin> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
309
310 for (ReportPlugin element : src) {
311 if (element.isInherited()) {
312
313 ReportPlugin plugin = ReportPlugin.newInstance(false);
314 plugin = mergeReportPlugin(plugin, element, sourceDominant, context);
315
316 merged.put(getReportPluginKey().apply(element), plugin);
317 }
318 }
319
320 for (ReportPlugin element : tgt) {
321 Object key = getReportPluginKey().apply(element);
322 ReportPlugin existing = merged.get(key);
323 if (existing != null) {
324 element = mergeReportPlugin(element, existing, sourceDominant, context);
325 }
326 merged.put(key, element);
327 }
328
329 builder.plugins(merged.values());
330 }
331 }
332 }
333 }