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 ( Iterator<Dependency> it = childDeps.iterator(); it.hasNext(); )
327                 {
328                     Dependency dep = it.next();
329                     mappedChildDeps.put( dep.getManagementKey(), dep );
330                 }
331 
332                 for ( Iterator<Dependency> it = parentDepMgmt.getDependencies().iterator(); it.hasNext(); )
333                 {
334                     Dependency dep = it.next();
335                     if ( !mappedChildDeps.containsKey( dep.getManagementKey() ) )
336                     {
337                         childDepMgmt.addDependency( dep );
338                     }
339                 }
340             }
341         }
342     }
343 
344     private void assembleReportingInheritance( Model child, Model parent )
345     {
346         // Reports :: aggregate
347         Reporting childReporting = child.getReporting();
348         Reporting parentReporting = parent.getReporting();
349 
350         if ( parentReporting != null )
351         {
352             if ( childReporting == null )
353             {
354                 childReporting = new Reporting();
355                 child.setReporting( childReporting );
356             }
357 
358             childReporting.setExcludeDefaults( parentReporting.isExcludeDefaults() );
359 
360             if ( StringUtils.isEmpty( childReporting.getOutputDirectory() ) )
361             {
362                 childReporting.setOutputDirectory( parentReporting.getOutputDirectory() );
363             }
364 
365             mergeReportPluginLists( childReporting, parentReporting, true );
366         }
367     }
368 
369     private static void mergeReportPluginLists( Reporting child, Reporting parent, boolean handleAsInheritance )
370     {
371         if ( ( child == null ) || ( parent == null ) )
372         {
373             // nothing to do.
374             return;
375         }
376 
377         List parentPlugins = parent.getPlugins();
378 
379         if ( ( parentPlugins != null ) && !parentPlugins.isEmpty() )
380         {
381             Map assembledPlugins = new TreeMap();
382 
383             Map childPlugins = child.getReportPluginsAsMap();
384 
385             for ( Iterator it = parentPlugins.iterator(); it.hasNext(); )
386             {
387                 ReportPlugin parentPlugin = (ReportPlugin) it.next();
388 
389                 String parentInherited = parentPlugin.getInherited();
390 
391                 if ( !handleAsInheritance || ( parentInherited == null )
392                     || Boolean.valueOf( parentInherited ).booleanValue() )
393                 {
394 
395                     ReportPlugin assembledPlugin = parentPlugin;
396 
397                     ReportPlugin childPlugin = (ReportPlugin) childPlugins.get( parentPlugin.getKey() );
398 
399                     if ( childPlugin != null )
400                     {
401                         assembledPlugin = childPlugin;
402 
403                         mergeReportPluginDefinitions( childPlugin, parentPlugin, handleAsInheritance );
404                     }
405 
406                     if ( handleAsInheritance && ( parentInherited == null ) )
407                     {
408                         assembledPlugin.unsetInheritanceApplied();
409                     }
410 
411                     assembledPlugins.put( assembledPlugin.getKey(), assembledPlugin );
412                 }
413             }
414 
415             for ( Iterator it = childPlugins.values().iterator(); it.hasNext(); )
416             {
417                 ReportPlugin childPlugin = (ReportPlugin) it.next();
418 
419                 if ( !assembledPlugins.containsKey( childPlugin.getKey() ) )
420                 {
421                     assembledPlugins.put( childPlugin.getKey(), childPlugin );
422                 }
423             }
424 
425             child.setPlugins( new ArrayList( assembledPlugins.values() ) );
426 
427             child.flushReportPluginMap();
428         }
429     }
430 
431     private static void mergeReportSetDefinitions( ReportSet child, ReportSet parent )
432     {
433         List parentReports = parent.getReports();
434         List childReports = child.getReports();
435 
436         List reports = new ArrayList();
437 
438         if ( ( childReports != null ) && !childReports.isEmpty() )
439         {
440             reports.addAll( childReports );
441         }
442 
443         if ( parentReports != null )
444         {
445             for ( Iterator i = parentReports.iterator(); i.hasNext(); )
446             {
447                 String report = (String) i.next();
448 
449                 if ( !reports.contains( report ) )
450                 {
451                     reports.add( report );
452                 }
453             }
454         }
455 
456         child.setReports( reports );
457 
458         Xpp3Dom childConfiguration = (Xpp3Dom) child.getConfiguration();
459         Xpp3Dom parentConfiguration = (Xpp3Dom) parent.getConfiguration();
460 
461         childConfiguration = Xpp3Dom.mergeXpp3Dom( childConfiguration, parentConfiguration );
462 
463         child.setConfiguration( childConfiguration );
464     }
465 
466 
467     public static void mergeReportPluginDefinitions( ReportPlugin child, ReportPlugin parent,
468                                                      boolean handleAsInheritance )
469     {
470         if ( ( child == null ) || ( parent == null ) )
471         {
472             // nothing to do.
473             return;
474         }
475 
476         if ( ( child.getVersion() == null ) && ( parent.getVersion() != null ) )
477         {
478             child.setVersion( parent.getVersion() );
479         }
480 
481         // from here to the end of the method is dealing with merging of the <executions/> section.
482         String parentInherited = parent.getInherited();
483 
484         boolean parentIsInherited = ( parentInherited == null ) || Boolean.valueOf( parentInherited ).booleanValue();
485 
486         List parentReportSets = parent.getReportSets();
487 
488         if ( ( parentReportSets != null ) && !parentReportSets.isEmpty() )
489         {
490             Map assembledReportSets = new TreeMap();
491 
492             Map childReportSets = child.getReportSetsAsMap();
493 
494             for ( Iterator it = parentReportSets.iterator(); it.hasNext(); )
495             {
496                 ReportSet parentReportSet = (ReportSet) it.next();
497 
498                 if ( !handleAsInheritance || parentIsInherited )
499                 {
500                     ReportSet assembledReportSet = parentReportSet;
501 
502                     ReportSet childReportSet = (ReportSet) childReportSets.get( parentReportSet.getId() );
503 
504                     if ( childReportSet != null )
505                     {
506                         mergeReportSetDefinitions( childReportSet, parentReportSet );
507 
508                         assembledReportSet = childReportSet;
509                     }
510                     else if ( handleAsInheritance && ( parentInherited == null ) )
511                     {
512                         parentReportSet.unsetInheritanceApplied();
513                     }
514 
515                     assembledReportSets.put( assembledReportSet.getId(), assembledReportSet );
516                 }
517             }
518 
519             for ( Iterator it = childReportSets.entrySet().iterator(); it.hasNext(); )
520             {
521                 Map.Entry entry = (Map.Entry) it.next();
522 
523                 String id = (String) entry.getKey();
524 
525                 if ( !assembledReportSets.containsKey( id ) )
526                 {
527                     assembledReportSets.put( id, entry.getValue() );
528                 }
529             }
530 
531             child.setReportSets( new ArrayList( assembledReportSets.values() ) );
532 
533             child.flushReportSetMap();
534         }
535 
536     }
537 
538     // TODO: Remove this!
539     @SuppressWarnings( "unchecked" )
540     private void assembleDependencyInheritance( Model child, Model parent )
541     {
542         Map<String, Dependency> depsMap = new LinkedHashMap<String, Dependency>();
543 
544         List<Dependency> deps = parent.getDependencies();
545 
546         if ( deps != null )
547         {
548             for ( Dependency dependency : deps )
549             {
550                 depsMap.put( dependency.getManagementKey(), dependency );
551             }
552         }
553 
554         deps = child.getDependencies();
555 
556         if ( deps != null )
557         {
558             for ( Dependency dependency : deps )
559             {
560                 depsMap.put( dependency.getManagementKey(), dependency );
561             }
562         }
563 
564         child.setDependencies( new ArrayList<Dependency>( depsMap.values() ) );
565     }
566 
567     private void assembleBuildInheritance( Model child, Model parent )
568     {
569         Build childBuild = child.getBuild();
570         Build parentBuild = parent.getBuild();
571 
572         if ( parentBuild != null )
573         {
574             if ( childBuild == null )
575             {
576                 childBuild = new Build();
577                 child.setBuild( childBuild );
578             }
579 
580             assembleBuildInheritance( childBuild, parentBuild, true );
581         }
582     }
583 
584     private void assembleDistributionInheritence( Model child, Model parent, String childPathAdjustment,
585                                                   boolean appendPaths )
586     {
587         if ( parent.getDistributionManagement() != null )
588         {
589             DistributionManagement parentDistMgmt = parent.getDistributionManagement();
590 
591             DistributionManagement childDistMgmt = child.getDistributionManagement();
592 
593             if ( childDistMgmt == null )
594             {
595                 childDistMgmt = new DistributionManagement();
596 
597                 child.setDistributionManagement( childDistMgmt );
598             }
599 
600             if ( childDistMgmt.getSite() == null )
601             {
602                 if ( parentDistMgmt.getSite() != null )
603                 {
604                     Site site = new Site();
605 
606                     childDistMgmt.setSite( site );
607 
608                     site.setId( parentDistMgmt.getSite().getId() );
609 
610                     site.setName( parentDistMgmt.getSite().getName() );
611 
612                     site.setUrl( parentDistMgmt.getSite().getUrl() );
613 
614                     if ( site.getUrl() != null )
615                     {
616                         site.setUrl(
617                             appendPath( site.getUrl(), child.getArtifactId(), childPathAdjustment, appendPaths ) );
618                     }
619                 }
620             }
621 
622             if ( childDistMgmt.getRepository() == null )
623             {
624                 if ( parentDistMgmt.getRepository() != null )
625                 {
626                     DeploymentRepository repository = copyDistributionRepository( parentDistMgmt.getRepository() );
627                     childDistMgmt.setRepository( repository );
628                 }
629             }
630 
631             if ( childDistMgmt.getSnapshotRepository() == null )
632             {
633                 if ( parentDistMgmt.getSnapshotRepository() != null )
634                 {
635                     DeploymentRepository repository =
636                         copyDistributionRepository( parentDistMgmt.getSnapshotRepository() );
637                     childDistMgmt.setSnapshotRepository( repository );
638                 }
639             }
640 
641             if ( StringUtils.isEmpty( childDistMgmt.getDownloadUrl() ) )
642             {
643                 childDistMgmt.setDownloadUrl( parentDistMgmt.getDownloadUrl() );
644             }
645 
646             // NOTE: We SHOULD NOT be inheriting status, since this is an assessment of the POM quality.
647             // NOTE: We SHOULD NOT be inheriting relocation, since this relates to a single POM
648         }
649     }
650 
651     private static DeploymentRepository copyDistributionRepository( DeploymentRepository parentRepository )
652     {
653         DeploymentRepository repository = new DeploymentRepository();
654 
655         repository.setId( parentRepository.getId() );
656 
657         repository.setName( parentRepository.getName() );
658 
659         repository.setUrl( parentRepository.getUrl() );
660 
661         repository.setLayout( parentRepository.getLayout() );
662 
663         repository.setUniqueVersion( parentRepository.isUniqueVersion() );
664 
665         return repository;
666     }
667 
668     // TODO: This should eventually be migrated to DefaultPathTranslator.
669     protected String appendPath( String parentPath, String childPath, String pathAdjustment, boolean appendPaths )
670     {
671         String uncleanPath = parentPath;
672 
673         if ( appendPaths )
674         {
675             if ( pathAdjustment != null )
676             {
677                 uncleanPath += "/" + pathAdjustment;
678             }
679 
680             if ( childPath != null )
681             {
682                 uncleanPath += "/" + childPath;
683             }
684         }
685 
686         String cleanedPath = "";
687 
688         int protocolIdx = uncleanPath.indexOf( "://" );
689 
690         if ( protocolIdx > -1 )
691         {
692             cleanedPath = uncleanPath.substring( 0, protocolIdx + 3 );
693             uncleanPath = uncleanPath.substring( protocolIdx + 3 );
694         }
695 
696         if ( uncleanPath.startsWith( "/" ) )
697         {
698             cleanedPath += "/";
699         }
700 
701         return cleanedPath + resolvePath( uncleanPath );
702     }
703 
704     // TODO: Move this to plexus-utils' PathTool.
705     private static String resolvePath( String uncleanPath )
706     {
707         LinkedList<String> pathElements = new LinkedList<String>();
708 
709         StringTokenizer tokenizer = new StringTokenizer( uncleanPath, "/" );
710 
711         while ( tokenizer.hasMoreTokens() )
712         {
713             String token = tokenizer.nextToken();
714 
715             if ( token.equals( "" ) )
716             {
717                 // Empty path entry ("...//.."), remove.
718             }
719             else if ( token.equals( ".." ) )
720             {
721                 if ( pathElements.isEmpty() )
722                 {
723                     // FIXME: somehow report to the user
724                     // that there are too many '..' elements.
725                     // For now, ignore the extra '..'.
726                 }
727                 else
728                 {
729                     pathElements.removeLast();
730                 }
731             }
732             else
733             {
734                 pathElements.addLast( token );
735             }
736         }
737 
738         StringBuilder cleanedPath = new StringBuilder();
739 
740         while ( !pathElements.isEmpty() )
741         {
742             cleanedPath.append( pathElements.removeFirst() );
743             if ( !pathElements.isEmpty() )
744             {
745                 cleanedPath.append( '/' );
746             }
747         }
748 
749         return cleanedPath.toString();
750     }
751 
752     private static void mergeExtensionLists( Build childBuild, Build parentBuild )
753     {
754         for ( Extension e : parentBuild.getExtensions() )
755         {
756             if ( !childBuild.getExtensions().contains( e ) )
757             {
758                 childBuild.addExtension( e );
759             }
760         }
761     }
762 }