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