001package org.apache.maven.model.merge; 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.LinkedHashMap; 024import java.util.LinkedHashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import org.apache.maven.model.BuildBase; 030import org.apache.maven.model.CiManagement; 031import org.apache.maven.model.Dependency; 032import org.apache.maven.model.DeploymentRepository; 033import org.apache.maven.model.DistributionManagement; 034import org.apache.maven.model.Exclusion; 035import org.apache.maven.model.Extension; 036import org.apache.maven.model.InputLocation; 037import org.apache.maven.model.IssueManagement; 038import org.apache.maven.model.Model; 039import org.apache.maven.model.ModelBase; 040import org.apache.maven.model.Organization; 041import org.apache.maven.model.Plugin; 042import org.apache.maven.model.PluginExecution; 043import org.apache.maven.model.ReportPlugin; 044import org.apache.maven.model.ReportSet; 045import org.apache.maven.model.Repository; 046import org.apache.maven.model.RepositoryBase; 047import org.apache.maven.model.Scm; 048import org.apache.maven.model.Site; 049 050/** 051 * The domain-specific model merger for the Maven POM, overriding generic code from parent class when necessary with 052 * more adapted algorithms. 053 * 054 * @author Benjamin Bentmann 055 */ 056public class MavenModelMerger 057 extends ModelMerger 058{ 059 060 /** 061 * The hint key for the child path adjustment used during inheritance for URL calculations. 062 */ 063 public static final String CHILD_PATH_ADJUSTMENT = "child-path-adjustment"; 064 065 /** 066 * The context key for the artifact id of the target model. 067 */ 068 public static final String ARTIFACT_ID = "artifact-id"; 069 070 @Override 071 protected void mergeModel( Model target, Model source, boolean sourceDominant, Map<Object, Object> context ) 072 { 073 context.put( ARTIFACT_ID, target.getArtifactId() ); 074 075 super.mergeModel( target, source, sourceDominant, context ); 076 } 077 078 @Override 079 protected void mergeModel_Name( Model target, Model source, boolean sourceDominant, Map<Object, Object> context ) 080 { 081 String src = source.getName(); 082 if ( src != null ) 083 { 084 if ( sourceDominant ) 085 { 086 target.setName( src ); 087 target.setLocation( "name", source.getLocation( "name" ) ); 088 } 089 } 090 } 091 092 @Override 093 protected void mergeModel_Url( Model target, Model source, boolean sourceDominant, Map<Object, Object> context ) 094 { 095 String src = source.getUrl(); 096 if ( src != null ) 097 { 098 if ( sourceDominant ) 099 { 100 target.setUrl( src ); 101 target.setLocation( "url", source.getLocation( "url" ) ); 102 } 103 else if ( target.getUrl() == null ) 104 { 105 target.setUrl( extrapolateChildUrl( src, context ) ); 106 target.setLocation( "url", source.getLocation( "url" ) ); 107 } 108 } 109 } 110 111 /* 112 * TODO: Whether the merge continues recursively into an existing node or not could be an option for the generated 113 * merger 114 */ 115 @Override 116 protected void mergeModel_Organization( Model target, Model source, boolean sourceDominant, 117 Map<Object, Object> context ) 118 { 119 Organization src = source.getOrganization(); 120 if ( src != null ) 121 { 122 Organization tgt = target.getOrganization(); 123 if ( tgt == null ) 124 { 125 tgt = new Organization(); 126 tgt.setLocation( "", src.getLocation( "" ) ); 127 target.setOrganization( tgt ); 128 mergeOrganization( tgt, src, sourceDominant, context ); 129 } 130 } 131 } 132 133 @Override 134 protected void mergeModel_IssueManagement( Model target, Model source, boolean sourceDominant, 135 Map<Object, Object> context ) 136 { 137 IssueManagement src = source.getIssueManagement(); 138 if ( src != null ) 139 { 140 IssueManagement tgt = target.getIssueManagement(); 141 if ( tgt == null ) 142 { 143 tgt = new IssueManagement(); 144 tgt.setLocation( "", src.getLocation( "" ) ); 145 target.setIssueManagement( tgt ); 146 mergeIssueManagement( tgt, src, sourceDominant, context ); 147 } 148 } 149 } 150 151 @Override 152 protected void mergeModel_CiManagement( Model target, Model source, boolean sourceDominant, 153 Map<Object, Object> context ) 154 { 155 CiManagement src = source.getCiManagement(); 156 if ( src != null ) 157 { 158 CiManagement tgt = target.getCiManagement(); 159 if ( tgt == null ) 160 { 161 tgt = new CiManagement(); 162 tgt.setLocation( "", src.getLocation( "" ) ); 163 target.setCiManagement( tgt ); 164 mergeCiManagement( tgt, src, sourceDominant, context ); 165 } 166 } 167 } 168 169 @Override 170 protected void mergeModel_ModelVersion( Model target, Model source, boolean sourceDominant, 171 Map<Object, Object> context ) 172 { 173 // neither inherited nor injected 174 } 175 176 @Override 177 protected void mergeModel_ArtifactId( Model target, Model source, boolean sourceDominant, 178 Map<Object, Object> context ) 179 { 180 // neither inherited nor injected 181 } 182 183 @Override 184 protected void mergeModel_Profiles( Model target, Model source, boolean sourceDominant, 185 Map<Object, Object> context ) 186 { 187 // neither inherited nor injected 188 } 189 190 @Override 191 protected void mergeModel_Prerequisites( Model target, Model source, boolean sourceDominant, 192 Map<Object, Object> context ) 193 { 194 // neither inherited nor injected 195 } 196 197 @Override 198 protected void mergeModel_Licenses( Model target, Model source, boolean sourceDominant, 199 Map<Object, Object> context ) 200 { 201 if ( target.getLicenses().isEmpty() ) 202 { 203 target.setLicenses( new ArrayList<>( source.getLicenses() ) ); 204 } 205 } 206 207 @Override 208 protected void mergeModel_Developers( Model target, Model source, boolean sourceDominant, 209 Map<Object, Object> context ) 210 { 211 if ( target.getDevelopers().isEmpty() ) 212 { 213 target.setDevelopers( new ArrayList<>( source.getDevelopers() ) ); 214 } 215 } 216 217 @Override 218 protected void mergeModel_Contributors( Model target, Model source, boolean sourceDominant, 219 Map<Object, Object> context ) 220 { 221 if ( target.getContributors().isEmpty() ) 222 { 223 target.setContributors( new ArrayList<>( source.getContributors() ) ); 224 } 225 } 226 227 @Override 228 protected void mergeModel_MailingLists( Model target, Model source, boolean sourceDominant, 229 Map<Object, Object> context ) 230 { 231 if ( target.getMailingLists().isEmpty() ) 232 { 233 target.setMailingLists( new ArrayList<>( source.getMailingLists() ) ); 234 } 235 } 236 237 @Override 238 protected void mergeModelBase_Modules( ModelBase target, ModelBase source, boolean sourceDominant, 239 Map<Object, Object> context ) 240 { 241 List<String> src = source.getModules(); 242 if ( !src.isEmpty() && sourceDominant ) 243 { 244 List<Integer> indices = new ArrayList<>(); 245 List<String> tgt = target.getModules(); 246 Set<String> excludes = new LinkedHashSet<>( tgt ); 247 List<String> merged = new ArrayList<>( tgt.size() + src.size() ); 248 merged.addAll( tgt ); 249 for ( int i = 0, n = tgt.size(); i < n; i++ ) 250 { 251 indices.add( i ); 252 } 253 for ( int i = 0, n = src.size(); i < n; i++ ) 254 { 255 String s = src.get( i ); 256 if ( !excludes.contains( s ) ) 257 { 258 merged.add( s ); 259 indices.add( ~i ); 260 } 261 } 262 target.setModules( merged ); 263 target.setLocation( "modules", InputLocation.merge( target.getLocation( "modules" ), 264 source.getLocation( "modules" ), indices ) ); 265 } 266 } 267 268 /* 269 * TODO: The order of the merged list could be controlled by an attribute in the model association: target-first, 270 * source-first, dominant-first, recessive-first 271 */ 272 @Override 273 protected void mergeModelBase_Repositories( ModelBase target, ModelBase source, boolean sourceDominant, 274 Map<Object, Object> context ) 275 { 276 List<Repository> src = source.getRepositories(); 277 if ( !src.isEmpty() ) 278 { 279 List<Repository> tgt = target.getRepositories(); 280 Map<Object, Repository> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 ); 281 282 List<Repository> dominant, recessive; 283 if ( sourceDominant ) 284 { 285 dominant = src; 286 recessive = tgt; 287 } 288 else 289 { 290 dominant = tgt; 291 recessive = src; 292 } 293 294 for ( Repository element : dominant ) 295 { 296 Object key = getRepositoryKey( element ); 297 merged.put( key, element ); 298 } 299 300 for ( Repository element : recessive ) 301 { 302 Object key = getRepositoryKey( element ); 303 if ( !merged.containsKey( key ) ) 304 { 305 merged.put( key, element ); 306 } 307 } 308 309 target.setRepositories( new ArrayList<>( merged.values() ) ); 310 } 311 } 312 313 @Override 314 protected void mergeModelBase_PluginRepositories( ModelBase target, ModelBase source, boolean sourceDominant, 315 Map<Object, Object> context ) 316 { 317 List<Repository> src = source.getPluginRepositories(); 318 if ( !src.isEmpty() ) 319 { 320 List<Repository> tgt = target.getPluginRepositories(); 321 Map<Object, Repository> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 ); 322 323 List<Repository> dominant, recessive; 324 if ( sourceDominant ) 325 { 326 dominant = src; 327 recessive = tgt; 328 } 329 else 330 { 331 dominant = tgt; 332 recessive = src; 333 } 334 335 for ( Repository element : dominant ) 336 { 337 Object key = getRepositoryKey( element ); 338 merged.put( key, element ); 339 } 340 341 for ( Repository element : recessive ) 342 { 343 Object key = getRepositoryKey( element ); 344 if ( !merged.containsKey( key ) ) 345 { 346 merged.put( key, element ); 347 } 348 } 349 350 target.setPluginRepositories( new ArrayList<>( merged.values() ) ); 351 } 352 } 353 354 /* 355 * TODO: Whether duplicates should be removed looks like an option for the generated merger. 356 */ 357 @Override 358 protected void mergeBuildBase_Filters( BuildBase target, BuildBase source, boolean sourceDominant, 359 Map<Object, Object> context ) 360 { 361 List<String> src = source.getFilters(); 362 if ( !src.isEmpty() ) 363 { 364 List<String> tgt = target.getFilters(); 365 Set<String> excludes = new LinkedHashSet<>( tgt ); 366 List<String> merged = new ArrayList<>( tgt.size() + src.size() ); 367 merged.addAll( tgt ); 368 for ( String s : src ) 369 { 370 if ( !excludes.contains( s ) ) 371 { 372 merged.add( s ); 373 } 374 } 375 target.setFilters( merged ); 376 } 377 } 378 379 @Override 380 protected void mergeBuildBase_Resources( BuildBase target, BuildBase source, boolean sourceDominant, 381 Map<Object, Object> context ) 382 { 383 if ( sourceDominant || target.getResources().isEmpty() ) 384 { 385 super.mergeBuildBase_Resources( target, source, sourceDominant, context ); 386 } 387 } 388 389 @Override 390 protected void mergeBuildBase_TestResources( BuildBase target, BuildBase source, boolean sourceDominant, 391 Map<Object, Object> context ) 392 { 393 if ( sourceDominant || target.getTestResources().isEmpty() ) 394 { 395 super.mergeBuildBase_TestResources( target, source, sourceDominant, context ); 396 } 397 } 398 399 @Override 400 protected void mergeDistributionManagement_Repository( DistributionManagement target, 401 DistributionManagement source, boolean sourceDominant, 402 Map<Object, Object> context ) 403 { 404 DeploymentRepository src = source.getRepository(); 405 if ( src != null ) 406 { 407 DeploymentRepository tgt = target.getRepository(); 408 if ( sourceDominant || tgt == null ) 409 { 410 tgt = new DeploymentRepository(); 411 tgt.setLocation( "", src.getLocation( "" ) ); 412 target.setRepository( tgt ); 413 mergeDeploymentRepository( tgt, src, sourceDominant, context ); 414 } 415 } 416 } 417 418 @Override 419 protected void mergeDistributionManagement_SnapshotRepository( DistributionManagement target, 420 DistributionManagement source, 421 boolean sourceDominant, 422 Map<Object, Object> context ) 423 { 424 DeploymentRepository src = source.getSnapshotRepository(); 425 if ( src != null ) 426 { 427 DeploymentRepository tgt = target.getSnapshotRepository(); 428 if ( sourceDominant || tgt == null ) 429 { 430 tgt = new DeploymentRepository(); 431 tgt.setLocation( "", src.getLocation( "" ) ); 432 target.setSnapshotRepository( tgt ); 433 mergeDeploymentRepository( tgt, src, sourceDominant, context ); 434 } 435 } 436 } 437 438 @Override 439 protected void mergeDistributionManagement_Site( DistributionManagement target, DistributionManagement source, 440 boolean sourceDominant, Map<Object, Object> context ) 441 { 442 Site src = source.getSite(); 443 if ( src != null ) 444 { 445 Site tgt = target.getSite(); 446 if ( sourceDominant || tgt == null ) 447 { 448 tgt = new Site(); 449 tgt.setLocation( "", src.getLocation( "" ) ); 450 target.setSite( tgt ); 451 mergeSite( tgt, src, sourceDominant, context ); 452 } 453 } 454 } 455 456 @Override 457 protected void mergeSite_Url( Site target, Site source, boolean sourceDominant, Map<Object, Object> context ) 458 { 459 String src = source.getUrl(); 460 if ( src != null ) 461 { 462 if ( sourceDominant ) 463 { 464 target.setUrl( src ); 465 target.setLocation( "url", source.getLocation( "url" ) ); 466 } 467 else if ( target.getUrl() == null ) 468 { 469 target.setUrl( extrapolateChildUrl( src, context ) ); 470 target.setLocation( "url", source.getLocation( "url" ) ); 471 } 472 } 473 } 474 475 @Override 476 protected void mergeScm_Url( Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context ) 477 { 478 String src = source.getUrl(); 479 if ( src != null ) 480 { 481 if ( sourceDominant ) 482 { 483 target.setUrl( src ); 484 target.setLocation( "url", source.getLocation( "url" ) ); 485 } 486 else if ( target.getUrl() == null ) 487 { 488 target.setUrl( extrapolateChildUrl( src, context ) ); 489 target.setLocation( "url", source.getLocation( "url" ) ); 490 } 491 } 492 } 493 494 @Override 495 protected void mergeScm_Connection( Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context ) 496 { 497 String src = source.getConnection(); 498 if ( src != null ) 499 { 500 if ( sourceDominant ) 501 { 502 target.setConnection( src ); 503 target.setLocation( "connection", source.getLocation( "connection" ) ); 504 } 505 else if ( target.getConnection() == null ) 506 { 507 target.setConnection( extrapolateChildUrl( src, context ) ); 508 target.setLocation( "connection", source.getLocation( "connection" ) ); 509 } 510 } 511 } 512 513 @Override 514 protected void mergeScm_DeveloperConnection( Scm target, Scm source, boolean sourceDominant, 515 Map<Object, Object> context ) 516 { 517 String src = source.getDeveloperConnection(); 518 if ( src != null ) 519 { 520 if ( sourceDominant ) 521 { 522 target.setDeveloperConnection( src ); 523 target.setLocation( "developerConnection", source.getLocation( "developerConnection" ) ); 524 } 525 else if ( target.getDeveloperConnection() == null ) 526 { 527 target.setDeveloperConnection( extrapolateChildUrl( src, context ) ); 528 target.setLocation( "developerConnection", source.getLocation( "developerConnection" ) ); 529 } 530 } 531 } 532 533 @Override 534 protected void mergePlugin_Executions( Plugin target, Plugin source, boolean sourceDominant, 535 Map<Object, Object> context ) 536 { 537 List<PluginExecution> src = source.getExecutions(); 538 if ( !src.isEmpty() ) 539 { 540 List<PluginExecution> tgt = target.getExecutions(); 541 Map<Object, PluginExecution> merged = 542 new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 ); 543 544 for ( PluginExecution element : src ) 545 { 546 if ( sourceDominant 547 || ( element.getInherited() != null ? element.isInherited() : source.isInherited() ) ) 548 { 549 Object key = getPluginExecutionKey( element ); 550 merged.put( key, element ); 551 } 552 } 553 554 for ( PluginExecution element : tgt ) 555 { 556 Object key = getPluginExecutionKey( element ); 557 PluginExecution existing = merged.get( key ); 558 if ( existing != null ) 559 { 560 mergePluginExecution( element, existing, sourceDominant, context ); 561 } 562 merged.put( key, element ); 563 } 564 565 target.setExecutions( new ArrayList<>( merged.values() ) ); 566 } 567 } 568 569 @Override 570 protected void mergePluginExecution_Goals( PluginExecution target, PluginExecution source, boolean sourceDominant, 571 Map<Object, Object> context ) 572 { 573 List<String> src = source.getGoals(); 574 if ( !src.isEmpty() ) 575 { 576 List<String> tgt = target.getGoals(); 577 Set<String> excludes = new LinkedHashSet<>( tgt ); 578 List<String> merged = new ArrayList<>( tgt.size() + src.size() ); 579 merged.addAll( tgt ); 580 for ( String s : src ) 581 { 582 if ( !excludes.contains( s ) ) 583 { 584 merged.add( s ); 585 } 586 } 587 target.setGoals( merged ); 588 } 589 } 590 591 @Override 592 protected void mergeReportPlugin_ReportSets( ReportPlugin target, ReportPlugin source, boolean sourceDominant, 593 Map<Object, Object> context ) 594 { 595 List<ReportSet> src = source.getReportSets(); 596 if ( !src.isEmpty() ) 597 { 598 List<ReportSet> tgt = target.getReportSets(); 599 Map<Object, ReportSet> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 ); 600 601 for ( ReportSet rset : src ) 602 { 603 if ( sourceDominant || ( rset.getInherited() != null ? rset.isInherited() : source.isInherited() ) ) 604 { 605 Object key = getReportSetKey( rset ); 606 merged.put( key, rset ); 607 } 608 } 609 610 for ( ReportSet element : tgt ) 611 { 612 Object key = getReportSetKey( element ); 613 ReportSet existing = merged.get( key ); 614 if ( existing != null ) 615 { 616 mergeReportSet( element, existing, sourceDominant, context ); 617 } 618 merged.put( key, element ); 619 } 620 621 target.setReportSets( new ArrayList<>( merged.values() ) ); 622 } 623 } 624 625 @Override 626 protected Object getDependencyKey( Dependency dependency ) 627 { 628 return dependency.getManagementKey(); 629 } 630 631 @Override 632 protected Object getPluginKey( Plugin plugin ) 633 { 634 return plugin.getKey(); 635 } 636 637 @Override 638 protected Object getPluginExecutionKey( PluginExecution pluginExecution ) 639 { 640 return pluginExecution.getId(); 641 } 642 643 @Override 644 protected Object getReportPluginKey( ReportPlugin reportPlugin ) 645 { 646 return reportPlugin.getKey(); 647 } 648 649 @Override 650 protected Object getReportSetKey( ReportSet reportSet ) 651 { 652 return reportSet.getId(); 653 } 654 655 @Override 656 protected Object getRepositoryBaseKey( RepositoryBase repositoryBase ) 657 { 658 return repositoryBase.getId(); 659 } 660 661 @Override 662 protected Object getExtensionKey( Extension extension ) 663 { 664 return extension.getGroupId() + ':' + extension.getArtifactId(); 665 } 666 667 @Override 668 protected Object getExclusionKey( Exclusion exclusion ) 669 { 670 return exclusion.getGroupId() + ':' + exclusion.getArtifactId(); 671 } 672 673 protected String extrapolateChildUrl( String parentUrl, Map<Object, Object> context ) 674 { 675 return parentUrl; 676 } 677 678}