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