1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.Objects;
27 import java.util.Set;
28
29 import org.apache.maven.api.model.BuildBase;
30 import org.apache.maven.api.model.CiManagement;
31 import org.apache.maven.api.model.Dependency;
32 import org.apache.maven.api.model.DeploymentRepository;
33 import org.apache.maven.api.model.DistributionManagement;
34 import org.apache.maven.api.model.Exclusion;
35 import org.apache.maven.api.model.Extension;
36 import org.apache.maven.api.model.InputLocation;
37 import org.apache.maven.api.model.IssueManagement;
38 import org.apache.maven.api.model.Model;
39 import org.apache.maven.api.model.ModelBase;
40 import org.apache.maven.api.model.Organization;
41 import org.apache.maven.api.model.Plugin;
42 import org.apache.maven.api.model.PluginExecution;
43 import org.apache.maven.api.model.ReportPlugin;
44 import org.apache.maven.api.model.ReportSet;
45 import org.apache.maven.api.model.Repository;
46 import org.apache.maven.api.model.RepositoryBase;
47 import org.apache.maven.api.model.Scm;
48 import org.apache.maven.api.model.Site;
49 import org.apache.maven.model.v4.MavenMerger;
50 import org.codehaus.plexus.util.StringUtils;
51
52
53
54
55
56
57
58 public class MavenModelMerger extends MavenMerger {
59
60
61
62
63 public static final String CHILD_PATH_ADJUSTMENT = "child-path-adjustment";
64
65
66
67
68 public static final String ARTIFACT_ID = "artifact-id";
69
70 public MavenModelMerger() {
71 super(false);
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public void merge(
87 org.apache.maven.model.Model target,
88 org.apache.maven.model.Model source,
89 boolean sourceDominant,
90 Map<?, ?> hints) {
91 Objects.requireNonNull(target, "target cannot be null");
92 if (source == null) {
93 return;
94 }
95 target.update(merge(target.getDelegate(), source.getDelegate(), sourceDominant, hints));
96 }
97
98 @Override
99 public Model merge(Model target, Model source, boolean sourceDominant, Map<?, ?> hints) {
100 return super.merge(target, source, sourceDominant, hints);
101 }
102
103 @Override
104 protected Model mergeModel(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
105 context.put(ARTIFACT_ID, target.getArtifactId());
106
107 return super.mergeModel(target, source, sourceDominant, context);
108 }
109
110 @Override
111 protected void mergeModel_Name(
112 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
113 String src = source.getName();
114 if (src != null) {
115 if (sourceDominant) {
116 builder.name(src);
117 builder.location("name", source.getLocation("name"));
118 }
119 }
120 }
121
122 @Override
123 protected void mergeModel_Url(
124 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
125 String src = source.getUrl();
126 if (src != null) {
127 if (sourceDominant) {
128 builder.url(src);
129 builder.location("url", source.getLocation("url"));
130 } else if (target.getUrl() == null) {
131 builder.url(extrapolateChildUrl(src, source.isChildProjectUrlInheritAppendPath(), context));
132 builder.location("url", source.getLocation("url"));
133 }
134 }
135 }
136
137
138
139
140
141 @Override
142 protected void mergeModel_Organization(
143 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
144 Organization src = source.getOrganization();
145 if (src != null) {
146 Organization tgt = target.getOrganization();
147 if (tgt == null) {
148 builder.organization(src);
149 builder.location("organisation", source.getLocation("organisation"));
150 }
151 }
152 }
153
154 @Override
155 protected void mergeModel_IssueManagement(
156 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
157 IssueManagement src = source.getIssueManagement();
158 if (src != null) {
159 IssueManagement tgt = target.getIssueManagement();
160 if (tgt == null) {
161 builder.issueManagement(src);
162 builder.location("issueManagement", source.getLocation("issueManagement"));
163 }
164 }
165 }
166
167 @Override
168 protected void mergeModel_CiManagement(
169 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
170 CiManagement src = source.getCiManagement();
171 if (src != null) {
172 CiManagement tgt = target.getCiManagement();
173 if (tgt == null) {
174 builder.ciManagement(src);
175 builder.location("ciManagement", source.getLocation("ciManagement"));
176 }
177 }
178 }
179
180 @Override
181 protected void mergeModel_ModelVersion(
182 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
183
184 }
185
186 @Override
187 protected void mergeModel_ArtifactId(
188 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
189
190 }
191
192 @Override
193 protected void mergeModel_Profiles(
194 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
195
196 }
197
198 @Override
199 protected void mergeModel_Prerequisites(
200 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
201
202 }
203
204 @Override
205 protected void mergeModel_Licenses(
206 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
207 builder.licenses(target.getLicenses().isEmpty() ? source.getLicenses() : target.getLicenses());
208 }
209
210 @Override
211 protected void mergeModel_Developers(
212 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
213 builder.developers(target.getDevelopers().isEmpty() ? source.getDevelopers() : target.getDevelopers());
214 }
215
216 @Override
217 protected void mergeModel_Contributors(
218 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
219 builder.contributors(target.getContributors().isEmpty() ? source.getContributors() : target.getContributors());
220 }
221
222 @Override
223 protected void mergeModel_MailingLists(
224 Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
225 if (target.getMailingLists().isEmpty()) {
226 builder.mailingLists(source.getMailingLists());
227 }
228 }
229
230 @Override
231 protected void mergeModelBase_Modules(
232 ModelBase.Builder builder,
233 ModelBase target,
234 ModelBase source,
235 boolean sourceDominant,
236 Map<Object, Object> context) {
237 List<String> src = source.getModules();
238 if (!src.isEmpty() && sourceDominant) {
239 List<Integer> indices = new ArrayList<>();
240 List<String> tgt = target.getModules();
241 Set<String> excludes = new LinkedHashSet<>(tgt);
242 List<String> merged = new ArrayList<>(tgt.size() + src.size());
243 merged.addAll(tgt);
244 for (int i = 0, n = tgt.size(); i < n; i++) {
245 indices.add(i);
246 }
247 for (int i = 0, n = src.size(); i < n; i++) {
248 String s = src.get(i);
249 if (!excludes.contains(s)) {
250 merged.add(s);
251 indices.add(~i);
252 }
253 }
254 builder.modules(merged);
255 builder.location(
256 "modules",
257 InputLocation.merge(target.getLocation("modules"), source.getLocation("modules"), indices));
258 }
259 }
260
261
262
263
264
265 @Override
266 protected void mergeModelBase_Repositories(
267 ModelBase.Builder builder,
268 ModelBase target,
269 ModelBase source,
270 boolean sourceDominant,
271 Map<Object, Object> context) {
272 List<Repository> src = source.getRepositories();
273 if (!src.isEmpty()) {
274 List<Repository> tgt = target.getRepositories();
275 Map<Object, Repository> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
276
277 List<Repository> dominant, recessive;
278 if (sourceDominant) {
279 dominant = src;
280 recessive = tgt;
281 } else {
282 dominant = tgt;
283 recessive = src;
284 }
285
286 for (Repository element : dominant) {
287 Object key = getRepositoryKey().apply(element);
288 merged.put(key, element);
289 }
290
291 for (Repository element : recessive) {
292 Object key = getRepositoryKey().apply(element);
293 if (!merged.containsKey(key)) {
294 merged.put(key, element);
295 }
296 }
297
298 builder.repositories(merged.values());
299 }
300 }
301
302 @Override
303 protected void mergeModelBase_PluginRepositories(
304 ModelBase.Builder builder,
305 ModelBase target,
306 ModelBase source,
307 boolean sourceDominant,
308 Map<Object, Object> context) {
309 List<Repository> src = source.getPluginRepositories();
310 if (!src.isEmpty()) {
311 List<Repository> tgt = target.getPluginRepositories();
312 Map<Object, Repository> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
313
314 List<Repository> dominant, recessive;
315 if (sourceDominant) {
316 dominant = src;
317 recessive = tgt;
318 } else {
319 dominant = tgt;
320 recessive = src;
321 }
322
323 for (Repository element : dominant) {
324 Object key = getRepositoryKey().apply(element);
325 merged.put(key, element);
326 }
327
328 for (Repository element : recessive) {
329 Object key = getRepositoryKey().apply(element);
330 if (!merged.containsKey(key)) {
331 merged.put(key, element);
332 }
333 }
334
335 builder.pluginRepositories(merged.values());
336 }
337 }
338
339
340
341
342 @Override
343 protected void mergeBuildBase_Filters(
344 BuildBase.Builder builder,
345 BuildBase target,
346 BuildBase source,
347 boolean sourceDominant,
348 Map<Object, Object> context) {
349 List<String> src = source.getFilters();
350 if (!src.isEmpty()) {
351 List<String> tgt = target.getFilters();
352 Set<String> excludes = new LinkedHashSet<>(tgt);
353 List<String> merged = new ArrayList<>(tgt.size() + src.size());
354 merged.addAll(tgt);
355 for (String s : src) {
356 if (!excludes.contains(s)) {
357 merged.add(s);
358 }
359 }
360 builder.filters(merged);
361 }
362 }
363
364 @Override
365 protected void mergeBuildBase_Resources(
366 BuildBase.Builder builder,
367 BuildBase target,
368 BuildBase source,
369 boolean sourceDominant,
370 Map<Object, Object> context) {
371 if (sourceDominant || target.getResources().isEmpty()) {
372 super.mergeBuildBase_Resources(builder, target, source, sourceDominant, context);
373 }
374 }
375
376 @Override
377 protected void mergeBuildBase_TestResources(
378 BuildBase.Builder builder,
379 BuildBase target,
380 BuildBase source,
381 boolean sourceDominant,
382 Map<Object, Object> context) {
383 if (sourceDominant || target.getTestResources().isEmpty()) {
384 super.mergeBuildBase_TestResources(builder, target, source, sourceDominant, context);
385 }
386 }
387
388 @Override
389 protected void mergeDistributionManagement_Relocation(
390 DistributionManagement.Builder builder,
391 DistributionManagement target,
392 DistributionManagement source,
393 boolean sourceDominant,
394 Map<Object, Object> context) {}
395
396 @Override
397 protected void mergeDistributionManagement_Repository(
398 DistributionManagement.Builder builder,
399 DistributionManagement target,
400 DistributionManagement source,
401 boolean sourceDominant,
402 Map<Object, Object> context) {
403 DeploymentRepository src = source.getRepository();
404 if (src != null) {
405 DeploymentRepository tgt = target.getRepository();
406 if (sourceDominant || tgt == null) {
407 tgt = DeploymentRepository.newInstance(false);
408 builder.repository(mergeDeploymentRepository(tgt, src, sourceDominant, context));
409 }
410 }
411 }
412
413 @Override
414 protected void mergeDistributionManagement_SnapshotRepository(
415 DistributionManagement.Builder builder,
416 DistributionManagement target,
417 DistributionManagement source,
418 boolean sourceDominant,
419 Map<Object, Object> context) {
420 DeploymentRepository src = source.getSnapshotRepository();
421 if (src != null) {
422 DeploymentRepository tgt = target.getSnapshotRepository();
423 if (sourceDominant || tgt == null) {
424 tgt = DeploymentRepository.newInstance(false);
425 builder.snapshotRepository(mergeDeploymentRepository(tgt, src, sourceDominant, context));
426 }
427 }
428 }
429
430 @Override
431 protected void mergeDistributionManagement_Site(
432 DistributionManagement.Builder builder,
433 DistributionManagement target,
434 DistributionManagement source,
435 boolean sourceDominant,
436 Map<Object, Object> context) {
437 Site src = source.getSite();
438 if (src != null) {
439 Site tgt = target.getSite();
440 if (tgt == null) {
441 tgt = Site.newBuilder(false).build();
442 }
443 Site.Builder sbuilder = Site.newBuilder(tgt);
444 if (sourceDominant || tgt == null || isSiteEmpty(tgt)) {
445 mergeSite(sbuilder, tgt, src, sourceDominant, context);
446 }
447 super.mergeSite_ChildSiteUrlInheritAppendPath(sbuilder, tgt, src, sourceDominant, context);
448 builder.site(sbuilder.build());
449 }
450 }
451
452 @Override
453 protected void mergeSite_ChildSiteUrlInheritAppendPath(
454 Site.Builder builder, Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {}
455
456 protected boolean isSiteEmpty(Site site) {
457 return StringUtils.isEmpty(site.getId())
458 && StringUtils.isEmpty(site.getName())
459 && StringUtils.isEmpty(site.getUrl());
460 }
461
462 @Override
463 protected void mergeSite_Url(
464 Site.Builder builder, Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
465 String src = source.getUrl();
466 if (src != null) {
467 if (sourceDominant) {
468 builder.url(src);
469 builder.location("url", source.getLocation("url"));
470 } else if (target.getUrl() == null) {
471 builder.url(extrapolateChildUrl(src, source.isChildSiteUrlInheritAppendPath(), context));
472 builder.location("url", source.getLocation("url"));
473 }
474 }
475 }
476
477 @Override
478 protected void mergeScm_Url(
479 Scm.Builder builder, Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
480 String src = source.getUrl();
481 if (src != null) {
482 if (sourceDominant) {
483 builder.url(src);
484 builder.location("url", source.getLocation("url"));
485 } else if (target.getUrl() == null) {
486 builder.url(extrapolateChildUrl(src, source.isChildScmUrlInheritAppendPath(), context));
487 builder.location("url", source.getLocation("url"));
488 }
489 }
490 }
491
492 @Override
493 protected void mergeScm_Connection(
494 Scm.Builder builder, Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
495 String src = source.getConnection();
496 if (src != null) {
497 if (sourceDominant) {
498 builder.connection(src);
499 builder.location("connection", source.getLocation("connection"));
500 } else if (target.getConnection() == null) {
501 builder.connection(extrapolateChildUrl(src, source.isChildScmConnectionInheritAppendPath(), context));
502 builder.location("connection", source.getLocation("connection"));
503 }
504 }
505 }
506
507 @Override
508 protected void mergeScm_DeveloperConnection(
509 Scm.Builder builder, Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
510 String src = source.getDeveloperConnection();
511 if (src != null) {
512 if (sourceDominant) {
513 builder.developerConnection(src);
514 builder.location("developerConnection", source.getLocation("developerConnection"));
515 } else if (target.getDeveloperConnection() == null) {
516 String e = extrapolateChildUrl(src, source.isChildScmDeveloperConnectionInheritAppendPath(), context);
517 builder.developerConnection(e);
518 builder.location("developerConnection", source.getLocation("developerConnection"));
519 }
520 }
521 }
522
523 @Override
524 protected void mergePlugin_Executions(
525 Plugin.Builder builder, Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
526 List<PluginExecution> src = source.getExecutions();
527 if (!src.isEmpty()) {
528 List<PluginExecution> tgt = target.getExecutions();
529 Map<Object, PluginExecution> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
530
531 for (PluginExecution element : src) {
532 if (sourceDominant || (element.getInherited() != null ? element.isInherited() : source.isInherited())) {
533 Object key = getPluginExecutionKey().apply(element);
534 merged.put(key, element);
535 }
536 }
537
538 for (PluginExecution element : tgt) {
539 Object key = getPluginExecutionKey().apply(element);
540 PluginExecution existing = merged.get(key);
541 if (existing != null) {
542 element = mergePluginExecution(element, existing, sourceDominant, context);
543 }
544 merged.put(key, element);
545 }
546
547 builder.executions(merged.values());
548 }
549 }
550
551 @Override
552 protected void mergePluginExecution_Goals(
553 PluginExecution.Builder builder,
554 PluginExecution target,
555 PluginExecution source,
556 boolean sourceDominant,
557 Map<Object, Object> context) {
558 List<String> src = source.getGoals();
559 if (!src.isEmpty()) {
560 List<String> tgt = target.getGoals();
561 Set<String> excludes = new LinkedHashSet<>(tgt);
562 List<String> merged = new ArrayList<>(tgt.size() + src.size());
563 merged.addAll(tgt);
564 for (String s : src) {
565 if (!excludes.contains(s)) {
566 merged.add(s);
567 }
568 }
569 builder.goals(merged);
570 }
571 }
572
573 @Override
574 protected void mergeReportPlugin_ReportSets(
575 ReportPlugin.Builder builder,
576 ReportPlugin target,
577 ReportPlugin source,
578 boolean sourceDominant,
579 Map<Object, Object> context) {
580 List<ReportSet> src = source.getReportSets();
581 if (!src.isEmpty()) {
582 List<ReportSet> tgt = target.getReportSets();
583 Map<Object, ReportSet> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
584
585 for (ReportSet rset : src) {
586 if (sourceDominant || (rset.getInherited() != null ? rset.isInherited() : source.isInherited())) {
587 Object key = getReportSetKey().apply(rset);
588 merged.put(key, rset);
589 }
590 }
591
592 for (ReportSet element : tgt) {
593 Object key = getReportSetKey().apply(element);
594 ReportSet existing = merged.get(key);
595 if (existing != null) {
596 mergeReportSet(element, existing, sourceDominant, context);
597 }
598 merged.put(key, element);
599 }
600
601 builder.reportSets(merged.values());
602 }
603 }
604
605 @Override
606 protected KeyComputer<Dependency> getDependencyKey() {
607 return Dependency::getManagementKey;
608 }
609
610 @Override
611 protected KeyComputer<Plugin> getPluginKey() {
612 return Plugin::getKey;
613 }
614
615 @Override
616 protected KeyComputer<PluginExecution> getPluginExecutionKey() {
617 return PluginExecution::getId;
618 }
619
620 @Override
621 protected KeyComputer<ReportPlugin> getReportPluginKey() {
622 return ReportPlugin::getKey;
623 }
624
625 @Override
626 protected KeyComputer<ReportSet> getReportSetKey() {
627 return ReportSet::getId;
628 }
629
630 @Override
631 protected KeyComputer<RepositoryBase> getRepositoryBaseKey() {
632 return RepositoryBase::getId;
633 }
634
635 @Override
636 protected KeyComputer<Extension> getExtensionKey() {
637 return e -> e.getGroupId() + ':' + e.getArtifactId();
638 }
639
640 @Override
641 protected KeyComputer<Exclusion> getExclusionKey() {
642 return e -> e.getGroupId() + ':' + e.getArtifactId();
643 }
644
645 protected String extrapolateChildUrl(String parentUrl, boolean appendPath, Map<Object, Object> context) {
646 return parentUrl;
647 }
648 }