View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugin.resources.remote;
20  
21  import java.util.ArrayList;
22  import java.util.LinkedHashMap;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Properties;
27  import java.util.StringTokenizer;
28  import java.util.TreeMap;
29  
30  import org.apache.maven.model.Build;
31  import org.apache.maven.model.Dependency;
32  import org.apache.maven.model.DependencyManagement;
33  import org.apache.maven.model.DeploymentRepository;
34  import org.apache.maven.model.DistributionManagement;
35  import org.apache.maven.model.Extension;
36  import org.apache.maven.model.Model;
37  import org.apache.maven.model.PluginManagement;
38  import org.apache.maven.model.ReportPlugin;
39  import org.apache.maven.model.ReportSet;
40  import org.apache.maven.model.Reporting;
41  import org.apache.maven.model.Resource;
42  import org.apache.maven.model.Scm;
43  import org.apache.maven.model.Site;
44  import org.codehaus.plexus.util.StringUtils;
45  import org.codehaus.plexus.util.xml.Xpp3Dom;
46  
47  /**
48   * DefaultModelInheritanceAssembler
49   */
50  public class ModelInheritanceAssembler {
51      // TODO Remove this!
52      public void assembleBuildInheritance(Build childBuild, Build parentBuild, boolean handleAsInheritance) {
53          // The build has been set but we want to step in here and fill in
54          // values that have not been set by the child.
55  
56          if (childBuild.getSourceDirectory() == null) {
57              childBuild.setSourceDirectory(parentBuild.getSourceDirectory());
58          }
59  
60          if (childBuild.getScriptSourceDirectory() == null) {
61              childBuild.setScriptSourceDirectory(parentBuild.getScriptSourceDirectory());
62          }
63  
64          if (childBuild.getTestSourceDirectory() == null) {
65              childBuild.setTestSourceDirectory(parentBuild.getTestSourceDirectory());
66          }
67  
68          if (childBuild.getOutputDirectory() == null) {
69              childBuild.setOutputDirectory(parentBuild.getOutputDirectory());
70          }
71  
72          if (childBuild.getTestOutputDirectory() == null) {
73              childBuild.setTestOutputDirectory(parentBuild.getTestOutputDirectory());
74          }
75  
76          // Extensions are accumulated
77          mergeExtensionLists(childBuild, parentBuild);
78  
79          if (childBuild.getDirectory() == null) {
80              childBuild.setDirectory(parentBuild.getDirectory());
81          }
82  
83          if (childBuild.getDefaultGoal() == null) {
84              childBuild.setDefaultGoal(parentBuild.getDefaultGoal());
85          }
86  
87          if (childBuild.getFinalName() == null) {
88              childBuild.setFinalName(parentBuild.getFinalName());
89          }
90  
91          ModelUtils.mergeFilterLists(childBuild.getFilters(), parentBuild.getFilters());
92  
93          List<Resource> resources = childBuild.getResources();
94          if ((resources == null) || resources.isEmpty()) {
95              childBuild.setResources(parentBuild.getResources());
96          }
97  
98          resources = childBuild.getTestResources();
99          if ((resources == null) || resources.isEmpty()) {
100             childBuild.setTestResources(parentBuild.getTestResources());
101         }
102 
103         // Plugins are aggregated if Plugin.inherit != false
104         ModelUtils.mergePluginLists(childBuild, parentBuild, handleAsInheritance);
105 
106         // Plugin management :: aggregate
107         PluginManagement dominantPM = childBuild.getPluginManagement();
108         PluginManagement recessivePM = parentBuild.getPluginManagement();
109 
110         if ((dominantPM == null) && (recessivePM != null)) {
111             // FIXME: Filter out the inherited == false stuff!
112             childBuild.setPluginManagement(recessivePM);
113         } else {
114             ModelUtils.mergePluginLists(childBuild.getPluginManagement(), parentBuild.getPluginManagement(), false);
115         }
116     }
117 
118     private void assembleScmInheritance(Model child, Model parent, String childPathAdjustment, boolean appendPaths) {
119         if (parent.getScm() != null) {
120             Scm parentScm = parent.getScm();
121 
122             Scm childScm = child.getScm();
123 
124             if (childScm == null) {
125                 childScm = new Scm();
126 
127                 child.setScm(childScm);
128             }
129 
130             if (StringUtils.isEmpty(childScm.getConnection()) && !StringUtils.isEmpty(parentScm.getConnection())) {
131                 childScm.setConnection(
132                         appendPath(parentScm.getConnection(), child.getArtifactId(), childPathAdjustment, appendPaths));
133             }
134 
135             if (StringUtils.isEmpty(childScm.getDeveloperConnection())
136                     && !StringUtils.isEmpty(parentScm.getDeveloperConnection())) {
137                 childScm.setDeveloperConnection(appendPath(
138                         parentScm.getDeveloperConnection(), child.getArtifactId(), childPathAdjustment, appendPaths));
139             }
140 
141             if (StringUtils.isEmpty(childScm.getUrl()) && !StringUtils.isEmpty(parentScm.getUrl())) {
142                 childScm.setUrl(
143                         appendPath(parentScm.getUrl(), child.getArtifactId(), childPathAdjustment, appendPaths));
144             }
145         }
146     }
147 
148     public void copyModel(Model dest, Model source) {
149         assembleModelInheritance(dest, source, null, false);
150     }
151 
152     public void assembleModelInheritance(Model child, Model parent, String childPathAdjustment) {
153         assembleModelInheritance(child, parent, childPathAdjustment, true);
154     }
155 
156     public void assembleModelInheritance(Model child, Model parent) {
157         assembleModelInheritance(child, parent, null, true);
158     }
159 
160     private void assembleModelInheritance(Model child, Model parent, String childPathAdjustment, boolean appendPaths) {
161         // cannot inherit from null parent.
162         if (parent == null) {
163             return;
164         }
165 
166         // Group id
167         if (child.getGroupId() == null) {
168             child.setGroupId(parent.getGroupId());
169         }
170 
171         // version
172         if (child.getVersion() == null) {
173             // The parent version may have resolved to something different, so we take what we asked for...
174             // instead of - child.setVersion( parent.getVersion() );
175 
176             if (child.getParent() != null) {
177                 child.setVersion(child.getParent().getVersion());
178             }
179         }
180 
181         // inceptionYear
182         if (child.getInceptionYear() == null) {
183             child.setInceptionYear(parent.getInceptionYear());
184         }
185 
186         // url
187         if (child.getUrl() == null) {
188             if (parent.getUrl() != null) {
189                 child.setUrl(appendPath(parent.getUrl(), child.getArtifactId(), childPathAdjustment, appendPaths));
190             } else {
191                 child.setUrl(parent.getUrl());
192             }
193         }
194 
195         assembleDistributionInheritance(child, parent, childPathAdjustment, appendPaths);
196 
197         // issueManagement
198         if (child.getIssueManagement() == null) {
199             child.setIssueManagement(parent.getIssueManagement());
200         }
201 
202         // description
203         if (child.getDescription() == null) {
204             child.setDescription(parent.getDescription());
205         }
206 
207         // Organization
208         if (child.getOrganization() == null) {
209             child.setOrganization(parent.getOrganization());
210         }
211 
212         // Scm
213         assembleScmInheritance(child, parent, childPathAdjustment, appendPaths);
214 
215         // ciManagement
216         if (child.getCiManagement() == null) {
217             child.setCiManagement(parent.getCiManagement());
218         }
219 
220         // developers
221         if (child.getDevelopers().size() == 0) {
222             child.setDevelopers(parent.getDevelopers());
223         }
224 
225         // licenses
226         if (child.getLicenses().size() == 0) {
227             child.setLicenses(parent.getLicenses());
228         }
229 
230         // developers
231         if (child.getContributors().size() == 0) {
232             child.setContributors(parent.getContributors());
233         }
234 
235         // mailingLists
236         if (child.getMailingLists().size() == 0) {
237             child.setMailingLists(parent.getMailingLists());
238         }
239 
240         // Build
241         assembleBuildInheritance(child, parent);
242 
243         assembleDependencyInheritance(child, parent);
244 
245         child.setRepositories(ModelUtils.mergeRepositoryLists(child.getRepositories(), parent.getRepositories()));
246         //        child.setPluginRepositories(
247         //            ModelUtils.mergeRepositoryLists( child.getPluginRepositories(), parent.getPluginRepositories() )
248         // );
249 
250         assembleReportingInheritance(child, parent);
251 
252         assembleDependencyManagementInheritance(child, parent);
253 
254         Properties props = new Properties();
255         props.putAll(parent.getProperties());
256         props.putAll(child.getProperties());
257 
258         child.setProperties(props);
259     }
260 
261     // TODO Remove this!
262     private void assembleDependencyManagementInheritance(Model child, Model parent) {
263         DependencyManagement parentDepMgmt = parent.getDependencyManagement();
264 
265         DependencyManagement childDepMgmt = child.getDependencyManagement();
266 
267         if (parentDepMgmt != null) {
268             if (childDepMgmt == null) {
269                 child.setDependencyManagement(parentDepMgmt);
270             } else {
271                 List<Dependency> childDeps = childDepMgmt.getDependencies();
272 
273                 Map<String, Dependency> mappedChildDeps = new TreeMap<>();
274                 for (Dependency dep : childDeps) {
275                     mappedChildDeps.put(dep.getManagementKey(), dep);
276                 }
277 
278                 for (Dependency dep : parentDepMgmt.getDependencies()) {
279                     if (!mappedChildDeps.containsKey(dep.getManagementKey())) {
280                         childDepMgmt.addDependency(dep);
281                     }
282                 }
283             }
284         }
285     }
286 
287     private void assembleReportingInheritance(Model child, Model parent) {
288         // Reports :: aggregate
289         Reporting childReporting = child.getReporting();
290         Reporting parentReporting = parent.getReporting();
291 
292         if (parentReporting != null) {
293             if (childReporting == null) {
294                 childReporting = new Reporting();
295                 child.setReporting(childReporting);
296             }
297 
298             childReporting.setExcludeDefaults(parentReporting.isExcludeDefaults());
299 
300             if (StringUtils.isEmpty(childReporting.getOutputDirectory())) {
301                 childReporting.setOutputDirectory(parentReporting.getOutputDirectory());
302             }
303 
304             mergeReportPluginLists(childReporting, parentReporting, true);
305         }
306     }
307 
308     private static void mergeReportPluginLists(Reporting child, Reporting parent, boolean handleAsInheritance) {
309         if ((child == null) || (parent == null)) {
310             // nothing to do.
311             return;
312         }
313 
314         List<ReportPlugin> parentPlugins = parent.getPlugins();
315 
316         if ((parentPlugins != null) && !parentPlugins.isEmpty()) {
317             Map<String, ReportPlugin> assembledPlugins = new TreeMap<>();
318 
319             Map<String, ReportPlugin> childPlugins = child.getReportPluginsAsMap();
320 
321             for (ReportPlugin parentPlugin : parentPlugins) {
322                 String parentInherited = parentPlugin.getInherited();
323 
324                 if (!handleAsInheritance || (parentInherited == null) || Boolean.parseBoolean(parentInherited)) {
325 
326                     ReportPlugin assembledPlugin = parentPlugin;
327 
328                     ReportPlugin childPlugin = childPlugins.get(parentPlugin.getKey());
329 
330                     if (childPlugin != null) {
331                         assembledPlugin = childPlugin;
332 
333                         mergeReportPluginDefinitions(childPlugin, parentPlugin, handleAsInheritance);
334                     }
335 
336                     if (handleAsInheritance && (parentInherited == null)) {
337                         assembledPlugin.unsetInheritanceApplied();
338                     }
339 
340                     assembledPlugins.put(assembledPlugin.getKey(), assembledPlugin);
341                 }
342             }
343 
344             for (ReportPlugin childPlugin : childPlugins.values()) {
345                 if (!assembledPlugins.containsKey(childPlugin.getKey())) {
346                     assembledPlugins.put(childPlugin.getKey(), childPlugin);
347                 }
348             }
349 
350             child.setPlugins(new ArrayList<>(assembledPlugins.values()));
351 
352             child.flushReportPluginMap();
353         }
354     }
355 
356     private static void mergeReportSetDefinitions(ReportSet child, ReportSet parent) {
357         List<String> parentReports = parent.getReports();
358         List<String> childReports = child.getReports();
359 
360         List<String> reports = new ArrayList<>();
361 
362         if ((childReports != null) && !childReports.isEmpty()) {
363             reports.addAll(childReports);
364         }
365 
366         if (parentReports != null) {
367             for (String report : parentReports) {
368                 if (!reports.contains(report)) {
369                     reports.add(report);
370                 }
371             }
372         }
373 
374         child.setReports(reports);
375 
376         Xpp3Dom childConfiguration = (Xpp3Dom) child.getConfiguration();
377         Xpp3Dom parentConfiguration = (Xpp3Dom) parent.getConfiguration();
378 
379         childConfiguration = Xpp3Dom.mergeXpp3Dom(childConfiguration, parentConfiguration);
380 
381         child.setConfiguration(childConfiguration);
382     }
383 
384     public static void mergeReportPluginDefinitions(
385             ReportPlugin child, ReportPlugin parent, boolean handleAsInheritance) {
386         if ((child == null) || (parent == null)) {
387             // nothing to do.
388             return;
389         }
390 
391         if ((child.getVersion() == null) && (parent.getVersion() != null)) {
392             child.setVersion(parent.getVersion());
393         }
394 
395         // from here to the end of the method is dealing with merging of the <executions/> section.
396         String parentInherited = parent.getInherited();
397 
398         boolean parentIsInherited = (parentInherited == null) || Boolean.parseBoolean(parentInherited);
399 
400         List<ReportSet> parentReportSets = parent.getReportSets();
401 
402         if ((parentReportSets != null) && !parentReportSets.isEmpty()) {
403             Map<String, ReportSet> assembledReportSets = new TreeMap<>();
404 
405             Map<String, ReportSet> childReportSets = child.getReportSetsAsMap();
406 
407             for (ReportSet parentReportSet : parentReportSets) {
408 
409                 if (!handleAsInheritance || parentIsInherited) {
410                     ReportSet assembledReportSet = parentReportSet;
411 
412                     ReportSet childReportSet = childReportSets.get(parentReportSet.getId());
413 
414                     if (childReportSet != null) {
415                         mergeReportSetDefinitions(childReportSet, parentReportSet);
416 
417                         assembledReportSet = childReportSet;
418                     } else if (handleAsInheritance && (parentInherited == null)) {
419                         parentReportSet.unsetInheritanceApplied();
420                     }
421 
422                     assembledReportSets.put(assembledReportSet.getId(), assembledReportSet);
423                 }
424             }
425 
426             for (Map.Entry<String, ReportSet> entry : childReportSets.entrySet()) {
427                 String id = entry.getKey();
428 
429                 if (!assembledReportSets.containsKey(id)) {
430                     assembledReportSets.put(id, entry.getValue());
431                 }
432             }
433 
434             child.setReportSets(new ArrayList<>(assembledReportSets.values()));
435 
436             child.flushReportSetMap();
437         }
438     }
439 
440     // TODO Remove this!
441     private void assembleDependencyInheritance(Model child, Model parent) {
442         Map<String, Dependency> depsMap = new LinkedHashMap<>();
443 
444         List<Dependency> deps = parent.getDependencies();
445 
446         if (deps != null) {
447             for (Dependency dependency : deps) {
448                 depsMap.put(dependency.getManagementKey(), dependency);
449             }
450         }
451 
452         deps = child.getDependencies();
453 
454         if (deps != null) {
455             for (Dependency dependency : deps) {
456                 depsMap.put(dependency.getManagementKey(), dependency);
457             }
458         }
459 
460         child.setDependencies(new ArrayList<>(depsMap.values()));
461     }
462 
463     private void assembleBuildInheritance(Model child, Model parent) {
464         Build childBuild = child.getBuild();
465         Build parentBuild = parent.getBuild();
466 
467         if (parentBuild != null) {
468             if (childBuild == null) {
469                 childBuild = new Build();
470                 child.setBuild(childBuild);
471             }
472 
473             assembleBuildInheritance(childBuild, parentBuild, true);
474         }
475     }
476 
477     private void assembleDistributionInheritance(
478             Model child, Model parent, String childPathAdjustment, boolean appendPaths) {
479         if (parent.getDistributionManagement() != null) {
480             DistributionManagement parentDistMgmt = parent.getDistributionManagement();
481 
482             DistributionManagement childDistMgmt = child.getDistributionManagement();
483 
484             if (childDistMgmt == null) {
485                 childDistMgmt = new DistributionManagement();
486 
487                 child.setDistributionManagement(childDistMgmt);
488             }
489 
490             if (childDistMgmt.getSite() == null) {
491                 if (parentDistMgmt.getSite() != null) {
492                     Site site = new Site();
493 
494                     childDistMgmt.setSite(site);
495 
496                     site.setId(parentDistMgmt.getSite().getId());
497 
498                     site.setName(parentDistMgmt.getSite().getName());
499 
500                     site.setUrl(parentDistMgmt.getSite().getUrl());
501 
502                     if (site.getUrl() != null) {
503                         site.setUrl(appendPath(site.getUrl(), child.getArtifactId(), childPathAdjustment, appendPaths));
504                     }
505                 }
506             }
507 
508             if (childDistMgmt.getRepository() == null) {
509                 if (parentDistMgmt.getRepository() != null) {
510                     DeploymentRepository repository = copyDistributionRepository(parentDistMgmt.getRepository());
511                     childDistMgmt.setRepository(repository);
512                 }
513             }
514 
515             if (childDistMgmt.getSnapshotRepository() == null) {
516                 if (parentDistMgmt.getSnapshotRepository() != null) {
517                     DeploymentRepository repository =
518                             copyDistributionRepository(parentDistMgmt.getSnapshotRepository());
519                     childDistMgmt.setSnapshotRepository(repository);
520                 }
521             }
522 
523             if (StringUtils.isEmpty(childDistMgmt.getDownloadUrl())) {
524                 childDistMgmt.setDownloadUrl(parentDistMgmt.getDownloadUrl());
525             }
526 
527             // NOTE: We SHOULD NOT be inheriting status, since this is an assessment of the POM quality.
528             // NOTE: We SHOULD NOT be inheriting relocation, since this relates to a single POM
529         }
530     }
531 
532     private static DeploymentRepository copyDistributionRepository(DeploymentRepository parentRepository) {
533         DeploymentRepository repository = new DeploymentRepository();
534 
535         repository.setId(parentRepository.getId());
536 
537         repository.setName(parentRepository.getName());
538 
539         repository.setUrl(parentRepository.getUrl());
540 
541         repository.setLayout(parentRepository.getLayout());
542 
543         repository.setUniqueVersion(parentRepository.isUniqueVersion());
544 
545         return repository;
546     }
547 
548     // TODO This should eventually be migrated to DefaultPathTranslator.
549     protected String appendPath(String parentPath, String childPath, String pathAdjustment, boolean appendPaths) {
550         String uncleanPath = parentPath;
551 
552         if (appendPaths) {
553             if (pathAdjustment != null) {
554                 uncleanPath += "/" + pathAdjustment;
555             }
556 
557             if (childPath != null) {
558                 uncleanPath += "/" + childPath;
559             }
560         }
561 
562         String cleanedPath = "";
563 
564         int protocolIdx = uncleanPath.indexOf("://");
565 
566         if (protocolIdx > -1) {
567             cleanedPath = uncleanPath.substring(0, protocolIdx + 3);
568             uncleanPath = uncleanPath.substring(protocolIdx + 3);
569         }
570 
571         if (uncleanPath.startsWith("/")) {
572             cleanedPath += "/";
573         }
574 
575         return cleanedPath + resolvePath(uncleanPath);
576     }
577 
578     // TODO Move this to plexus-utils' PathTool.
579     private static String resolvePath(String uncleanPath) {
580         LinkedList<String> pathElements = new LinkedList<>();
581 
582         StringTokenizer tokenizer = new StringTokenizer(uncleanPath, "/");
583 
584         while (tokenizer.hasMoreTokens()) {
585             String token = tokenizer.nextToken();
586 
587             switch (token) {
588                 case "":
589                     // Empty path entry ("...//.."), remove.
590                     break;
591                 case "..":
592                     if (pathElements.isEmpty()) {
593                         // FIXME: somehow report to the user
594                         // that there are too many '..' elements.
595                         // For now, ignore the extra '..'.
596                     } else {
597                         pathElements.removeLast();
598                     }
599                     break;
600                 default:
601                     pathElements.addLast(token);
602                     break;
603             }
604         }
605 
606         StringBuilder cleanedPath = new StringBuilder();
607 
608         while (!pathElements.isEmpty()) {
609             cleanedPath.append(pathElements.removeFirst());
610             if (!pathElements.isEmpty()) {
611                 cleanedPath.append('/');
612             }
613         }
614 
615         return cleanedPath.toString();
616     }
617 
618     private static void mergeExtensionLists(Build childBuild, Build parentBuild) {
619         for (Extension e : parentBuild.getExtensions()) {
620             if (!childBuild.getExtensions().contains(e)) {
621                 childBuild.addExtension(e);
622             }
623         }
624     }
625 }