View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.model.merge;
20  
21  import java.util.ArrayList;
22  import java.util.LinkedHashMap;
23  import java.util.LinkedHashSet;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.maven.model.BuildBase;
29  import org.apache.maven.model.CiManagement;
30  import org.apache.maven.model.Dependency;
31  import org.apache.maven.model.DeploymentRepository;
32  import org.apache.maven.model.DistributionManagement;
33  import org.apache.maven.model.Exclusion;
34  import org.apache.maven.model.Extension;
35  import org.apache.maven.model.InputLocation;
36  import org.apache.maven.model.IssueManagement;
37  import org.apache.maven.model.Model;
38  import org.apache.maven.model.ModelBase;
39  import org.apache.maven.model.Organization;
40  import org.apache.maven.model.Plugin;
41  import org.apache.maven.model.PluginExecution;
42  import org.apache.maven.model.ReportPlugin;
43  import org.apache.maven.model.ReportSet;
44  import org.apache.maven.model.Repository;
45  import org.apache.maven.model.RepositoryBase;
46  import org.apache.maven.model.Scm;
47  import org.apache.maven.model.Site;
48  import org.codehaus.plexus.util.StringUtils;
49  
50  /**
51   * The domain-specific model merger for the Maven POM, overriding generic code from parent class when necessary with
52   * more adapted algorithms.
53   *
54   * @deprecated use {@code org.apache.maven.impl.model.MavenModelMerger} instead
55   */
56  @Deprecated(since = "4.0.0")
57  public class MavenModelMerger extends ModelMerger {
58  
59      /**
60       * The hint key for the child path adjustment used during inheritance for URL calculations.
61       */
62      public static final String CHILD_PATH_ADJUSTMENT = "child-path-adjustment";
63  
64      /**
65       * The context key for the artifact id of the target model.
66       */
67      public static final String ARTIFACT_ID = "artifact-id";
68  
69      @Override
70      protected void mergeModel(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
71          context.put(ARTIFACT_ID, target.getArtifactId());
72  
73          super.mergeModel(target, source, sourceDominant, context);
74      }
75  
76      @Override
77      protected void mergeModel_Name(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
78          String src = source.getName();
79          if (src != null) {
80              if (sourceDominant) {
81                  target.setName(src);
82                  target.setLocation("name", source.getLocation("name"));
83              }
84          }
85      }
86  
87      @Override
88      protected void mergeModel_Url(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
89          String src = source.getUrl();
90          if (src != null) {
91              if (sourceDominant) {
92                  target.setUrl(src);
93                  target.setLocation("url", source.getLocation("url"));
94              } else if (target.getUrl() == null) {
95                  target.setUrl(extrapolateChildUrl(src, source.isChildProjectUrlInheritAppendPath(), context));
96                  target.setLocation("url", source.getLocation("url"));
97              }
98          }
99      }
100 
101     /*
102      * TODO: Whether the merge continues recursively into an existing node or not could be an option for the generated
103      * merger
104      */
105     @Override
106     protected void mergeModel_Organization(
107             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
108         Organization src = source.getOrganization();
109         if (src != null) {
110             Organization tgt = target.getOrganization();
111             if (tgt == null) {
112                 tgt = new Organization();
113                 tgt.setLocation("", src.getLocation(""));
114                 target.setOrganization(tgt);
115                 mergeOrganization(tgt, src, sourceDominant, context);
116             }
117         }
118     }
119 
120     @Override
121     protected void mergeModel_IssueManagement(
122             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
123         IssueManagement src = source.getIssueManagement();
124         if (src != null) {
125             IssueManagement tgt = target.getIssueManagement();
126             if (tgt == null) {
127                 tgt = new IssueManagement();
128                 tgt.setLocation("", src.getLocation(""));
129                 target.setIssueManagement(tgt);
130                 mergeIssueManagement(tgt, src, sourceDominant, context);
131             }
132         }
133     }
134 
135     @Override
136     protected void mergeModel_CiManagement(
137             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
138         CiManagement src = source.getCiManagement();
139         if (src != null) {
140             CiManagement tgt = target.getCiManagement();
141             if (tgt == null) {
142                 tgt = new CiManagement();
143                 tgt.setLocation("", src.getLocation(""));
144                 target.setCiManagement(tgt);
145                 mergeCiManagement(tgt, src, sourceDominant, context);
146             }
147         }
148     }
149 
150     @Override
151     protected void mergeModel_ModelVersion(
152             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
153         // neither inherited nor injected
154     }
155 
156     @Override
157     protected void mergeModel_ArtifactId(
158             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
159         // neither inherited nor injected
160     }
161 
162     @Override
163     protected void mergeModel_Profiles(
164             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
165         // neither inherited nor injected
166     }
167 
168     @Override
169     protected void mergeModel_Prerequisites(
170             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
171         // neither inherited nor injected
172     }
173 
174     @Override
175     protected void mergeModel_Licenses(
176             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
177         if (target.getLicenses().isEmpty()) {
178             target.setLicenses(new ArrayList<>(source.getLicenses()));
179         }
180     }
181 
182     @Override
183     protected void mergeModel_Developers(
184             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
185         if (target.getDevelopers().isEmpty()) {
186             target.setDevelopers(new ArrayList<>(source.getDevelopers()));
187         }
188     }
189 
190     @Override
191     protected void mergeModel_Contributors(
192             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
193         if (target.getContributors().isEmpty()) {
194             target.setContributors(new ArrayList<>(source.getContributors()));
195         }
196     }
197 
198     @Override
199     protected void mergeModel_MailingLists(
200             Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
201         if (target.getMailingLists().isEmpty()) {
202             target.setMailingLists(new ArrayList<>(source.getMailingLists()));
203         }
204     }
205 
206     @Override
207     protected void mergeModelBase_Modules(
208             ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
209         List<String> src = source.getModules();
210         if (!src.isEmpty() && sourceDominant) {
211             List<Integer> indices = new ArrayList<>();
212             List<String> tgt = target.getModules();
213             Set<String> excludes = new LinkedHashSet<>(tgt);
214             List<String> merged = new ArrayList<>(tgt.size() + src.size());
215             merged.addAll(tgt);
216             for (int i = 0, n = tgt.size(); i < n; i++) {
217                 indices.add(i);
218             }
219             for (int i = 0, n = src.size(); i < n; i++) {
220                 String s = src.get(i);
221                 if (!excludes.contains(s)) {
222                     merged.add(s);
223                     indices.add(~i);
224                 }
225             }
226             target.setModules(merged);
227             target.setLocation(
228                     "modules",
229                     InputLocation.merge(target.getLocation("modules"), source.getLocation("modules"), indices));
230         }
231     }
232 
233     /*
234      * TODO: The order of the merged list could be controlled by an attribute in the model association: target-first,
235      * source-first, dominant-first, recessive-first
236      */
237     @Override
238     protected void mergeModelBase_Repositories(
239             ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
240         List<Repository> src = source.getRepositories();
241         if (!src.isEmpty()) {
242             List<Repository> tgt = target.getRepositories();
243             Map<Object, Repository> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
244 
245             List<Repository> dominant, recessive;
246             if (sourceDominant) {
247                 dominant = src;
248                 recessive = tgt;
249             } else {
250                 dominant = tgt;
251                 recessive = src;
252             }
253 
254             for (Repository element : dominant) {
255                 Object key = getRepositoryKey(element);
256                 merged.put(key, element);
257             }
258 
259             for (Repository element : recessive) {
260                 Object key = getRepositoryKey(element);
261                 if (!merged.containsKey(key)) {
262                     merged.put(key, element);
263                 }
264             }
265 
266             target.setRepositories(new ArrayList<>(merged.values()));
267         }
268     }
269 
270     @Override
271     protected void mergeModelBase_PluginRepositories(
272             ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
273         List<Repository> src = source.getPluginRepositories();
274         if (!src.isEmpty()) {
275             List<Repository> tgt = target.getPluginRepositories();
276             Map<Object, Repository> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
277 
278             List<Repository> dominant, recessive;
279             if (sourceDominant) {
280                 dominant = src;
281                 recessive = tgt;
282             } else {
283                 dominant = tgt;
284                 recessive = src;
285             }
286 
287             for (Repository element : dominant) {
288                 Object key = getRepositoryKey(element);
289                 merged.put(key, element);
290             }
291 
292             for (Repository element : recessive) {
293                 Object key = getRepositoryKey(element);
294                 if (!merged.containsKey(key)) {
295                     merged.put(key, element);
296                 }
297             }
298 
299             target.setPluginRepositories(new ArrayList<>(merged.values()));
300         }
301     }
302 
303     /*
304      * TODO: Whether duplicates should be removed looks like an option for the generated merger.
305      */
306     @Override
307     protected void mergeBuildBase_Filters(
308             BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
309         List<String> src = source.getFilters();
310         if (!src.isEmpty()) {
311             List<String> tgt = target.getFilters();
312             Set<String> excludes = new LinkedHashSet<>(tgt);
313             List<String> merged = new ArrayList<>(tgt.size() + src.size());
314             merged.addAll(tgt);
315             for (String s : src) {
316                 if (!excludes.contains(s)) {
317                     merged.add(s);
318                 }
319             }
320             target.setFilters(merged);
321         }
322     }
323 
324     @Override
325     protected void mergeBuildBase_Resources(
326             BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
327         if (sourceDominant || target.getResources().isEmpty()) {
328             super.mergeBuildBase_Resources(target, source, sourceDominant, context);
329         }
330     }
331 
332     @Override
333     protected void mergeBuildBase_TestResources(
334             BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
335         if (sourceDominant || target.getTestResources().isEmpty()) {
336             super.mergeBuildBase_TestResources(target, source, sourceDominant, context);
337         }
338     }
339 
340     @Override
341     protected void mergeDistributionManagement_Repository(
342             DistributionManagement target,
343             DistributionManagement source,
344             boolean sourceDominant,
345             Map<Object, Object> context) {
346         DeploymentRepository src = source.getRepository();
347         if (src != null) {
348             DeploymentRepository tgt = target.getRepository();
349             if (sourceDominant || tgt == null) {
350                 tgt = new DeploymentRepository();
351                 tgt.setLocation("", src.getLocation(""));
352                 target.setRepository(tgt);
353                 mergeDeploymentRepository(tgt, src, sourceDominant, context);
354             }
355         }
356     }
357 
358     @Override
359     protected void mergeDistributionManagement_SnapshotRepository(
360             DistributionManagement target,
361             DistributionManagement source,
362             boolean sourceDominant,
363             Map<Object, Object> context) {
364         DeploymentRepository src = source.getSnapshotRepository();
365         if (src != null) {
366             DeploymentRepository tgt = target.getSnapshotRepository();
367             if (sourceDominant || tgt == null) {
368                 tgt = new DeploymentRepository();
369                 tgt.setLocation("", src.getLocation(""));
370                 target.setSnapshotRepository(tgt);
371                 mergeDeploymentRepository(tgt, src, sourceDominant, context);
372             }
373         }
374     }
375 
376     @Override
377     protected void mergeDistributionManagement_Site(
378             DistributionManagement target,
379             DistributionManagement source,
380             boolean sourceDominant,
381             Map<Object, Object> context) {
382         Site src = source.getSite();
383         if (src != null) {
384             Site tgt = target.getSite();
385             if (sourceDominant || tgt == null || isSiteEmpty(tgt)) {
386                 if (tgt == null) {
387                     tgt = new Site();
388                 }
389                 tgt.setLocation("", src.getLocation(""));
390                 target.setSite(tgt);
391                 mergeSite(tgt, src, sourceDominant, context);
392             }
393             mergeSite_ChildSiteUrlInheritAppendPath(tgt, src, sourceDominant, context);
394         }
395     }
396 
397     @Override
398     protected void mergeSite(Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
399         mergeSite_Id(target, source, sourceDominant, context);
400         mergeSite_Name(target, source, sourceDominant, context);
401         mergeSite_Url(target, source, sourceDominant, context);
402     }
403 
404     protected boolean isSiteEmpty(Site site) {
405         return StringUtils.isEmpty(site.getId())
406                 && StringUtils.isEmpty(site.getName())
407                 && StringUtils.isEmpty(site.getUrl());
408     }
409 
410     @Override
411     protected void mergeSite_Url(Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
412         String src = source.getUrl();
413         if (src != null) {
414             if (sourceDominant) {
415                 target.setUrl(src);
416                 target.setLocation("url", source.getLocation("url"));
417             } else if (target.getUrl() == null) {
418                 target.setUrl(extrapolateChildUrl(src, source.isChildSiteUrlInheritAppendPath(), context));
419                 target.setLocation("url", source.getLocation("url"));
420             }
421         }
422     }
423 
424     @Override
425     protected void mergeScm_Url(Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
426         String src = source.getUrl();
427         if (src != null) {
428             if (sourceDominant) {
429                 target.setUrl(src);
430                 target.setLocation("url", source.getLocation("url"));
431             } else if (target.getUrl() == null) {
432                 target.setUrl(extrapolateChildUrl(src, source.isChildScmUrlInheritAppendPath(), context));
433                 target.setLocation("url", source.getLocation("url"));
434             }
435         }
436     }
437 
438     @Override
439     protected void mergeScm_Connection(Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
440         String src = source.getConnection();
441         if (src != null) {
442             if (sourceDominant) {
443                 target.setConnection(src);
444                 target.setLocation("connection", source.getLocation("connection"));
445             } else if (target.getConnection() == null) {
446                 target.setConnection(extrapolateChildUrl(src, source.isChildScmConnectionInheritAppendPath(), context));
447                 target.setLocation("connection", source.getLocation("connection"));
448             }
449         }
450     }
451 
452     @Override
453     protected void mergeScm_DeveloperConnection(
454             Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
455         String src = source.getDeveloperConnection();
456         if (src != null) {
457             if (sourceDominant) {
458                 target.setDeveloperConnection(src);
459                 target.setLocation("developerConnection", source.getLocation("developerConnection"));
460             } else if (target.getDeveloperConnection() == null) {
461                 String e = extrapolateChildUrl(src, source.isChildScmDeveloperConnectionInheritAppendPath(), context);
462                 target.setDeveloperConnection(e);
463                 target.setLocation("developerConnection", source.getLocation("developerConnection"));
464             }
465         }
466     }
467 
468     @Override
469     protected void mergePlugin_Executions(
470             Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
471         List<PluginExecution> src = source.getExecutions();
472         if (!src.isEmpty()) {
473             List<PluginExecution> tgt = target.getExecutions();
474             Map<Object, PluginExecution> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
475 
476             for (PluginExecution element : src) {
477                 if (sourceDominant || (element.getInherited() != null ? element.isInherited() : source.isInherited())) {
478                     Object key = getPluginExecutionKey(element);
479                     merged.put(key, element);
480                 }
481             }
482 
483             for (PluginExecution element : tgt) {
484                 Object key = getPluginExecutionKey(element);
485                 PluginExecution existing = merged.get(key);
486                 if (existing != null) {
487                     mergePluginExecution(element, existing, sourceDominant, context);
488                 }
489                 merged.put(key, element);
490             }
491 
492             target.setExecutions(new ArrayList<>(merged.values()));
493         }
494     }
495 
496     @Override
497     protected void mergePluginExecution_Goals(
498             PluginExecution target, PluginExecution source, boolean sourceDominant, Map<Object, Object> context) {
499         List<String> src = source.getGoals();
500         if (!src.isEmpty()) {
501             List<String> tgt = target.getGoals();
502             Set<String> excludes = new LinkedHashSet<>(tgt);
503             List<String> merged = new ArrayList<>(tgt.size() + src.size());
504             merged.addAll(tgt);
505             for (String s : src) {
506                 if (!excludes.contains(s)) {
507                     merged.add(s);
508                 }
509             }
510             target.setGoals(merged);
511         }
512     }
513 
514     @Override
515     protected void mergeReportPlugin_ReportSets(
516             ReportPlugin target, ReportPlugin source, boolean sourceDominant, Map<Object, Object> context) {
517         List<ReportSet> src = source.getReportSets();
518         if (!src.isEmpty()) {
519             List<ReportSet> tgt = target.getReportSets();
520             Map<Object, ReportSet> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
521 
522             for (ReportSet rset : src) {
523                 if (sourceDominant || (rset.getInherited() != null ? rset.isInherited() : source.isInherited())) {
524                     Object key = getReportSetKey(rset);
525                     merged.put(key, rset);
526                 }
527             }
528 
529             for (ReportSet element : tgt) {
530                 Object key = getReportSetKey(element);
531                 ReportSet existing = merged.get(key);
532                 if (existing != null) {
533                     mergeReportSet(element, existing, sourceDominant, context);
534                 }
535                 merged.put(key, element);
536             }
537 
538             target.setReportSets(new ArrayList<>(merged.values()));
539         }
540     }
541 
542     @Override
543     protected Object getDependencyKey(Dependency dependency) {
544         return dependency.getManagementKey();
545     }
546 
547     @Override
548     protected Object getPluginKey(Plugin plugin) {
549         return plugin.getKey();
550     }
551 
552     @Override
553     protected Object getPluginExecutionKey(PluginExecution pluginExecution) {
554         return pluginExecution.getId();
555     }
556 
557     @Override
558     protected Object getReportPluginKey(ReportPlugin reportPlugin) {
559         return reportPlugin.getKey();
560     }
561 
562     @Override
563     protected Object getReportSetKey(ReportSet reportSet) {
564         return reportSet.getId();
565     }
566 
567     @Override
568     protected Object getRepositoryBaseKey(RepositoryBase repositoryBase) {
569         return repositoryBase.getId();
570     }
571 
572     @Override
573     protected Object getExtensionKey(Extension extension) {
574         return extension.getGroupId() + ':' + extension.getArtifactId();
575     }
576 
577     @Override
578     protected Object getExclusionKey(Exclusion exclusion) {
579         return exclusion.getGroupId() + ':' + exclusion.getArtifactId();
580     }
581 
582     protected String extrapolateChildUrl(String parentUrl, boolean appendPath, Map<Object, Object> context) {
583         return parentUrl;
584     }
585 }