]> source.dussan.org Git - archiva.git/blob
f84d9f48f68947a50924ddaac6ec810434b57ef7
[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
234         return merged;
235     }
236
237     private static Dependency merge( Dependency mainDep, Dependency parentDep )
238     {
239         if ( parentDep == null )
240         {
241             return mainDep;
242         }
243
244         if ( mainDep == null )
245         {
246             Dependency dep = ArchivaModelCloner.clone( parentDep );
247             dep.setFromParent( true );
248             return dep;
249         }
250
251         Dependency merged = new Dependency();
252
253         merged.setFromParent( true );
254
255         // Unmerged.
256         merged.setGroupId( mainDep.getGroupId() );
257         merged.setArtifactId( mainDep.getArtifactId() );
258
259         // Merged.
260         merged.setVersion( merge( mainDep.getVersion(), parentDep.getVersion() ) );
261         merged.setClassifier( merge( mainDep.getClassifier(), parentDep.getClassifier() ) );
262         merged.setType( merge( mainDep.getType(), parentDep.getType() ) );
263         merged.setScope( merge( mainDep.getScope(), parentDep.getScope() ) );
264         if ( parentDep.isOptional() )
265         {
266             merged.setOptional( true );
267         }
268
269         merged.setSystemPath( merge( mainDep.getSystemPath(), parentDep.getSystemPath() ) );
270         merged.setUrl( merge( mainDep.getUrl(), parentDep.getUrl() ) );
271         merged.setExclusions( mergeExclusions( mainDep.getExclusions(), parentDep.getExclusions() ) );
272
273         return merged;
274     }
275
276     private static IssueManagement merge( IssueManagement mainIssueManagement, IssueManagement parentIssueManagement )
277     {
278         if ( parentIssueManagement == null )
279         {
280             return mainIssueManagement;
281         }
282
283         if ( mainIssueManagement == null )
284         {
285             return ArchivaModelCloner.clone( parentIssueManagement );
286         }
287
288         IssueManagement merged = new IssueManagement();
289
290         merged.setSystem( merge( mainIssueManagement.getSystem(), parentIssueManagement.getSystem() ) );
291         merged.setUrl( merge( mainIssueManagement.getUrl(), parentIssueManagement.getUrl() ) );
292
293         return merged;
294     }
295
296     private static Organization merge( Organization mainOrganization, Organization parentOrganization )
297     {
298         if ( parentOrganization == null )
299         {
300             return mainOrganization;
301         }
302
303         if ( mainOrganization == null )
304         {
305             return ArchivaModelCloner.clone( parentOrganization );
306         }
307
308         Organization merged = new Organization();
309
310         merged.setFavicon( merge( mainOrganization.getFavicon(), parentOrganization.getFavicon() ) );
311         merged.setName( merge( mainOrganization.getName(), parentOrganization.getName() ) );
312         merged.setUrl( merge( mainOrganization.getUrl(), parentOrganization.getUrl() ) );
313
314         return merged;
315     }
316
317     @SuppressWarnings("unchecked")
318     private static Properties merge( Properties mainProperties, Properties parentProperties )
319     {
320         if ( parentProperties == null )
321         {
322             return mainProperties;
323         }
324
325         if ( mainProperties == null )
326         {
327             return ArchivaModelCloner.clone( parentProperties );
328         }
329
330         Properties merged = new Properties();
331         merged.putAll(mainProperties);
332
333         Enumeration<String> keys = (Enumeration<String>) parentProperties.propertyNames();
334         while ( keys.hasMoreElements() )
335         {
336             String key = (String) keys.nextElement();
337             merged.put( key, merge( mainProperties.getProperty( key ), parentProperties.getProperty( key ) ) );
338         }
339
340         return merged;
341     }
342
343     private static Scm merge( Scm mainScm, Scm parentScm )
344     {
345         if ( parentScm == null )
346         {
347             return mainScm;
348         }
349
350         if ( mainScm == null )
351         {
352             return ArchivaModelCloner.clone( parentScm );
353         }
354
355         Scm merged = new Scm();
356
357         merged.setConnection( merge( mainScm.getConnection(), parentScm.getConnection() ) );
358         merged.setDeveloperConnection( merge( mainScm.getDeveloperConnection(), parentScm.getDeveloperConnection() ) );
359         merged.setUrl( merge( mainScm.getUrl(), parentScm.getUrl() ) );
360
361         return merged;
362     }
363
364     private static String merge( String main, String parent )
365     {
366         if ( empty( main ) && !empty( parent ) )
367         {
368             return parent;
369         }
370
371         return main;
372     }
373
374     private static List<ArtifactReference> mergeArtifactReferences( List<ArtifactReference> mainArtifactReferences, List<ArtifactReference> parentArtifactReferences )
375     {
376         if ( parentArtifactReferences == null )
377         {
378             return mainArtifactReferences;
379         }
380
381         if ( mainArtifactReferences == null )
382         {
383             return ArchivaModelCloner.cloneArtifactReferences( parentArtifactReferences );
384         }
385
386         List<ArtifactReference> merged = new ArrayList<ArtifactReference>();
387
388         Map<String, ArtifactReference> mainArtifactReferenceMap = createArtifactReferenceMap( mainArtifactReferences );
389         Map<String, ArtifactReference> parentArtifactReferenceMap = createArtifactReferenceMap( parentArtifactReferences );
390
391         for ( Map.Entry<String,ArtifactReference> entry : mainArtifactReferenceMap.entrySet() )
392         {
393             String key = entry.getKey();
394             ArtifactReference mainArtifactReference = (ArtifactReference) entry.getValue();
395             ArtifactReference parentArtifactReference = parentArtifactReferenceMap.get( key );
396
397             if ( parentArtifactReference == null )
398             {
399                 merged.add( mainArtifactReference );
400             }
401             else
402             {
403                 // Not merging. Local wins.
404                 merged.add( merge( mainArtifactReference, parentArtifactReference ) );
405             }
406         }
407
408         return merged;
409     }
410
411     private static List<Dependency> mergeDependencies( List<Dependency> mainDependencies, List<Dependency> parentDependencies )
412     {
413         if ( parentDependencies == null )
414         {
415             return mainDependencies;
416         }
417
418         if ( mainDependencies == null )
419         {
420             List<Dependency> merged = ArchivaModelCloner.cloneDependencies( parentDependencies );
421             Iterator<Dependency> it = merged.iterator();
422             while ( it.hasNext() )
423             {
424                 Dependency dep = it.next();
425                 dep.setFromParent( true );
426             }
427             return merged;
428         }
429
430         List<Dependency> merged = new ArrayList<Dependency>();
431
432         Map<String, Dependency> mainDepMap = createDependencyMap( mainDependencies );
433         Map<String, Dependency> parentDepMap = createDependencyMap( parentDependencies );
434         Set<String> uniqueKeys = new HashSet<String>();
435         uniqueKeys.addAll( mainDepMap.keySet() );
436         uniqueKeys.addAll( parentDepMap.keySet() );
437
438         Iterator<String> it = uniqueKeys.iterator();
439         while ( it.hasNext() )
440         {
441             String key = it.next();
442             Dependency parentDep = parentDepMap.get( key );
443             Dependency mainDep = mainDepMap.get( key );
444
445             if ( parentDep == null )
446             {
447                 // Means there is no parent dep to override main dep.
448                 merged.add( mainDep );
449             }
450             else
451             {
452                 // Parent dep exists (main doesn't have to).
453                 // Merge the parent over the main dep.
454                 merged.add( merge( mainDep, parentDep ) );
455             }
456         }
457
458         return merged;
459     }
460
461     private static List<Dependency> mergeDependencyManagement( List<Dependency> mainDepMgmt, List<Dependency> parentDepMgmt )
462     {
463         if ( parentDepMgmt == null )
464         {
465             return mainDepMgmt;
466         }
467
468         if ( mainDepMgmt == null )
469         {
470             List<Dependency> merged = ArchivaModelCloner.cloneDependencies( parentDepMgmt );
471             Iterator<Dependency> it = merged.iterator();
472             while ( it.hasNext() )
473             {
474                 Dependency dep = it.next();
475                 dep.setFromParent( true );
476             }
477             return merged;
478         }
479
480         List<Dependency> merged = new ArrayList<Dependency>();
481
482         Map<String, Dependency> mainDepMap = createDependencyMap( mainDepMgmt );
483         Map<String, Dependency> parentDepMap = createDependencyMap( parentDepMgmt );
484         Set<String> uniqueKeys = new HashSet<String>();
485         uniqueKeys.addAll( mainDepMap.keySet() );
486         uniqueKeys.addAll( parentDepMap.keySet() );
487
488         Iterator<String> it = uniqueKeys.iterator();
489         while ( it.hasNext() )
490         {
491             String key = it.next();
492             Dependency parentDep = parentDepMap.get( key );
493             Dependency mainDep = mainDepMap.get( key );
494
495             if ( parentDep == null )
496             {
497                 // Means there is no parent depMan entry to override main depMan.
498                 merged.add( mainDep );
499             }
500             else
501             {
502                 // Parent depMan entry exists (main doesn't have to).
503                 // Merge the parent over the main depMan entry.
504                 merged.add( merge( mainDep, parentDep ) );
505             }
506         }
507
508         return merged;
509     }
510
511     public static List<Exclusion> mergeExclusions( List<Exclusion> mainExclusions, List<Exclusion> parentExclusions )
512     {
513         if ( parentExclusions == null )
514         {
515             return mainExclusions;
516         }
517
518         if ( mainExclusions == null )
519         {
520             return ArchivaModelCloner.cloneExclusions( parentExclusions );
521         }
522
523         List<Exclusion> merged = new ArrayList<Exclusion>();
524
525         Map<String, Exclusion> mainExclusionMap = createExclusionMap( mainExclusions );
526         Map<String, Exclusion> parentExclusionMap = createExclusionMap( parentExclusions );
527
528         for ( Map.Entry<String, Exclusion> entry : mainExclusionMap.entrySet() )
529         {
530             String key = entry.getKey();
531             Exclusion mainExclusion = entry.getValue();
532             Exclusion parentExclusion = parentExclusionMap.get( key );
533
534             if ( parentExclusion == null )
535             {
536                 merged.add( mainExclusion );
537             }
538             else
539             {
540                 merged.add( parentExclusion );
541             }
542         }
543
544         return merged;
545     }
546
547     private static List<Individual> mergeIndividuals( List<Individual> mainIndividuals, List<Individual> parentIndividuals )
548     {
549         if ( parentIndividuals == null )
550         {
551             return mainIndividuals;
552         }
553
554         if ( mainIndividuals == null )
555         {
556             return ArchivaModelCloner.cloneIndividuals( parentIndividuals );
557         }
558
559         List<Individual> merged = ArchivaModelCloner.cloneIndividuals( mainIndividuals );
560
561         Iterator<Individual> it = parentIndividuals.iterator();
562         while ( it.hasNext() )
563         {
564             Individual parentIndividual = it.next();
565
566             if ( !mainIndividuals.contains( parentIndividual ) )
567             {
568                 merged.add( parentIndividual );
569             }
570         }
571
572         return merged;
573     }
574
575     private static List<License> mergeLicenses( List<License> mainLicenses, List<License> parentLicenses )
576     {
577         if ( parentLicenses == null )
578         {
579             return mainLicenses;
580         }
581
582         if ( mainLicenses == null )
583         {
584             return ArchivaModelCloner.cloneLicenses( parentLicenses );
585         }
586
587         List<License> merged = new ArrayList<License>();
588
589         Map<String, License> mainLicensesMap = createLicensesMap( mainLicenses );
590         Map<String, License> parentLicensesMap = createLicensesMap( parentLicenses );
591
592         for ( Map.Entry<String, License> entry : mainLicensesMap.entrySet() )
593         {
594             String key = entry.getKey();
595             License mainLicense = entry.getValue();
596             License parentLicense = parentLicensesMap.get( key );
597
598             if ( parentLicense == null )
599             {
600                 merged.add( mainLicense );
601             }
602             else
603             {
604                 // Not merging. Local wins.
605                 merged.add( parentLicense );
606             }
607         }
608
609         return merged;
610     }
611
612     private static List<ArtifactReference> mergePlugins( List<ArtifactReference> mainPlugins, List<ArtifactReference> parentPlugins )
613     {
614         return mergeArtifactReferences( mainPlugins, parentPlugins );
615     }
616
617     private static List<ArtifactReference> mergeReports( List<ArtifactReference> mainReports, List<ArtifactReference> parentReports )
618     {
619         return mergeArtifactReferences( mainReports, parentReports );
620     }
621
622     private static List<ProjectRepository> mergeRepositories( List<ProjectRepository> mainRepositories, List<ProjectRepository> parentRepositories )
623     {
624         if ( parentRepositories == null )
625         {
626             return mainRepositories;
627         }
628
629         if ( mainRepositories == null )
630         {
631             return ArchivaModelCloner.cloneRepositories( parentRepositories );
632         }
633
634         List<ProjectRepository> merged = new ArrayList<ProjectRepository>();
635
636         Map<String, ProjectRepository> mainRepositoriesMap = createRepositoriesMap( mainRepositories );
637         Map<String, ProjectRepository> parentRepositoriesMap = createRepositoriesMap( parentRepositories );
638
639         for ( Map.Entry<String, ProjectRepository> entry : mainRepositoriesMap.entrySet() )
640         {
641             String key = entry.getKey();
642             ProjectRepository mainProjectRepository = entry.getValue();
643             ProjectRepository parentProjectRepository = parentRepositoriesMap.get( key );
644
645             if ( parentProjectRepository == null )
646             {
647                 merged.add( mainProjectRepository );
648             }
649             else
650             {
651                 // Not merging. Local wins.
652                 merged.add( parentProjectRepository );
653             }
654         }
655
656         return merged;
657     }
658
659     private static String toVersionlessArtifactKey( ArtifactReference artifactReference )
660     {
661         StringBuffer key = new StringBuffer();
662
663         key.append( artifactReference.getGroupId() ).append( ":" ).append( artifactReference.getArtifactId() );
664         key.append( StringUtils.defaultString( artifactReference.getClassifier() ) ).append( ":" );
665         key.append( artifactReference.getType() );
666
667         return key.toString();
668     }
669
670     private static String toVersionlessDependencyKey( Dependency dep )
671     {
672         StringBuffer key = new StringBuffer();
673
674         key.append( dep.getGroupId() ).append( ":" ).append( dep.getArtifactId() );
675         key.append( StringUtils.defaultString( dep.getClassifier() ) ).append( ":" );
676         key.append( dep.getType() );
677
678         return key.toString();
679     }
680 }