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 }