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