1 package org.apache.maven.archiva.repository.project;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import java.util.ArrayList;
23 import java.util.Enumeration;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
29 import java.util.Properties;
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.maven.archiva.model.ArchivaModelCloner;
34 import org.apache.maven.archiva.model.ArchivaProjectModel;
35 import org.apache.maven.archiva.model.ArtifactReference;
36 import org.apache.maven.archiva.model.CiManagement;
37 import org.apache.maven.archiva.model.Dependency;
38 import org.apache.maven.archiva.model.Exclusion;
39 import org.apache.maven.archiva.model.Individual;
40 import org.apache.maven.archiva.model.IssueManagement;
41 import org.apache.maven.archiva.model.License;
42 import org.apache.maven.archiva.model.Organization;
43 import org.apache.maven.archiva.model.ProjectRepository;
44 import org.apache.maven.archiva.model.Scm;
49 * TODO: Should call this ProjectModelAncestry as it deals with the current project and its parent.
53 public class ProjectModelMerge
56 * Merge the contents of a project with it's parent project.
58 * @param mainProject the main project.
59 * @param parentProject the parent project to merge.
60 * @throws ProjectModelException if there was a problem merging the model.
62 public static ArchivaProjectModel merge( ArchivaProjectModel mainProject, ArchivaProjectModel parentProject )
63 throws ProjectModelException
65 if ( mainProject == null )
67 throw new ProjectModelException( "Cannot merge with a null main project." );
70 if ( parentProject == null )
72 throw new ProjectModelException( "Cannot merge with a null parent project." );
75 ArchivaProjectModel merged = new ArchivaProjectModel();
78 merged.setParentProject(mainProject.getParentProject());
79 merged.setArtifactId( mainProject.getArtifactId() );
80 merged.setPackaging( StringUtils.defaultIfEmpty( mainProject.getPackaging(), "jar" ) );
81 merged.setRelocation( mainProject.getRelocation() );
84 merged.setGroupId( merge( mainProject.getGroupId(), parentProject.getGroupId() ) );
85 merged.setVersion( merge( mainProject.getVersion(), parentProject.getVersion() ) );
86 merged.setName( merge( mainProject.getName(), parentProject.getName() ) );
87 merged.setUrl( merge( mainProject.getUrl(), parentProject.getUrl() ) );
88 merged.setDescription( merge( mainProject.getDescription(), parentProject.getDescription() ) );
90 merged.setOrigin( "merged" );
92 merged.setCiManagement( merge( mainProject.getCiManagement(), parentProject.getCiManagement() ) );
93 merged.setIndividuals( mergeIndividuals( mainProject.getIndividuals(), parentProject.getIndividuals() ) );
94 merged.setIssueManagement( merge( mainProject.getIssueManagement(), parentProject.getIssueManagement() ) );
95 merged.setLicenses( mergeLicenses( mainProject.getLicenses(), parentProject.getLicenses() ) );
96 merged.setOrganization( merge( mainProject.getOrganization(), parentProject.getOrganization() ) );
97 merged.setScm( merge( mainProject.getScm(), parentProject.getScm() ) );
98 merged.setRepositories( mergeRepositories( mainProject.getRepositories(), parentProject.getRepositories() ) );
99 merged.setDependencies( mergeDependencies( mainProject.getDependencies(), parentProject.getDependencies() ) );
100 merged.setDependencyManagement( mergeDependencyManagement( mainProject.getDependencyManagement(), parentProject
101 .getDependencyManagement() ) );
102 merged.setPlugins( mergePlugins( mainProject.getPlugins(), parentProject.getPlugins() ) );
103 merged.setReports( mergeReports( mainProject.getReports(), parentProject.getReports() ) );
104 merged.setProperties( merge( mainProject.getProperties(), parentProject.getProperties() ) );
109 private static Map<String, ArtifactReference> createArtifactReferenceMap( List<ArtifactReference> artifactReferences )
111 Map<String, ArtifactReference> ret = new HashMap<String, ArtifactReference>();
113 for ( ArtifactReference artifactReference : artifactReferences )
115 String key = toVersionlessArtifactKey( artifactReference );
116 ret.put( key, artifactReference );
122 private static Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
124 Map<String, Dependency> ret = new HashMap<String, Dependency>();
126 Iterator<Dependency> it = dependencies.iterator();
127 while ( it.hasNext() )
129 Dependency dep = it.next();
130 String key = toVersionlessDependencyKey( dep );
137 private static Map<String, Exclusion> createExclusionMap( List<Exclusion> exclusions )
139 Map<String, Exclusion> ret = new HashMap<String, Exclusion>();
141 Iterator<Exclusion> it = exclusions.iterator();
142 while ( it.hasNext() )
144 Exclusion exclusion = it.next();
145 String key = exclusion.getGroupId() + ":" + exclusion.getArtifactId();
146 ret.put( key, exclusion );
152 private static Map<String, License> createLicensesMap( List<License> licenses )
154 Map<String, License> ret = new HashMap<String, License>();
156 for ( License license : licenses )
158 // TODO: Change to 'id' when LicenseTypeMapper is created.
159 String key = license.getName();
160 ret.put( key, license );
166 private static Map<String, ProjectRepository> createRepositoriesMap( List<ProjectRepository> repositories )
168 Map<String, ProjectRepository> ret = new HashMap<String, ProjectRepository>();
170 for ( ProjectRepository repo : repositories )
172 // Should this really be using repo.id ?
173 String key = repo.getUrl();
174 ret.put( key, repo );
180 private static boolean empty( String val )
187 return ( val.trim().length() <= 0 );
190 private static ArtifactReference merge( ArtifactReference mainArtifactReference,
191 ArtifactReference parentArtifactReference )
193 if ( parentArtifactReference == null )
195 return mainArtifactReference;
198 if ( mainArtifactReference == null )
200 return ArchivaModelCloner.clone( parentArtifactReference );
203 ArtifactReference merged = new ArtifactReference();
206 merged.setGroupId( mainArtifactReference.getGroupId() );
207 merged.setArtifactId( mainArtifactReference.getArtifactId() );
210 merged.setVersion( merge( mainArtifactReference.getVersion(), parentArtifactReference.getVersion() ) );
211 merged.setClassifier( merge( mainArtifactReference.getClassifier(), parentArtifactReference.getClassifier() ) );
212 merged.setType( merge( mainArtifactReference.getType(), parentArtifactReference.getType() ) );
217 private static CiManagement merge( CiManagement mainCim, CiManagement parentCim )
219 if ( parentCim == null )
224 if ( mainCim == null )
226 return ArchivaModelCloner.clone( parentCim );
229 CiManagement merged = new CiManagement();
231 merged.setSystem( merge( mainCim.getSystem(), parentCim.getSystem() ) );
232 merged.setUrl( merge( mainCim.getUrl(), parentCim.getUrl() ) );
233 merged.setCiUrl( merge( mainCim.getCiUrl(), parentCim.getCiUrl() ) );
238 private static Dependency merge( Dependency mainDep, Dependency parentDep )
240 if ( parentDep == null )
245 if ( mainDep == null )
247 Dependency dep = ArchivaModelCloner.clone( parentDep );
248 dep.setFromParent( true );
252 Dependency merged = new Dependency();
254 merged.setFromParent( true );
257 merged.setGroupId( mainDep.getGroupId() );
258 merged.setArtifactId( mainDep.getArtifactId() );
261 merged.setVersion( merge( mainDep.getVersion(), parentDep.getVersion() ) );
262 merged.setClassifier( merge( mainDep.getClassifier(), parentDep.getClassifier() ) );
263 merged.setType( merge( mainDep.getType(), parentDep.getType() ) );
264 merged.setScope( merge( mainDep.getScope(), parentDep.getScope() ) );
265 if ( parentDep.isOptional() )
267 merged.setOptional( true );
270 merged.setSystemPath( merge( mainDep.getSystemPath(), parentDep.getSystemPath() ) );
271 merged.setUrl( merge( mainDep.getUrl(), parentDep.getUrl() ) );
272 merged.setExclusions( mergeExclusions( mainDep.getExclusions(), parentDep.getExclusions() ) );
277 private static IssueManagement merge( IssueManagement mainIssueManagement, IssueManagement parentIssueManagement )
279 if ( parentIssueManagement == null )
281 return mainIssueManagement;
284 if ( mainIssueManagement == null )
286 return ArchivaModelCloner.clone( parentIssueManagement );
289 IssueManagement merged = new IssueManagement();
291 merged.setSystem( merge( mainIssueManagement.getSystem(), parentIssueManagement.getSystem() ) );
292 merged.setUrl( merge( mainIssueManagement.getUrl(), parentIssueManagement.getUrl() ) );
293 merged.setIssueManagementUrl( merge( mainIssueManagement.getIssueManagementUrl(), parentIssueManagement.getIssueManagementUrl() ) );
298 private static Organization merge( Organization mainOrganization, Organization parentOrganization )
300 if ( parentOrganization == null )
302 return mainOrganization;
305 if ( mainOrganization == null )
307 return ArchivaModelCloner.clone( parentOrganization );
310 Organization merged = new Organization();
312 merged.setFavicon( merge( mainOrganization.getFavicon(), parentOrganization.getFavicon() ) );
313 merged.setOrganizationName( merge( mainOrganization.getOrganizationName(), parentOrganization.getOrganizationName() ) );
314 merged.setName( merge( mainOrganization.getName(), parentOrganization.getName() ) );
315 merged.setUrl( merge( mainOrganization.getUrl(), parentOrganization.getUrl() ) );
320 @SuppressWarnings("unchecked")
321 private static Properties merge( Properties mainProperties, Properties parentProperties )
323 if ( parentProperties == null )
325 return mainProperties;
328 if ( mainProperties == null )
330 return ArchivaModelCloner.clone( parentProperties );
333 Properties merged = new Properties();
334 merged.putAll(mainProperties);
336 Enumeration<String> keys = (Enumeration<String>) parentProperties.propertyNames();
337 while ( keys.hasMoreElements() )
339 String key = (String) keys.nextElement();
340 merged.put( key, merge( mainProperties.getProperty( key ), parentProperties.getProperty( key ) ) );
346 private static Scm merge( Scm mainScm, Scm parentScm )
348 if ( parentScm == null )
353 if ( mainScm == null )
355 return ArchivaModelCloner.clone( parentScm );
358 Scm merged = new Scm();
360 merged.setConnection( merge( mainScm.getConnection(), parentScm.getConnection() ) );
361 merged.setDeveloperConnection( merge( mainScm.getDeveloperConnection(), parentScm.getDeveloperConnection() ) );
362 merged.setUrl( merge( mainScm.getUrl(), parentScm.getUrl() ) );
367 private static String merge( String main, String parent )
369 if ( empty( main ) && !empty( parent ) )
377 private static List<ArtifactReference> mergeArtifactReferences( List<ArtifactReference> mainArtifactReferences, List<ArtifactReference> parentArtifactReferences )
379 if ( parentArtifactReferences == null )
381 return mainArtifactReferences;
384 if ( mainArtifactReferences == null )
386 return ArchivaModelCloner.cloneArtifactReferences( parentArtifactReferences );
389 List<ArtifactReference> merged = new ArrayList<ArtifactReference>();
391 Map<String, ArtifactReference> mainArtifactReferenceMap = createArtifactReferenceMap( mainArtifactReferences );
392 Map<String, ArtifactReference> parentArtifactReferenceMap = createArtifactReferenceMap( parentArtifactReferences );
394 for ( Map.Entry<String,ArtifactReference> entry : mainArtifactReferenceMap.entrySet() )
396 String key = entry.getKey();
397 ArtifactReference mainArtifactReference = (ArtifactReference) entry.getValue();
398 ArtifactReference parentArtifactReference = parentArtifactReferenceMap.get( key );
400 if ( parentArtifactReference == null )
402 merged.add( mainArtifactReference );
406 // Not merging. Local wins.
407 merged.add( merge( mainArtifactReference, parentArtifactReference ) );
414 private static List<Dependency> mergeDependencies( List<Dependency> mainDependencies, List<Dependency> parentDependencies )
416 if ( parentDependencies == null )
418 return mainDependencies;
421 if ( mainDependencies == null )
423 List<Dependency> merged = ArchivaModelCloner.cloneDependencies( parentDependencies );
424 Iterator<Dependency> it = merged.iterator();
425 while ( it.hasNext() )
427 Dependency dep = it.next();
428 dep.setFromParent( true );
433 List<Dependency> merged = new ArrayList<Dependency>();
435 Map<String, Dependency> mainDepMap = createDependencyMap( mainDependencies );
436 Map<String, Dependency> parentDepMap = createDependencyMap( parentDependencies );
437 Set<String> uniqueKeys = new HashSet<String>();
438 uniqueKeys.addAll( mainDepMap.keySet() );
439 uniqueKeys.addAll( parentDepMap.keySet() );
441 Iterator<String> it = uniqueKeys.iterator();
442 while ( it.hasNext() )
444 String key = it.next();
445 Dependency parentDep = parentDepMap.get( key );
446 Dependency mainDep = mainDepMap.get( key );
448 if ( parentDep == null )
450 // Means there is no parent dep to override main dep.
451 merged.add( mainDep );
455 // Parent dep exists (main doesn't have to).
456 // Merge the parent over the main dep.
457 merged.add( merge( mainDep, parentDep ) );
464 private static List<Dependency> mergeDependencyManagement( List<Dependency> mainDepMgmt, List<Dependency> parentDepMgmt )
466 if ( parentDepMgmt == null )
471 if ( mainDepMgmt == null )
473 List<Dependency> merged = ArchivaModelCloner.cloneDependencies( parentDepMgmt );
474 Iterator<Dependency> it = merged.iterator();
475 while ( it.hasNext() )
477 Dependency dep = it.next();
478 dep.setFromParent( true );
483 List<Dependency> merged = new ArrayList<Dependency>();
485 Map<String, Dependency> mainDepMap = createDependencyMap( mainDepMgmt );
486 Map<String, Dependency> parentDepMap = createDependencyMap( parentDepMgmt );
487 Set<String> uniqueKeys = new HashSet<String>();
488 uniqueKeys.addAll( mainDepMap.keySet() );
489 uniqueKeys.addAll( parentDepMap.keySet() );
491 Iterator<String> it = uniqueKeys.iterator();
492 while ( it.hasNext() )
494 String key = it.next();
495 Dependency parentDep = parentDepMap.get( key );
496 Dependency mainDep = mainDepMap.get( key );
498 if ( parentDep == null )
500 // Means there is no parent depMan entry to override main depMan.
501 merged.add( mainDep );
505 // Parent depMan entry exists (main doesn't have to).
506 // Merge the parent over the main depMan entry.
507 merged.add( merge( mainDep, parentDep ) );
514 public static List<Exclusion> mergeExclusions( List<Exclusion> mainExclusions, List<Exclusion> parentExclusions )
516 if ( parentExclusions == null )
518 return mainExclusions;
521 if ( mainExclusions == null )
523 return ArchivaModelCloner.cloneExclusions( parentExclusions );
526 List<Exclusion> merged = new ArrayList<Exclusion>();
528 Map<String, Exclusion> mainExclusionMap = createExclusionMap( mainExclusions );
529 Map<String, Exclusion> parentExclusionMap = createExclusionMap( parentExclusions );
531 for ( Map.Entry<String, Exclusion> entry : mainExclusionMap.entrySet() )
533 String key = entry.getKey();
534 Exclusion mainExclusion = entry.getValue();
535 Exclusion parentExclusion = parentExclusionMap.get( key );
537 if ( parentExclusion == null )
539 merged.add( mainExclusion );
543 merged.add( parentExclusion );
550 private static List<Individual> mergeIndividuals( List<Individual> mainIndividuals, List<Individual> parentIndividuals )
552 if ( parentIndividuals == null )
554 return mainIndividuals;
557 if ( mainIndividuals == null )
559 return ArchivaModelCloner.cloneIndividuals( parentIndividuals );
562 List<Individual> merged = ArchivaModelCloner.cloneIndividuals( mainIndividuals );
564 Iterator<Individual> it = parentIndividuals.iterator();
565 while ( it.hasNext() )
567 Individual parentIndividual = it.next();
569 if ( !mainIndividuals.contains( parentIndividual ) )
571 merged.add( parentIndividual );
578 private static List<License> mergeLicenses( List<License> mainLicenses, List<License> parentLicenses )
580 if ( parentLicenses == null )
585 if ( mainLicenses == null )
587 return ArchivaModelCloner.cloneLicenses( parentLicenses );
590 List<License> merged = new ArrayList<License>();
592 Map<String, License> mainLicensesMap = createLicensesMap( mainLicenses );
593 Map<String, License> parentLicensesMap = createLicensesMap( parentLicenses );
595 for ( Map.Entry<String, License> entry : mainLicensesMap.entrySet() )
597 String key = entry.getKey();
598 License mainLicense = entry.getValue();
599 License parentLicense = parentLicensesMap.get( key );
601 if ( parentLicense == null )
603 merged.add( mainLicense );
607 // Not merging. Local wins.
608 merged.add( parentLicense );
615 private static List<ArtifactReference> mergePlugins( List<ArtifactReference> mainPlugins, List<ArtifactReference> parentPlugins )
617 return mergeArtifactReferences( mainPlugins, parentPlugins );
620 private static List<ArtifactReference> mergeReports( List<ArtifactReference> mainReports, List<ArtifactReference> parentReports )
622 return mergeArtifactReferences( mainReports, parentReports );
625 private static List<ProjectRepository> mergeRepositories( List<ProjectRepository> mainRepositories, List<ProjectRepository> parentRepositories )
627 if ( parentRepositories == null )
629 return mainRepositories;
632 if ( mainRepositories == null )
634 return ArchivaModelCloner.cloneRepositories( parentRepositories );
637 List<ProjectRepository> merged = new ArrayList<ProjectRepository>();
639 Map<String, ProjectRepository> mainRepositoriesMap = createRepositoriesMap( mainRepositories );
640 Map<String, ProjectRepository> parentRepositoriesMap = createRepositoriesMap( parentRepositories );
642 for ( Map.Entry<String, ProjectRepository> entry : mainRepositoriesMap.entrySet() )
644 String key = entry.getKey();
645 ProjectRepository mainProjectRepository = entry.getValue();
646 ProjectRepository parentProjectRepository = parentRepositoriesMap.get( key );
648 if ( parentProjectRepository == null )
650 merged.add( mainProjectRepository );
654 // Not merging. Local wins.
655 merged.add( parentProjectRepository );
662 private static String toVersionlessArtifactKey( ArtifactReference artifactReference )
664 StringBuffer key = new StringBuffer();
666 key.append( artifactReference.getGroupId() ).append( ":" ).append( artifactReference.getArtifactId() );
667 key.append( StringUtils.defaultString( artifactReference.getClassifier() ) ).append( ":" );
668 key.append( artifactReference.getType() );
670 return key.toString();
673 private static String toVersionlessDependencyKey( Dependency dep )
675 StringBuffer key = new StringBuffer();
677 key.append( dep.getGroupId() ).append( ":" ).append( dep.getArtifactId() );
678 key.append( StringUtils.defaultString( dep.getClassifier() ) ).append( ":" );
679 key.append( dep.getType() );
681 return key.toString();