View Javadoc

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