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