1 package org.apache.maven.project.inheritance;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.apache.maven.project.ModelUtils;
46 import org.codehaus.plexus.component.annotations.Component;
47 import org.codehaus.plexus.util.StringUtils;
48 import org.codehaus.plexus.util.xml.Xpp3Dom;
49
50
51
52
53 @Component( role = ModelInheritanceAssembler.class )
54 public class DefaultModelInheritanceAssembler
55 implements ModelInheritanceAssembler
56 {
57
58 @SuppressWarnings( "unchecked" )
59 public void assembleBuildInheritance( Build childBuild, Build parentBuild, boolean handleAsInheritance )
60 {
61
62
63
64 if ( childBuild.getSourceDirectory() == null )
65 {
66 childBuild.setSourceDirectory( parentBuild.getSourceDirectory() );
67 }
68
69 if ( childBuild.getScriptSourceDirectory() == null )
70 {
71 childBuild.setScriptSourceDirectory( parentBuild.getScriptSourceDirectory() );
72 }
73
74 if ( childBuild.getTestSourceDirectory() == null )
75 {
76 childBuild.setTestSourceDirectory( parentBuild.getTestSourceDirectory() );
77 }
78
79 if ( childBuild.getOutputDirectory() == null )
80 {
81 childBuild.setOutputDirectory( parentBuild.getOutputDirectory() );
82 }
83
84 if ( childBuild.getTestOutputDirectory() == null )
85 {
86 childBuild.setTestOutputDirectory( parentBuild.getTestOutputDirectory() );
87 }
88
89
90 mergeExtensionLists( childBuild, parentBuild );
91
92 if ( childBuild.getDirectory() == null )
93 {
94 childBuild.setDirectory( parentBuild.getDirectory() );
95 }
96
97 if ( childBuild.getDefaultGoal() == null )
98 {
99 childBuild.setDefaultGoal( parentBuild.getDefaultGoal() );
100 }
101
102 if ( childBuild.getFinalName() == null )
103 {
104 childBuild.setFinalName( parentBuild.getFinalName() );
105 }
106
107 ModelUtils.mergeFilterLists( childBuild.getFilters(), parentBuild.getFilters() );
108
109 List<Resource> resources = childBuild.getResources();
110 if ( ( resources == null ) || resources.isEmpty() )
111 {
112 childBuild.setResources( parentBuild.getResources() );
113 }
114
115 resources = childBuild.getTestResources();
116 if ( ( resources == null ) || resources.isEmpty() )
117 {
118 childBuild.setTestResources( parentBuild.getTestResources() );
119 }
120
121
122 ModelUtils.mergePluginLists( childBuild, parentBuild, handleAsInheritance );
123
124
125 PluginManagement dominantPM = childBuild.getPluginManagement();
126 PluginManagement recessivePM = parentBuild.getPluginManagement();
127
128 if ( ( dominantPM == null ) && ( recessivePM != null ) )
129 {
130
131 childBuild.setPluginManagement( recessivePM );
132 }
133 else
134 {
135 ModelUtils.mergePluginLists( childBuild.getPluginManagement(), parentBuild.getPluginManagement(), false );
136 }
137 }
138
139 private void assembleScmInheritance( Model child, Model parent, String childPathAdjustment, boolean appendPaths )
140 {
141 if ( parent.getScm() != null )
142 {
143 Scm parentScm = parent.getScm();
144
145 Scm childScm = child.getScm();
146
147 if ( childScm == null )
148 {
149 childScm = new Scm();
150
151 child.setScm( childScm );
152 }
153
154 if ( StringUtils.isEmpty( childScm.getConnection() ) && !StringUtils.isEmpty( parentScm.getConnection() ) )
155 {
156 childScm.setConnection(
157 appendPath( parentScm.getConnection(), child.getArtifactId(), childPathAdjustment, appendPaths ) );
158 }
159
160 if ( StringUtils.isEmpty( childScm.getDeveloperConnection() )
161 && !StringUtils.isEmpty( parentScm.getDeveloperConnection() ) )
162 {
163 childScm
164 .setDeveloperConnection( appendPath( parentScm.getDeveloperConnection(), child.getArtifactId(),
165 childPathAdjustment, appendPaths ) );
166 }
167
168 if ( StringUtils.isEmpty( childScm.getUrl() ) && !StringUtils.isEmpty( parentScm.getUrl() ) )
169 {
170 childScm.setUrl(
171 appendPath( parentScm.getUrl(), child.getArtifactId(), childPathAdjustment, appendPaths ) );
172 }
173 }
174 }
175
176 public void copyModel( Model dest, Model source )
177 {
178 assembleModelInheritance( dest, source, null, false );
179 }
180
181 public void assembleModelInheritance( Model child, Model parent, String childPathAdjustment )
182 {
183 assembleModelInheritance( child, parent, childPathAdjustment, true );
184 }
185
186 public void assembleModelInheritance( Model child, Model parent )
187 {
188 assembleModelInheritance( child, parent, null, true );
189 }
190
191 private void assembleModelInheritance( Model child, Model parent, String childPathAdjustment, boolean appendPaths )
192 {
193
194 if ( parent == null )
195 {
196 return;
197 }
198
199
200 if ( child.getGroupId() == null )
201 {
202 child.setGroupId( parent.getGroupId() );
203 }
204
205
206 if ( child.getVersion() == null )
207 {
208
209
210
211 if ( child.getParent() != null )
212 {
213 child.setVersion( child.getParent().getVersion() );
214 }
215 }
216
217
218 if ( child.getInceptionYear() == null )
219 {
220 child.setInceptionYear( parent.getInceptionYear() );
221 }
222
223
224 if ( child.getUrl() == null )
225 {
226 if ( parent.getUrl() != null )
227 {
228 child.setUrl( appendPath( parent.getUrl(), child.getArtifactId(), childPathAdjustment, appendPaths ) );
229 }
230 else
231 {
232 child.setUrl( parent.getUrl() );
233 }
234 }
235
236 assembleDistributionInheritance( child, parent, childPathAdjustment, appendPaths );
237
238
239 if ( child.getIssueManagement() == null )
240 {
241 child.setIssueManagement( parent.getIssueManagement() );
242 }
243
244
245 if ( child.getDescription() == null )
246 {
247 child.setDescription( parent.getDescription() );
248 }
249
250
251 if ( child.getOrganization() == null )
252 {
253 child.setOrganization( parent.getOrganization() );
254 }
255
256
257 assembleScmInheritance( child, parent, childPathAdjustment, appendPaths );
258
259
260 if ( child.getCiManagement() == null )
261 {
262 child.setCiManagement( parent.getCiManagement() );
263 }
264
265
266 if ( child.getDevelopers().size() == 0 )
267 {
268 child.setDevelopers( parent.getDevelopers() );
269 }
270
271
272 if ( child.getLicenses().size() == 0 )
273 {
274 child.setLicenses( parent.getLicenses() );
275 }
276
277
278 if ( child.getContributors().size() == 0 )
279 {
280 child.setContributors( parent.getContributors() );
281 }
282
283
284 if ( child.getMailingLists().size() == 0 )
285 {
286 child.setMailingLists( parent.getMailingLists() );
287 }
288
289
290 assembleBuildInheritance( child, parent );
291
292 assembleDependencyInheritance( child, parent );
293
294 child.setRepositories( ModelUtils.mergeRepositoryLists( child.getRepositories(), parent.getRepositories() ) );
295
296
297
298 assembleReportingInheritance( child, parent );
299
300 assembleDependencyManagementInheritance( child, parent );
301
302 Properties props = new Properties();
303 props.putAll( parent.getProperties() );
304 props.putAll( child.getProperties() );
305
306 child.setProperties( props );
307 }
308
309
310 @SuppressWarnings( "unchecked" )
311 private void assembleDependencyManagementInheritance( Model child, Model parent )
312 {
313 DependencyManagement parentDepMgmt = parent.getDependencyManagement();
314
315 DependencyManagement childDepMgmt = child.getDependencyManagement();
316
317 if ( parentDepMgmt != null )
318 {
319 if ( childDepMgmt == null )
320 {
321 child.setDependencyManagement( parentDepMgmt );
322 }
323 else
324 {
325 List<Dependency> childDeps = childDepMgmt.getDependencies();
326
327 Map<String, Dependency> mappedChildDeps = new TreeMap<>();
328 for ( Dependency dep : childDeps )
329 {
330 mappedChildDeps.put( dep.getManagementKey(), dep );
331 }
332
333 for ( Dependency dep : parentDepMgmt.getDependencies() )
334 {
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
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
374 return;
375 }
376
377 List<ReportPlugin> parentPlugins = parent.getPlugins();
378
379 if ( ( parentPlugins != null ) && !parentPlugins.isEmpty() )
380 {
381 Map<String, ReportPlugin> assembledPlugins = new TreeMap<>();
382
383 Map<String, ReportPlugin> childPlugins = child.getReportPluginsAsMap();
384
385 for ( ReportPlugin parentPlugin : parentPlugins )
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 = 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 ( ReportPlugin childPlugin : childPlugins.values() )
413 {
414 if ( !assembledPlugins.containsKey( childPlugin.getKey() ) )
415 {
416 assembledPlugins.put( childPlugin.getKey(), childPlugin );
417 }
418 }
419
420 child.setPlugins( new ArrayList<>( assembledPlugins.values() ) );
421
422 child.flushReportPluginMap();
423 }
424 }
425
426 private static void mergeReportSetDefinitions( ReportSet child, ReportSet parent )
427 {
428 List<String> parentReports = parent.getReports();
429 List<String> childReports = child.getReports();
430
431 List<String> reports = new ArrayList<>();
432
433 if ( ( childReports != null ) && !childReports.isEmpty() )
434 {
435 reports.addAll( childReports );
436 }
437
438 if ( parentReports != null )
439 {
440 for ( String report : parentReports )
441 {
442 if ( !reports.contains( report ) )
443 {
444 reports.add( report );
445 }
446 }
447 }
448
449 child.setReports( reports );
450
451 Xpp3Dom childConfiguration = (Xpp3Dom) child.getConfiguration();
452 Xpp3Dom parentConfiguration = (Xpp3Dom) parent.getConfiguration();
453
454 childConfiguration = Xpp3Dom.mergeXpp3Dom( childConfiguration, parentConfiguration );
455
456 child.setConfiguration( childConfiguration );
457 }
458
459
460 public static void mergeReportPluginDefinitions( ReportPlugin child, ReportPlugin parent,
461 boolean handleAsInheritance )
462 {
463 if ( ( child == null ) || ( parent == null ) )
464 {
465
466 return;
467 }
468
469 if ( ( child.getVersion() == null ) && ( parent.getVersion() != null ) )
470 {
471 child.setVersion( parent.getVersion() );
472 }
473
474
475 String parentInherited = parent.getInherited();
476
477 boolean parentIsInherited = ( parentInherited == null ) || Boolean.valueOf( parentInherited );
478
479 List<ReportSet> parentReportSets = parent.getReportSets();
480
481 if ( ( parentReportSets != null ) && !parentReportSets.isEmpty() )
482 {
483 Map<String, ReportSet> assembledReportSets = new TreeMap<>();
484
485 Map<String, ReportSet> childReportSets = child.getReportSetsAsMap();
486
487 for ( Object parentReportSet1 : parentReportSets )
488 {
489 ReportSet parentReportSet = (ReportSet) parentReportSet1;
490
491 if ( !handleAsInheritance || parentIsInherited )
492 {
493 ReportSet assembledReportSet = parentReportSet;
494
495 ReportSet childReportSet = childReportSets.get( parentReportSet.getId() );
496
497 if ( childReportSet != null )
498 {
499 mergeReportSetDefinitions( childReportSet, parentReportSet );
500
501 assembledReportSet = childReportSet;
502 }
503 else if ( handleAsInheritance && ( parentInherited == null ) )
504 {
505 parentReportSet.unsetInheritanceApplied();
506 }
507
508 assembledReportSets.put( assembledReportSet.getId(), assembledReportSet );
509 }
510 }
511
512 for ( Map.Entry<String, ReportSet> entry : childReportSets.entrySet() )
513 {
514 String id = entry.getKey();
515
516 if ( !assembledReportSets.containsKey( id ) )
517 {
518 assembledReportSets.put( id, entry.getValue() );
519 }
520 }
521
522 child.setReportSets( new ArrayList<>( assembledReportSets.values() ) );
523
524 child.flushReportSetMap();
525 }
526
527 }
528
529
530 @SuppressWarnings( "unchecked" )
531 private void assembleDependencyInheritance( Model child, Model parent )
532 {
533 Map<String, Dependency> depsMap = new LinkedHashMap<>();
534
535 List<Dependency> deps = parent.getDependencies();
536
537 if ( deps != null )
538 {
539 for ( Dependency dependency : deps )
540 {
541 depsMap.put( dependency.getManagementKey(), dependency );
542 }
543 }
544
545 deps = child.getDependencies();
546
547 if ( deps != null )
548 {
549 for ( Dependency dependency : deps )
550 {
551 depsMap.put( dependency.getManagementKey(), dependency );
552 }
553 }
554
555 child.setDependencies( new ArrayList<>( depsMap.values() ) );
556 }
557
558 private void assembleBuildInheritance( Model child, Model parent )
559 {
560 Build childBuild = child.getBuild();
561 Build parentBuild = parent.getBuild();
562
563 if ( parentBuild != null )
564 {
565 if ( childBuild == null )
566 {
567 childBuild = new Build();
568 child.setBuild( childBuild );
569 }
570
571 assembleBuildInheritance( childBuild, parentBuild, true );
572 }
573 }
574
575 private void assembleDistributionInheritance( Model child, Model parent, String childPathAdjustment,
576 boolean appendPaths )
577 {
578 if ( parent.getDistributionManagement() != null )
579 {
580 DistributionManagement parentDistMgmt = parent.getDistributionManagement();
581
582 DistributionManagement childDistMgmt = child.getDistributionManagement();
583
584 if ( childDistMgmt == null )
585 {
586 childDistMgmt = new DistributionManagement();
587
588 child.setDistributionManagement( childDistMgmt );
589 }
590
591 if ( childDistMgmt.getSite() == null )
592 {
593 if ( parentDistMgmt.getSite() != null )
594 {
595 Site site = new Site();
596
597 childDistMgmt.setSite( site );
598
599 site.setId( parentDistMgmt.getSite().getId() );
600
601 site.setName( parentDistMgmt.getSite().getName() );
602
603 site.setUrl( parentDistMgmt.getSite().getUrl() );
604
605 if ( site.getUrl() != null )
606 {
607 site.setUrl(
608 appendPath( site.getUrl(), child.getArtifactId(), childPathAdjustment, appendPaths ) );
609 }
610 }
611 }
612
613 if ( childDistMgmt.getRepository() == null )
614 {
615 if ( parentDistMgmt.getRepository() != null )
616 {
617 DeploymentRepository repository = copyDistributionRepository( parentDistMgmt.getRepository() );
618 childDistMgmt.setRepository( repository );
619 }
620 }
621
622 if ( childDistMgmt.getSnapshotRepository() == null )
623 {
624 if ( parentDistMgmt.getSnapshotRepository() != null )
625 {
626 DeploymentRepository repository =
627 copyDistributionRepository( parentDistMgmt.getSnapshotRepository() );
628 childDistMgmt.setSnapshotRepository( repository );
629 }
630 }
631
632 if ( StringUtils.isEmpty( childDistMgmt.getDownloadUrl() ) )
633 {
634 childDistMgmt.setDownloadUrl( parentDistMgmt.getDownloadUrl() );
635 }
636
637
638
639 }
640 }
641
642 private static DeploymentRepository copyDistributionRepository( DeploymentRepository parentRepository )
643 {
644 DeploymentRepository repository = new DeploymentRepository();
645
646 repository.setId( parentRepository.getId() );
647
648 repository.setName( parentRepository.getName() );
649
650 repository.setUrl( parentRepository.getUrl() );
651
652 repository.setLayout( parentRepository.getLayout() );
653
654 repository.setUniqueVersion( parentRepository.isUniqueVersion() );
655
656 return repository;
657 }
658
659
660 protected String appendPath( String parentPath, String childPath, String pathAdjustment, boolean appendPaths )
661 {
662 String uncleanPath = parentPath;
663
664 if ( appendPaths )
665 {
666 if ( pathAdjustment != null )
667 {
668 uncleanPath += "/" + pathAdjustment;
669 }
670
671 if ( childPath != null )
672 {
673 uncleanPath += "/" + childPath;
674 }
675 }
676
677 String cleanedPath = "";
678
679 int protocolIdx = uncleanPath.indexOf( "://" );
680
681 if ( protocolIdx > -1 )
682 {
683 cleanedPath = uncleanPath.substring( 0, protocolIdx + 3 );
684 uncleanPath = uncleanPath.substring( protocolIdx + 3 );
685 }
686
687 if ( uncleanPath.startsWith( "/" ) )
688 {
689 cleanedPath += "/";
690 }
691
692 return cleanedPath + resolvePath( uncleanPath );
693 }
694
695
696 private static String resolvePath( String uncleanPath )
697 {
698 LinkedList<String> pathElements = new LinkedList<>();
699
700 StringTokenizer tokenizer = new StringTokenizer( uncleanPath, "/" );
701
702 while ( tokenizer.hasMoreTokens() )
703 {
704 String token = tokenizer.nextToken();
705
706 switch ( token )
707 {
708 case "":
709
710 break;
711 case "..":
712 if ( pathElements.isEmpty() )
713 {
714
715
716
717 }
718 else
719 {
720 pathElements.removeLast();
721 }
722 break;
723 default:
724 pathElements.addLast( token );
725 break;
726 }
727 }
728
729 StringBuilder cleanedPath = new StringBuilder();
730
731 while ( !pathElements.isEmpty() )
732 {
733 cleanedPath.append( pathElements.removeFirst() );
734 if ( !pathElements.isEmpty() )
735 {
736 cleanedPath.append( '/' );
737 }
738 }
739
740 return cleanedPath.toString();
741 }
742
743 private static void mergeExtensionLists( Build childBuild, Build parentBuild )
744 {
745 for ( Extension e : parentBuild.getExtensions() )
746 {
747 if ( !childBuild.getExtensions().contains( e ) )
748 {
749 childBuild.addExtension( e );
750 }
751 }
752 }
753 }