001 package org.apache.maven.project.inheritance; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import java.util.ArrayList; 023 import java.util.Iterator; 024 import java.util.LinkedHashMap; 025 import java.util.LinkedList; 026 import java.util.List; 027 import java.util.Map; 028 import java.util.Properties; 029 import java.util.StringTokenizer; 030 import java.util.TreeMap; 031 032 import org.apache.maven.model.Build; 033 import org.apache.maven.model.Dependency; 034 import org.apache.maven.model.DependencyManagement; 035 import org.apache.maven.model.DeploymentRepository; 036 import org.apache.maven.model.DistributionManagement; 037 import org.apache.maven.model.Extension; 038 import org.apache.maven.model.Model; 039 import org.apache.maven.model.PluginManagement; 040 import org.apache.maven.model.ReportPlugin; 041 import org.apache.maven.model.ReportSet; 042 import org.apache.maven.model.Reporting; 043 import org.apache.maven.model.Resource; 044 import org.apache.maven.model.Scm; 045 import org.apache.maven.model.Site; 046 import org.apache.maven.project.ModelUtils; 047 import org.codehaus.plexus.component.annotations.Component; 048 import org.codehaus.plexus.util.StringUtils; 049 import org.codehaus.plexus.util.xml.Xpp3Dom; 050 051 @Component( role = ModelInheritanceAssembler.class ) 052 public class DefaultModelInheritanceAssembler 053 implements ModelInheritanceAssembler 054 { 055 // TODO: Remove this! 056 @SuppressWarnings( "unchecked" ) 057 public void assembleBuildInheritance( Build childBuild, Build parentBuild, boolean handleAsInheritance ) 058 { 059 // The build has been set but we want to step in here and fill in 060 // values that have not been set by the child. 061 062 if ( childBuild.getSourceDirectory() == null ) 063 { 064 childBuild.setSourceDirectory( parentBuild.getSourceDirectory() ); 065 } 066 067 if ( childBuild.getScriptSourceDirectory() == null ) 068 { 069 childBuild.setScriptSourceDirectory( parentBuild.getScriptSourceDirectory() ); 070 } 071 072 if ( childBuild.getTestSourceDirectory() == null ) 073 { 074 childBuild.setTestSourceDirectory( parentBuild.getTestSourceDirectory() ); 075 } 076 077 if ( childBuild.getOutputDirectory() == null ) 078 { 079 childBuild.setOutputDirectory( parentBuild.getOutputDirectory() ); 080 } 081 082 if ( childBuild.getTestOutputDirectory() == null ) 083 { 084 childBuild.setTestOutputDirectory( parentBuild.getTestOutputDirectory() ); 085 } 086 087 // Extensions are accumulated 088 mergeExtensionLists( childBuild, parentBuild ); 089 090 if ( childBuild.getDirectory() == null ) 091 { 092 childBuild.setDirectory( parentBuild.getDirectory() ); 093 } 094 095 if ( childBuild.getDefaultGoal() == null ) 096 { 097 childBuild.setDefaultGoal( parentBuild.getDefaultGoal() ); 098 } 099 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 }