]> source.dussan.org Git - archiva.git/blob
01d8e9f2815e6acde9dc9fa00d205fc7d3b75c7a
[archiva.git] /
1 package org.apache.maven.archiva.repository.project;
2
3 /*
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
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.Set;
31
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;
45
46 /**
47  * ProjectModelMerge
48  *
49  * TODO: Should call this ProjectModelAncestry as it deals with the current project and its parent.
50  *
51  * @version $Id$
52  */
53 public class ProjectModelMerge
54 {
55     /**
56      * Merge the contents of a project with it's parent project.
57      * 
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.
61      */
62     public static ArchivaProjectModel merge( ArchivaProjectModel mainProject, ArchivaProjectModel parentProject )
63         throws ProjectModelException
64     {
65         if ( mainProject == null )
66         {
67             throw new ProjectModelException( "Cannot merge with a null main project." );
68         }
69
70         if ( parentProject == null )
71         {
72             throw new ProjectModelException( "Cannot merge with a null parent project." );
73         }
74
75         ArchivaProjectModel merged = new ArchivaProjectModel();
76
77         // Unmerged.
78         merged.setParentProject(mainProject.getParentProject());
79         merged.setArtifactId( mainProject.getArtifactId() );
80         merged.setPackaging( StringUtils.defaultIfEmpty( mainProject.getPackaging(), "jar" ) );
81         merged.setRelocation( mainProject.getRelocation() );
82
83         // Merged
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() ) );
89
90         merged.setOrigin( "merged" );
91
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() ) );
105         
106         return merged;
107     }
108
109     private static Map<String, ArtifactReference> createArtifactReferenceMap( List<ArtifactReference> artifactReferences )
110     {
111         Map<String, ArtifactReference> ret = new HashMap<String, ArtifactReference>();
112
113         for ( ArtifactReference artifactReference : artifactReferences )
114         {
115             String key = toVersionlessArtifactKey( artifactReference );
116             ret.put( key, artifactReference );
117         }
118
119         return ret;
120     }
121
122     private static Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
123     {
124         Map<String, Dependency> ret = new HashMap<String, Dependency>();
125
126         Iterator<Dependency> it = dependencies.iterator();
127         while ( it.hasNext() )
128         {
129             Dependency dep = it.next();
130             String key = toVersionlessDependencyKey( dep );
131             ret.put( key, dep );
132         }
133
134         return ret;
135     }
136
137     private static Map<String, Exclusion> createExclusionMap( List<Exclusion> exclusions )
138     {
139         Map<String, Exclusion> ret = new HashMap<String, Exclusion>();
140
141         Iterator<Exclusion> it = exclusions.iterator();
142         while ( it.hasNext() )
143         {
144             Exclusion exclusion = it.next();
145             String key = exclusion.getGroupId() + ":" + exclusion.getArtifactId();
146             ret.put( key, exclusion );
147         }
148
149         return ret;
150     }
151
152     private static Map<String, License> createLicensesMap( List<License> licenses )
153     {
154         Map<String, License> ret = new HashMap<String, License>();
155
156         for ( License license : licenses )
157         {
158             // TODO: Change to 'id' when LicenseTypeMapper is created.
159             String key = license.getName();
160             ret.put( key, license );
161         }
162
163         return ret;
164     }
165
166     private static Map<String, ProjectRepository> createRepositoriesMap( List<ProjectRepository> repositories )
167     {
168         Map<String, ProjectRepository> ret = new HashMap<String, ProjectRepository>();
169
170         for ( ProjectRepository repo : repositories )
171         {
172             // Should this really be using repo.id ?
173             String key = repo.getUrl();
174             ret.put( key, repo );
175         }
176
177         return ret;
178     }
179
180     private static boolean empty( String val )
181     {
182         if ( val == null )
183         {
184             return true;
185         }
186
187         return ( val.trim().length() <= 0 );
188     }
189
190     private static ArtifactReference merge( ArtifactReference mainArtifactReference,
191                                             ArtifactReference parentArtifactReference )
192     {
193         if ( parentArtifactReference == null )
194         {
195             return mainArtifactReference;
196         }
197
198         if ( mainArtifactReference == null )
199         {
200             return ArchivaModelCloner.clone( parentArtifactReference );
201         }
202
203         ArtifactReference merged = new ArtifactReference();
204
205         // Unmerged.
206         merged.setGroupId( mainArtifactReference.getGroupId() );
207         merged.setArtifactId( mainArtifactReference.getArtifactId() );
208
209         // Merged.
210         merged.setVersion( merge( mainArtifactReference.getVersion(), parentArtifactReference.getVersion() ) );
211         merged.setClassifier( merge( mainArtifactReference.getClassifier(), parentArtifactReference.getClassifier() ) );
212         merged.setType( merge( mainArtifactReference.getType(), parentArtifactReference.getType() ) );
213
214         return merged;
215     }
216
217     private static CiManagement merge( CiManagement mainCim, CiManagement parentCim )
218     {
219         if ( parentCim == null )
220         {
221             return mainCim;
222         }
223
224         if ( mainCim == null )
225         {
226             return ArchivaModelCloner.clone( parentCim );
227         }
228
229         CiManagement merged = new CiManagement();
230
231         merged.setSystem( merge( mainCim.getSystem(), parentCim.getSystem() ) );
232         merged.setUrl( merge( mainCim.getUrl(), parentCim.getUrl() ) );
233         merged.setCiUrl( merge( mainCim.getCiUrl(), parentCim.getCiUrl() ) );
234
235         return merged;
236     }
237
238     private static Dependency merge( Dependency mainDep, Dependency parentDep )
239     {
240         if ( parentDep == null )
241         {
242             return mainDep;
243         }
244
245         if ( mainDep == null )
246         {
247             Dependency dep = ArchivaModelCloner.clone( parentDep );
248             dep.setFromParent( true );
249             return dep;
250         }
251
252         Dependency merged = new Dependency();
253
254         merged.setFromParent( true );
255
256         // Unmerged.
257         merged.setGroupId( mainDep.getGroupId() );
258         merged.setArtifactId( mainDep.getArtifactId() );
259
260         // Merged.
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() )
266         {
267             merged.setOptional( true );
268         }
269
270         merged.setSystemPath( merge( mainDep.getSystemPath(), parentDep.getSystemPath() ) );
271         merged.setUrl( merge( mainDep.getUrl(), parentDep.getUrl() ) );
272         merged.setExclusions( mergeExclusions( mainDep.getExclusions(), parentDep.getExclusions() ) );
273
274         return merged;
275     }
276
277     private static IssueManagement merge( IssueManagement mainIssueManagement, IssueManagement parentIssueManagement )
278     {
279         if ( parentIssueManagement == null )
280         {
281             return mainIssueManagement;
282         }
283
284         if ( mainIssueManagement == null )
285         {
286             return ArchivaModelCloner.clone( parentIssueManagement );
287         }
288
289         IssueManagement merged = new IssueManagement();
290
291         merged.setSystem( merge( mainIssueManagement.getSystem(), parentIssueManagement.getSystem() ) );
292         merged.setUrl( merge( mainIssueManagement.getUrl(), parentIssueManagement.getUrl() ) );
293         merged.setIssueManagementUrl( merge( mainIssueManagement.getIssueManagementUrl(), parentIssueManagement.getIssueManagementUrl() ) );
294         
295         return merged;
296     }
297
298     private static Organization merge( Organization mainOrganization, Organization parentOrganization )
299     {
300         if ( parentOrganization == null )
301         {
302             return mainOrganization;
303         }
304
305         if ( mainOrganization == null )
306         {
307             return ArchivaModelCloner.clone( parentOrganization );
308         }
309
310         Organization merged = new Organization();
311
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() ) );
316
317         return merged;
318     }
319
320     @SuppressWarnings("unchecked")
321     private static Properties merge( Properties mainProperties, Properties parentProperties )
322     {
323         if ( parentProperties == null )
324         {
325             return mainProperties;
326         }
327
328         if ( mainProperties == null )
329         {
330             return ArchivaModelCloner.clone( parentProperties );
331         }
332
333         Properties merged = new Properties();
334         merged.putAll(mainProperties);
335
336         Enumeration<String> keys = (Enumeration<String>) parentProperties.propertyNames();
337         while ( keys.hasMoreElements() )
338         {
339             String key = (String) keys.nextElement();
340             merged.put( key, merge( mainProperties.getProperty( key ), parentProperties.getProperty( key ) ) );
341         }
342
343         return merged;
344     }
345
346     private static Scm merge( Scm mainScm, Scm parentScm )
347     {
348         if ( parentScm == null )
349         {
350             return mainScm;
351         }
352
353         if ( mainScm == null )
354         {
355             return ArchivaModelCloner.clone( parentScm );
356         }
357
358         Scm merged = new Scm();
359
360         merged.setConnection( merge( mainScm.getConnection(), parentScm.getConnection() ) );
361         merged.setDeveloperConnection( merge( mainScm.getDeveloperConnection(), parentScm.getDeveloperConnection() ) );
362         merged.setUrl( merge( mainScm.getUrl(), parentScm.getUrl() ) );
363
364         return merged;
365     }
366
367     private static String merge( String main, String parent )
368     {
369         if ( empty( main ) && !empty( parent ) )
370         {
371             return parent;
372         }
373
374         return main;
375     }
376
377     private static List<ArtifactReference> mergeArtifactReferences( List<ArtifactReference> mainArtifactReferences, List<ArtifactReference> parentArtifactReferences )
378     {
379         if ( parentArtifactReferences == null )
380         {
381             return mainArtifactReferences;
382         }
383
384         if ( mainArtifactReferences == null )
385         {
386             return ArchivaModelCloner.cloneArtifactReferences( parentArtifactReferences );
387         }
388
389         List<ArtifactReference> merged = new ArrayList<ArtifactReference>();
390
391         Map<String, ArtifactReference> mainArtifactReferenceMap = createArtifactReferenceMap( mainArtifactReferences );
392         Map<String, ArtifactReference> parentArtifactReferenceMap = createArtifactReferenceMap( parentArtifactReferences );
393
394         for ( Map.Entry<String,ArtifactReference> entry : mainArtifactReferenceMap.entrySet() )
395         {
396             String key = entry.getKey();
397             ArtifactReference mainArtifactReference = (ArtifactReference) entry.getValue();
398             ArtifactReference parentArtifactReference = parentArtifactReferenceMap.get( key );
399
400             if ( parentArtifactReference == null )
401             {
402                 merged.add( mainArtifactReference );
403             }
404             else
405             {
406                 // Not merging. Local wins.
407                 merged.add( merge( mainArtifactReference, parentArtifactReference ) );
408             }
409         }
410
411         return merged;
412     }
413
414     private static List<Dependency> mergeDependencies( List<Dependency> mainDependencies, List<Dependency> parentDependencies )
415     {
416         if ( parentDependencies == null )
417         {
418             return mainDependencies;
419         }
420
421         if ( mainDependencies == null )
422         {
423             List<Dependency> merged = ArchivaModelCloner.cloneDependencies( parentDependencies );
424             Iterator<Dependency> it = merged.iterator();
425             while ( it.hasNext() )
426             {
427                 Dependency dep = it.next();
428                 dep.setFromParent( true );
429             }
430             return merged;
431         }
432
433         List<Dependency> merged = new ArrayList<Dependency>();
434
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() );
440
441         Iterator<String> it = uniqueKeys.iterator();
442         while ( it.hasNext() )
443         {
444             String key = it.next();
445             Dependency parentDep = parentDepMap.get( key );
446             Dependency mainDep = mainDepMap.get( key );
447
448             if ( parentDep == null )
449             {
450                 // Means there is no parent dep to override main dep.
451                 merged.add( mainDep );
452             }
453             else
454             {
455                 // Parent dep exists (main doesn't have to).
456                 // Merge the parent over the main dep.
457                 merged.add( merge( mainDep, parentDep ) );
458             }
459         }
460
461         return merged;
462     }
463
464     private static List<Dependency> mergeDependencyManagement( List<Dependency> mainDepMgmt, List<Dependency> parentDepMgmt )
465     {
466         if ( parentDepMgmt == null )
467         {
468             return mainDepMgmt;
469         }
470
471         if ( mainDepMgmt == null )
472         {
473             List<Dependency> merged = ArchivaModelCloner.cloneDependencies( parentDepMgmt );
474             Iterator<Dependency> it = merged.iterator();
475             while ( it.hasNext() )
476             {
477                 Dependency dep = it.next();
478                 dep.setFromParent( true );
479             }
480             return merged;
481         }
482
483         List<Dependency> merged = new ArrayList<Dependency>();
484
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() );
490
491         Iterator<String> it = uniqueKeys.iterator();
492         while ( it.hasNext() )
493         {
494             String key = it.next();
495             Dependency parentDep = parentDepMap.get( key );
496             Dependency mainDep = mainDepMap.get( key );
497
498             if ( parentDep == null )
499             {
500                 // Means there is no parent depMan entry to override main depMan.
501                 merged.add( mainDep );
502             }
503             else
504             {
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 ) );
508             }
509         }
510
511         return merged;
512     }
513
514     public static List<Exclusion> mergeExclusions( List<Exclusion> mainExclusions, List<Exclusion> parentExclusions )
515     {
516         if ( parentExclusions == null )
517         {
518             return mainExclusions;
519         }
520
521         if ( mainExclusions == null )
522         {
523             return ArchivaModelCloner.cloneExclusions( parentExclusions );
524         }
525
526         List<Exclusion> merged = new ArrayList<Exclusion>();
527
528         Map<String, Exclusion> mainExclusionMap = createExclusionMap( mainExclusions );
529         Map<String, Exclusion> parentExclusionMap = createExclusionMap( parentExclusions );
530
531         for ( Map.Entry<String, Exclusion> entry : mainExclusionMap.entrySet() )
532         {
533             String key = entry.getKey();
534             Exclusion mainExclusion = entry.getValue();
535             Exclusion parentExclusion = parentExclusionMap.get( key );
536
537             if ( parentExclusion == null )
538             {
539                 merged.add( mainExclusion );
540             }
541             else
542             {
543                 merged.add( parentExclusion );
544             }
545         }
546
547         return merged;
548     }
549
550     private static List<Individual> mergeIndividuals( List<Individual> mainIndividuals, List<Individual> parentIndividuals )
551     {
552         if ( parentIndividuals == null )
553         {
554             return mainIndividuals;
555         }
556
557         if ( mainIndividuals == null )
558         {
559             return ArchivaModelCloner.cloneIndividuals( parentIndividuals );
560         }
561
562         List<Individual> merged = ArchivaModelCloner.cloneIndividuals( mainIndividuals );
563
564         Iterator<Individual> it = parentIndividuals.iterator();
565         while ( it.hasNext() )
566         {
567             Individual parentIndividual = it.next();
568
569             if ( !mainIndividuals.contains( parentIndividual ) )
570             {
571                 merged.add( parentIndividual );
572             }
573         }
574
575         return merged;
576     }
577
578     private static List<License> mergeLicenses( List<License> mainLicenses, List<License> parentLicenses )
579     {
580         if ( parentLicenses == null )
581         {
582             return mainLicenses;
583         }
584
585         if ( mainLicenses == null )
586         {
587             return ArchivaModelCloner.cloneLicenses( parentLicenses );
588         }
589
590         List<License> merged = new ArrayList<License>();
591
592         Map<String, License> mainLicensesMap = createLicensesMap( mainLicenses );
593         Map<String, License> parentLicensesMap = createLicensesMap( parentLicenses );
594
595         for ( Map.Entry<String, License> entry : mainLicensesMap.entrySet() )
596         {
597             String key = entry.getKey();
598             License mainLicense = entry.getValue();
599             License parentLicense = parentLicensesMap.get( key );
600
601             if ( parentLicense == null )
602             {
603                 merged.add( mainLicense );
604             }
605             else
606             {
607                 // Not merging. Local wins.
608                 merged.add( parentLicense );
609             }
610         }
611
612         return merged;
613     }
614
615     private static List<ArtifactReference> mergePlugins( List<ArtifactReference> mainPlugins, List<ArtifactReference> parentPlugins )
616     {
617         return mergeArtifactReferences( mainPlugins, parentPlugins );
618     }
619
620     private static List<ArtifactReference> mergeReports( List<ArtifactReference> mainReports, List<ArtifactReference> parentReports )
621     {
622         return mergeArtifactReferences( mainReports, parentReports );
623     }
624
625     private static List<ProjectRepository> mergeRepositories( List<ProjectRepository> mainRepositories, List<ProjectRepository> parentRepositories )
626     {
627         if ( parentRepositories == null )
628         {
629             return mainRepositories;
630         }
631
632         if ( mainRepositories == null )
633         {
634             return ArchivaModelCloner.cloneRepositories( parentRepositories );
635         }
636
637         List<ProjectRepository> merged = new ArrayList<ProjectRepository>();
638
639         Map<String, ProjectRepository> mainRepositoriesMap = createRepositoriesMap( mainRepositories );
640         Map<String, ProjectRepository> parentRepositoriesMap = createRepositoriesMap( parentRepositories );
641
642         for ( Map.Entry<String, ProjectRepository> entry : mainRepositoriesMap.entrySet() )
643         {
644             String key = entry.getKey();
645             ProjectRepository mainProjectRepository = entry.getValue();
646             ProjectRepository parentProjectRepository = parentRepositoriesMap.get( key );
647
648             if ( parentProjectRepository == null )
649             {
650                 merged.add( mainProjectRepository );
651             }
652             else
653             {
654                 // Not merging. Local wins.
655                 merged.add( parentProjectRepository );
656             }
657         }
658
659         return merged;
660     }
661
662     private static String toVersionlessArtifactKey( ArtifactReference artifactReference )
663     {
664         StringBuffer key = new StringBuffer();
665
666         key.append( artifactReference.getGroupId() ).append( ":" ).append( artifactReference.getArtifactId() );
667         key.append( StringUtils.defaultString( artifactReference.getClassifier() ) ).append( ":" );
668         key.append( artifactReference.getType() );
669
670         return key.toString();
671     }
672
673     private static String toVersionlessDependencyKey( Dependency dep )
674     {
675         StringBuffer key = new StringBuffer();
676
677         key.append( dep.getGroupId() ).append( ":" ).append( dep.getArtifactId() );
678         key.append( StringUtils.defaultString( dep.getClassifier() ) ).append( ":" );
679         key.append( dep.getType() );
680
681         return key.toString();
682     }
683 }