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 org.apache.commons.lang.StringUtils;
23 import org.apache.maven.archiva.model.ArchivaModelCloner;
24 import org.apache.maven.archiva.model.ArchivaProjectModel;
25 import org.apache.maven.archiva.model.ArtifactReference;
26 import org.apache.maven.archiva.model.CiManagement;
27 import org.apache.maven.archiva.model.Dependency;
28 import org.apache.maven.archiva.model.Exclusion;
29 import org.apache.maven.archiva.model.Individual;
30 import org.apache.maven.archiva.model.IssueManagement;
31 import org.apache.maven.archiva.model.License;
32 import org.apache.maven.archiva.model.Organization;
33 import org.apache.maven.archiva.model.ProjectRepository;
34 import org.apache.maven.archiva.model.Scm;
36 import java.util.ArrayList;
37 import java.util.Enumeration;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.Iterator;
41 import java.util.List;
43 import java.util.Properties;
45 import java.util.Map.Entry;
50 * TODO: Should call this ProjectModelAncestry as it deals with the current project and its parent.
54 public class ProjectModelMerge
57 * Merge the contents of a project with it's parent project.
59 * @param mainProject the main project.
60 * @param parentProject the parent project to merge.
61 * @throws ProjectModelException if there was a problem merging the model.
63 public static ArchivaProjectModel merge( ArchivaProjectModel mainProject, ArchivaProjectModel parentProject )
64 throws ProjectModelException
66 if ( mainProject == null )
68 throw new ProjectModelException( "Cannot merge with a null main project." );
71 if ( parentProject == null )
73 throw new ProjectModelException( "Cannot merge with a null parent project." );
76 ArchivaProjectModel merged = new ArchivaProjectModel();
79 merged.setParentProject(mainProject.getParentProject());
80 merged.setArtifactId( mainProject.getArtifactId() );
81 merged.setPackaging( StringUtils.defaultIfEmpty( mainProject.getPackaging(), "jar" ) );
82 merged.setRelocation( mainProject.getRelocation() );
85 merged.setGroupId( merge( mainProject.getGroupId(), parentProject.getGroupId() ) );
86 merged.setVersion( merge( mainProject.getVersion(), parentProject.getVersion() ) );
87 merged.setName( merge( mainProject.getName(), parentProject.getName() ) );
88 merged.setUrl( merge( mainProject.getUrl(), parentProject.getUrl() ) );
89 merged.setDescription( merge( mainProject.getDescription(), parentProject.getDescription() ) );
91 merged.setOrigin( "merged" );
93 merged.setCiManagement( merge( mainProject.getCiManagement(), parentProject.getCiManagement() ) );
94 merged.setIndividuals( mergeIndividuals( mainProject.getIndividuals(), parentProject.getIndividuals() ) );
95 merged.setIssueManagement( merge( mainProject.getIssueManagement(), parentProject.getIssueManagement() ) );
96 merged.setLicenses( mergeLicenses( mainProject.getLicenses(), parentProject.getLicenses() ) );
97 merged.setOrganization( merge( mainProject.getOrganization(), parentProject.getOrganization() ) );
98 merged.setScm( merge( mainProject.getScm(), parentProject.getScm() ) );
99 merged.setRepositories( mergeRepositories( mainProject.getRepositories(), parentProject.getRepositories() ) );
100 merged.setDependencies( mergeDependencies( mainProject.getDependencies(), parentProject.getDependencies() ) );
101 merged.setDependencyManagement( mergeDependencyManagement( mainProject.getDependencyManagement(), parentProject
102 .getDependencyManagement() ) );
103 merged.setPlugins( mergePlugins( mainProject.getPlugins(), parentProject.getPlugins() ) );
104 merged.setReports( mergeReports( mainProject.getReports(), parentProject.getReports() ) );
105 merged.setProperties( merge( mainProject.getProperties(), parentProject.getProperties() ) );
110 private static Map createArtifactReferenceMap( List artifactReferences )
112 Map ret = new HashMap();
114 Iterator it = artifactReferences.iterator();
115 while ( it.hasNext() )
117 ArtifactReference artifactReference = (ArtifactReference) it.next();
118 String key = toVersionlessArtifactKey( artifactReference );
119 ret.put( key, artifactReference );
125 private static Map createDependencyMap( List dependencies )
127 Map ret = new HashMap();
129 Iterator it = dependencies.iterator();
130 while ( it.hasNext() )
132 Dependency dep = (Dependency) it.next();
133 String key = toVersionlessDependencyKey( dep );
140 private static Map createExclusionMap( List exclusions )
142 Map ret = new HashMap();
144 Iterator it = exclusions.iterator();
145 while ( it.hasNext() )
147 Exclusion exclusion = (Exclusion) it.next();
148 String key = exclusion.getGroupId() + ":" + exclusion.getArtifactId();
149 ret.put( key, exclusion );
155 private static Map createLicensesMap( List licenses )
157 Map ret = new HashMap();
159 Iterator it = licenses.iterator();
160 while ( it.hasNext() )
162 License license = (License) it.next();
163 // TODO: Change to 'id' when LicenseTypeMapper is created.
164 String key = license.getName();
165 ret.put( key, license );
171 private static Map createRepositoriesMap( List repositories )
173 Map ret = new HashMap();
175 Iterator it = repositories.iterator();
176 while ( it.hasNext() )
178 ProjectRepository repo = (ProjectRepository) it.next();
179 // Should this really be using repo.id ?
180 String key = repo.getUrl();
181 ret.put( key, repo );
187 private static boolean empty( String val )
194 return ( val.trim().length() <= 0 );
197 private static ArtifactReference merge( ArtifactReference mainArtifactReference,
198 ArtifactReference parentArtifactReference )
200 if ( parentArtifactReference == null )
202 return mainArtifactReference;
205 if ( mainArtifactReference == null )
207 return ArchivaModelCloner.clone( parentArtifactReference );
210 ArtifactReference merged = new ArtifactReference();
213 merged.setGroupId( mainArtifactReference.getGroupId() );
214 merged.setArtifactId( mainArtifactReference.getArtifactId() );
217 merged.setVersion( merge( mainArtifactReference.getVersion(), parentArtifactReference.getVersion() ) );
218 merged.setClassifier( merge( mainArtifactReference.getClassifier(), parentArtifactReference.getClassifier() ) );
219 merged.setType( merge( mainArtifactReference.getType(), parentArtifactReference.getType() ) );
224 private static CiManagement merge( CiManagement mainCim, CiManagement parentCim )
226 if ( parentCim == null )
231 if ( mainCim == null )
233 return ArchivaModelCloner.clone( parentCim );
236 CiManagement merged = new CiManagement();
238 merged.setSystem( merge( mainCim.getSystem(), parentCim.getSystem() ) );
239 merged.setUrl( merge( mainCim.getUrl(), parentCim.getUrl() ) );
244 private static Dependency merge( Dependency mainDep, Dependency parentDep )
246 if ( parentDep == null )
251 if ( mainDep == null )
253 Dependency dep = ArchivaModelCloner.clone( parentDep );
254 dep.setFromParent( true );
258 Dependency merged = new Dependency();
260 merged.setFromParent( true );
263 merged.setGroupId( mainDep.getGroupId() );
264 merged.setArtifactId( mainDep.getArtifactId() );
267 merged.setVersion( merge( mainDep.getVersion(), parentDep.getVersion() ) );
268 merged.setClassifier( merge( mainDep.getClassifier(), parentDep.getClassifier() ) );
269 merged.setType( merge( mainDep.getType(), parentDep.getType() ) );
270 merged.setScope( merge( mainDep.getScope(), parentDep.getScope() ) );
271 if ( parentDep.isOptional() )
273 merged.setOptional( true );
276 merged.setSystemPath( merge( mainDep.getSystemPath(), parentDep.getSystemPath() ) );
277 merged.setUrl( merge( mainDep.getUrl(), parentDep.getUrl() ) );
278 merged.setExclusions( mergeExclusions( mainDep.getExclusions(), parentDep.getExclusions() ) );
283 private static IssueManagement merge( IssueManagement mainIssueManagement, IssueManagement parentIssueManagement )
285 if ( parentIssueManagement == null )
287 return mainIssueManagement;
290 if ( mainIssueManagement == null )
292 return ArchivaModelCloner.clone( parentIssueManagement );
295 IssueManagement merged = new IssueManagement();
297 merged.setSystem( merge( mainIssueManagement.getSystem(), parentIssueManagement.getSystem() ) );
298 merged.setUrl( merge( mainIssueManagement.getUrl(), parentIssueManagement.getUrl() ) );
303 private static Organization merge( Organization mainOrganization, Organization parentOrganization )
305 if ( parentOrganization == null )
307 return mainOrganization;
310 if ( mainOrganization == null )
312 return ArchivaModelCloner.clone( parentOrganization );
315 Organization merged = new Organization();
317 merged.setFavicon( merge( mainOrganization.getFavicon(), parentOrganization.getFavicon() ) );
318 merged.setName( merge( mainOrganization.getName(), parentOrganization.getName() ) );
319 merged.setUrl( merge( mainOrganization.getUrl(), parentOrganization.getUrl() ) );
324 private static Properties merge( Properties mainProperties, Properties parentProperties )
326 if ( parentProperties == null )
328 return mainProperties;
331 if ( mainProperties == null )
333 return ArchivaModelCloner.clone( parentProperties );
336 Properties merged = new Properties();
337 merged.putAll(mainProperties);
339 Enumeration keys = parentProperties.propertyNames();
340 while ( keys.hasMoreElements() )
342 String key = (String) keys.nextElement();
343 merged.put( key, merge( mainProperties.getProperty( key ), parentProperties.getProperty( key ) ) );
349 private static Scm merge( Scm mainScm, Scm parentScm )
351 if ( parentScm == null )
356 if ( mainScm == null )
358 return ArchivaModelCloner.clone( parentScm );
361 Scm merged = new Scm();
363 merged.setConnection( merge( mainScm.getConnection(), parentScm.getConnection() ) );
364 merged.setDeveloperConnection( merge( mainScm.getDeveloperConnection(), parentScm.getDeveloperConnection() ) );
365 merged.setUrl( merge( mainScm.getUrl(), parentScm.getUrl() ) );
370 private static String merge( String main, String parent )
372 if ( empty( main ) && !empty( parent ) )
380 private static List mergeArtifactReferences( List mainArtifactReferences, List parentArtifactReferences )
382 if ( parentArtifactReferences == null )
384 return mainArtifactReferences;
387 if ( mainArtifactReferences == null )
389 return ArchivaModelCloner.cloneLicenses( parentArtifactReferences );
392 List merged = new ArrayList();
394 Map mainArtifactReferenceMap = createArtifactReferenceMap( mainArtifactReferences );
395 Map parentArtifactReferenceMap = createArtifactReferenceMap( parentArtifactReferences );
397 Iterator it = mainArtifactReferenceMap.entrySet().iterator();
398 while ( it.hasNext() )
400 Map.Entry entry = (Entry) it.next();
401 String key = (String) entry.getKey();
402 ArtifactReference mainArtifactReference = (ArtifactReference) entry.getValue();
403 ArtifactReference parentArtifactReference = (ArtifactReference) parentArtifactReferenceMap.get( key );
405 if ( parentArtifactReference == null )
407 merged.add( mainArtifactReference );
411 // Not merging. Local wins.
412 merged.add( merge( mainArtifactReference, parentArtifactReference ) );
419 private static List mergeDependencies( List mainDependencies, List parentDependencies )
421 if ( parentDependencies == null )
423 return mainDependencies;
426 if ( mainDependencies == null )
428 List merged = ArchivaModelCloner.cloneDependencies( parentDependencies );
429 Iterator it = merged.iterator();
430 while ( it.hasNext() )
432 Dependency dep = (Dependency) it.next();
433 dep.setFromParent( true );
438 List merged = new ArrayList();
440 Map mainDepMap = createDependencyMap( mainDependencies );
441 Map parentDepMap = createDependencyMap( parentDependencies );
442 Set uniqueKeys = new HashSet();
443 uniqueKeys.addAll( mainDepMap.keySet() );
444 uniqueKeys.addAll( parentDepMap.keySet() );
446 Iterator it = uniqueKeys.iterator();
447 while ( it.hasNext() )
449 String key = (String) it.next();
450 Dependency parentDep = (Dependency) parentDepMap.get( key );
451 Dependency mainDep = (Dependency) mainDepMap.get( key );
453 if ( parentDep == null )
455 // Means there is no parent dep to override main dep.
456 merged.add( mainDep );
460 // Parent dep exists (main doesn't have to).
461 // Merge the parent over the main dep.
462 merged.add( merge( mainDep, parentDep ) );
469 private static List mergeDependencyManagement( List mainDepMgmt, List parentDepMgmt )
471 if ( parentDepMgmt == null )
476 if ( mainDepMgmt == null )
478 List merged = ArchivaModelCloner.cloneDependencies( parentDepMgmt );
479 Iterator it = merged.iterator();
480 while ( it.hasNext() )
482 Dependency dep = (Dependency) it.next();
483 dep.setFromParent( true );
488 List merged = new ArrayList();
490 Map mainDepMap = createDependencyMap( mainDepMgmt );
491 Map parentDepMap = createDependencyMap( parentDepMgmt );
492 Set uniqueKeys = new HashSet();
493 uniqueKeys.addAll( mainDepMap.keySet() );
494 uniqueKeys.addAll( parentDepMap.keySet() );
496 Iterator it = uniqueKeys.iterator();
497 while ( it.hasNext() )
499 String key = (String) it.next();
500 Dependency parentDep = (Dependency) parentDepMap.get( key );
501 Dependency mainDep = (Dependency) mainDepMap.get( key );
503 if ( parentDep == null )
505 // Means there is no parent depMan entry to override main depMan.
506 merged.add( mainDep );
510 // Parent depMan entry exists (main doesn't have to).
511 // Merge the parent over the main depMan entry.
512 merged.add( merge( mainDep, parentDep ) );
519 public static List mergeExclusions( List mainExclusions, List parentExclusions )
521 if ( parentExclusions == null )
523 return mainExclusions;
526 if ( mainExclusions == null )
528 return ArchivaModelCloner.cloneExclusions( parentExclusions );
531 List merged = new ArrayList();
533 Map mainExclusionMap = createExclusionMap( mainExclusions );
534 Map parentExclusionMap = createExclusionMap( parentExclusions );
536 Iterator it = mainExclusionMap.entrySet().iterator();
537 while ( it.hasNext() )
539 Map.Entry entry = (Entry) it.next();
540 String key = (String) entry.getKey();
541 Exclusion mainExclusion = (Exclusion) entry.getValue();
542 Exclusion parentExclusion = (Exclusion) parentExclusionMap.get( key );
544 if ( parentExclusion == null )
546 merged.add( mainExclusion );
550 merged.add( parentExclusion );
557 private static List mergeIndividuals( List mainIndividuals, List parentIndividuals )
559 if ( parentIndividuals == null )
561 return mainIndividuals;
564 if ( mainIndividuals == null )
566 return ArchivaModelCloner.cloneIndividuals( parentIndividuals );
569 List merged = ArchivaModelCloner.cloneIndividuals( mainIndividuals );
571 Iterator it = parentIndividuals.iterator();
572 while ( it.hasNext() )
574 Individual parentIndividual = (Individual) it.next();
576 if ( !mainIndividuals.contains( parentIndividual ) )
578 merged.add( parentIndividual );
585 private static List mergeLicenses( List mainLicenses, List parentLicenses )
587 if ( parentLicenses == null )
592 if ( mainLicenses == null )
594 return ArchivaModelCloner.cloneLicenses( parentLicenses );
597 List merged = new ArrayList();
599 Map mainLicensesMap = createLicensesMap( mainLicenses );
600 Map parentLicensesMap = createLicensesMap( parentLicenses );
602 Iterator it = mainLicensesMap.entrySet().iterator();
603 while ( it.hasNext() )
605 Map.Entry entry = (Entry) it.next();
606 String key = (String) entry.getKey();
607 License mainLicense = (License) entry.getValue();
608 License parentLicense = (License) parentLicensesMap.get( key );
610 if ( parentLicense == null )
612 merged.add( mainLicense );
616 // Not merging. Local wins.
617 merged.add( parentLicense );
624 private static List mergePlugins( List mainPlugins, List parentPlugins )
626 return mergeArtifactReferences( mainPlugins, parentPlugins );
629 private static List mergeReports( List mainReports, List parentReports )
631 return mergeArtifactReferences( mainReports, parentReports );
634 private static List mergeRepositories( List mainRepositories, List parentRepositories )
636 if ( parentRepositories == null )
638 return mainRepositories;
641 if ( mainRepositories == null )
643 return ArchivaModelCloner.cloneLicenses( parentRepositories );
646 List merged = new ArrayList();
648 Map mainRepositoriesMap = createRepositoriesMap( mainRepositories );
649 Map parentRepositoriesMap = createRepositoriesMap( parentRepositories );
651 Iterator it = mainRepositoriesMap.entrySet().iterator();
652 while ( it.hasNext() )
654 Map.Entry entry = (Entry) it.next();
655 String key = (String) entry.getKey();
656 ProjectRepository mainProjectRepository = (ProjectRepository) entry.getValue();
657 ProjectRepository parentProjectRepository = (ProjectRepository) parentRepositoriesMap.get( key );
659 if ( parentProjectRepository == null )
661 merged.add( mainProjectRepository );
665 // Not merging. Local wins.
666 merged.add( parentProjectRepository );
673 private static String toVersionlessArtifactKey( ArtifactReference artifactReference )
675 StringBuffer key = new StringBuffer();
677 key.append( artifactReference.getGroupId() ).append( ":" ).append( artifactReference.getArtifactId() );
678 key.append( StringUtils.defaultString( artifactReference.getClassifier() ) ).append( ":" );
679 key.append( artifactReference.getType() );
681 return key.toString();
684 private static String toVersionlessDependencyKey( Dependency dep )
686 StringBuffer key = new StringBuffer();
688 key.append( dep.getGroupId() ).append( ":" ).append( dep.getArtifactId() );
689 key.append( StringUtils.defaultString( dep.getClassifier() ) ).append( ":" );
690 key.append( dep.getType() );
692 return key.toString();