1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.util.graph.transformer;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.IdentityHashMap;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.ListIterator;
31 import java.util.Map;
32 import java.util.Objects;
33
34 import org.eclipse.aether.RepositoryException;
35 import org.eclipse.aether.RepositorySystemSession;
36 import org.eclipse.aether.artifact.Artifact;
37 import org.eclipse.aether.collection.DependencyGraphTransformationContext;
38 import org.eclipse.aether.collection.DependencyGraphTransformer;
39 import org.eclipse.aether.graph.DefaultDependencyNode;
40 import org.eclipse.aether.graph.Dependency;
41 import org.eclipse.aether.graph.DependencyNode;
42 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
43
44 import static java.util.Objects.requireNonNull;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public final class ConflictResolver implements DependencyGraphTransformer {
67
68
69
70
71
72
73
74 public static final String CONFIG_PROP_VERBOSE = "aether.conflictResolver.verbose";
75
76
77
78
79
80
81 public enum Verbosity {
82
83
84
85
86
87 NONE,
88
89
90
91
92
93
94
95
96 STANDARD,
97
98
99
100
101
102
103
104 FULL
105 }
106
107
108
109
110
111
112
113
114
115
116
117 private static Verbosity getVerbosity(RepositorySystemSession session) {
118 final Object verbosityValue = session.getConfigProperties().get(CONFIG_PROP_VERBOSE);
119 if (verbosityValue instanceof Boolean) {
120 return (Boolean) verbosityValue ? Verbosity.STANDARD : Verbosity.NONE;
121 } else if (verbosityValue instanceof String) {
122 return Boolean.parseBoolean(verbosityValue.toString()) ? Verbosity.STANDARD : Verbosity.NONE;
123 } else if (verbosityValue instanceof Verbosity) {
124 return (Verbosity) verbosityValue;
125 } else if (verbosityValue != null) {
126 throw new IllegalArgumentException("Unsupported Verbosity configuration: " + verbosityValue);
127 }
128 return Verbosity.NONE;
129 }
130
131
132
133
134
135 public static final String NODE_DATA_WINNER = "conflict.winner";
136
137
138
139
140
141 public static final String NODE_DATA_ORIGINAL_SCOPE = "conflict.originalScope";
142
143
144
145
146
147 public static final String NODE_DATA_ORIGINAL_OPTIONALITY = "conflict.originalOptionality";
148
149 private final VersionSelector versionSelector;
150
151 private final ScopeSelector scopeSelector;
152
153 private final ScopeDeriver scopeDeriver;
154
155 private final OptionalitySelector optionalitySelector;
156
157
158
159
160
161
162
163
164
165 public ConflictResolver(
166 VersionSelector versionSelector,
167 ScopeSelector scopeSelector,
168 OptionalitySelector optionalitySelector,
169 ScopeDeriver scopeDeriver) {
170 this.versionSelector = requireNonNull(versionSelector, "version selector cannot be null");
171 this.scopeSelector = requireNonNull(scopeSelector, "scope selector cannot be null");
172 this.optionalitySelector = requireNonNull(optionalitySelector, "optionality selector cannot be null");
173 this.scopeDeriver = requireNonNull(scopeDeriver, "scope deriver cannot be null");
174 }
175
176 public DependencyNode transformGraph(DependencyNode node, DependencyGraphTransformationContext context)
177 throws RepositoryException {
178 requireNonNull(node, "node cannot be null");
179 requireNonNull(context, "context cannot be null");
180 List<?> sortedConflictIds = (List<?>) context.get(TransformationContextKeys.SORTED_CONFLICT_IDS);
181 if (sortedConflictIds == null) {
182 ConflictIdSorter sorter = new ConflictIdSorter();
183 sorter.transformGraph(node, context);
184
185 sortedConflictIds = (List<?>) context.get(TransformationContextKeys.SORTED_CONFLICT_IDS);
186 }
187
188 @SuppressWarnings("unchecked")
189 Map<String, Object> stats = (Map<String, Object>) context.get(TransformationContextKeys.STATS);
190 long time1 = System.nanoTime();
191
192 @SuppressWarnings("unchecked")
193 Collection<Collection<?>> conflictIdCycles =
194 (Collection<Collection<?>>) context.get(TransformationContextKeys.CYCLIC_CONFLICT_IDS);
195 if (conflictIdCycles == null) {
196 throw new RepositoryException("conflict id cycles have not been identified");
197 }
198
199 Map<?, ?> conflictIds = (Map<?, ?>) context.get(TransformationContextKeys.CONFLICT_IDS);
200 if (conflictIds == null) {
201 throw new RepositoryException("conflict groups have not been identified");
202 }
203
204 Map<Object, Collection<Object>> cyclicPredecessors = new HashMap<>();
205 for (Collection<?> cycle : conflictIdCycles) {
206 for (Object conflictId : cycle) {
207 Collection<Object> predecessors = cyclicPredecessors.computeIfAbsent(conflictId, k -> new HashSet<>());
208 predecessors.addAll(cycle);
209 }
210 }
211
212 State state = new State(node, conflictIds, sortedConflictIds.size(), context);
213 for (Iterator<?> it = sortedConflictIds.iterator(); it.hasNext(); ) {
214 Object conflictId = it.next();
215
216
217 state.prepare(conflictId, cyclicPredecessors.get(conflictId));
218
219
220 gatherConflictItems(node, state);
221
222
223 state.finish();
224
225
226 if (!state.items.isEmpty()) {
227 ConflictContext ctx = state.conflictCtx;
228 state.versionSelector.selectVersion(ctx);
229 if (ctx.winner == null) {
230 throw new RepositoryException("conflict resolver did not select winner among " + state.items);
231 }
232 DependencyNode winner = ctx.winner.node;
233
234 state.scopeSelector.selectScope(ctx);
235 if (Verbosity.NONE != state.verbosity) {
236 winner.setData(
237 NODE_DATA_ORIGINAL_SCOPE, winner.getDependency().getScope());
238 }
239 winner.setScope(ctx.scope);
240
241 state.optionalitySelector.selectOptionality(ctx);
242 if (Verbosity.NONE != state.verbosity) {
243 winner.setData(
244 NODE_DATA_ORIGINAL_OPTIONALITY,
245 winner.getDependency().isOptional());
246 }
247 winner.setOptional(ctx.optional);
248
249 removeLosers(state);
250 }
251
252
253 state.winner();
254
255
256 if (!it.hasNext() && !conflictIdCycles.isEmpty() && state.conflictCtx.winner != null) {
257 DependencyNode winner = state.conflictCtx.winner.node;
258 state.prepare(state, null);
259 gatherConflictItems(winner, state);
260 }
261 }
262
263 if (stats != null) {
264 long time2 = System.nanoTime();
265 stats.put("ConflictResolver.totalTime", time2 - time1);
266 stats.put("ConflictResolver.conflictItemCount", state.totalConflictItems);
267 }
268
269 return node;
270 }
271
272 private boolean gatherConflictItems(DependencyNode node, State state) throws RepositoryException {
273 Object conflictId = state.conflictIds.get(node);
274 if (state.currentId.equals(conflictId)) {
275
276 state.add(node);
277
278 } else if (state.loser(node, conflictId)) {
279
280 return false;
281 } else if (state.push(node, conflictId)) {
282
283 for (Iterator<DependencyNode> it = node.getChildren().iterator(); it.hasNext(); ) {
284 DependencyNode child = it.next();
285 if (!gatherConflictItems(child, state)) {
286 it.remove();
287 }
288 }
289 state.pop();
290 }
291 return true;
292 }
293
294 private static void removeLosers(State state) {
295 ConflictItem winner = state.conflictCtx.winner;
296 String winnerArtifactId = ArtifactIdUtils.toId(winner.node.getArtifact());
297 List<DependencyNode> previousParent = null;
298 ListIterator<DependencyNode> childIt = null;
299 HashSet<String> toRemoveIds = new HashSet<>();
300 for (ConflictItem item : state.items) {
301 if (item == winner) {
302 continue;
303 }
304 if (item.parent != previousParent) {
305 childIt = item.parent.listIterator();
306 previousParent = item.parent;
307 }
308 while (childIt.hasNext()) {
309 DependencyNode child = childIt.next();
310 if (child == item.node) {
311
312 if (Verbosity.NONE == state.verbosity) {
313 childIt.remove();
314 break;
315 }
316
317
318 if (Verbosity.STANDARD == state.verbosity) {
319 String childArtifactId = ArtifactIdUtils.toId(child.getArtifact());
320
321
322
323
324
325
326
327
328
329
330
331
332
333 if (!Objects.equals(winnerArtifactId, childArtifactId)) {
334 toRemoveIds.add(childArtifactId);
335 }
336 }
337
338
339 DependencyNode loser = new DefaultDependencyNode(child);
340 loser.setData(NODE_DATA_WINNER, winner.node);
341 loser.setData(
342 NODE_DATA_ORIGINAL_SCOPE, loser.getDependency().getScope());
343 loser.setData(
344 NODE_DATA_ORIGINAL_OPTIONALITY,
345 loser.getDependency().isOptional());
346 loser.setScope(item.getScopes().iterator().next());
347 loser.setChildren(Collections.emptyList());
348 childIt.set(loser);
349 item.node = loser;
350 break;
351 }
352 }
353 }
354
355
356 if (Verbosity.STANDARD == state.verbosity && !toRemoveIds.isEmpty()) {
357 previousParent = null;
358 for (ConflictItem item : state.items) {
359 if (item == winner) {
360 continue;
361 }
362 if (item.parent != previousParent) {
363 childIt = item.parent.listIterator();
364 previousParent = item.parent;
365 }
366 while (childIt.hasNext()) {
367 DependencyNode child = childIt.next();
368 if (child == item.node) {
369 String childArtifactId = ArtifactIdUtils.toId(child.getArtifact());
370 if (toRemoveIds.contains(childArtifactId)
371 && relatedSiblingsCount(child.getArtifact(), item.parent) > 1) {
372 childIt.remove();
373 }
374 break;
375 }
376 }
377 }
378 }
379
380
381
382 }
383
384 private static long relatedSiblingsCount(Artifact artifact, List<DependencyNode> parent) {
385 String ga = artifact.getGroupId() + ":" + artifact.getArtifactId();
386 return parent.stream()
387 .map(DependencyNode::getArtifact)
388 .filter(a -> ga.equals(a.getGroupId() + ":" + a.getArtifactId()))
389 .count();
390 }
391
392 static final class NodeInfo {
393
394
395
396
397 int minDepth;
398
399
400
401
402
403
404 Object derivedScopes;
405
406
407
408
409
410
411 int derivedOptionalities;
412
413
414
415
416
417 List<ConflictItem> children;
418
419 static final int CHANGE_SCOPE = 0x01;
420
421 static final int CHANGE_OPTIONAL = 0x02;
422
423 private static final int OPT_FALSE = 0x01;
424
425 private static final int OPT_TRUE = 0x02;
426
427 NodeInfo(int depth, String derivedScope, boolean optional) {
428 minDepth = depth;
429 derivedScopes = derivedScope;
430 derivedOptionalities = optional ? OPT_TRUE : OPT_FALSE;
431 }
432
433 @SuppressWarnings("unchecked")
434 int update(int depth, String derivedScope, boolean optional) {
435 if (depth < minDepth) {
436 minDepth = depth;
437 }
438 int changes;
439 if (derivedScopes.equals(derivedScope)) {
440 changes = 0;
441 } else if (derivedScopes instanceof Collection) {
442 changes = ((Collection<String>) derivedScopes).add(derivedScope) ? CHANGE_SCOPE : 0;
443 } else {
444 Collection<String> scopes = new HashSet<>();
445 scopes.add((String) derivedScopes);
446 scopes.add(derivedScope);
447 derivedScopes = scopes;
448 changes = CHANGE_SCOPE;
449 }
450 int bit = optional ? OPT_TRUE : OPT_FALSE;
451 if ((derivedOptionalities & bit) == 0) {
452 derivedOptionalities |= bit;
453 changes |= CHANGE_OPTIONAL;
454 }
455 return changes;
456 }
457
458 void add(ConflictItem item) {
459 if (children == null) {
460 children = new ArrayList<>(1);
461 }
462 children.add(item);
463 }
464 }
465
466 final class State {
467
468
469
470
471 Object currentId;
472
473
474
475
476 int totalConflictItems;
477
478
479
480
481 final Verbosity verbosity;
482
483
484
485
486
487 final Map<Object, DependencyNode> resolvedIds;
488
489
490
491
492
493
494 final Collection<Object> potentialAncestorIds;
495
496
497
498
499 final Map<?, ?> conflictIds;
500
501
502
503
504 final List<ConflictItem> items;
505
506
507
508
509
510 final Map<List<DependencyNode>, NodeInfo> infos;
511
512
513
514
515
516 final Map<List<DependencyNode>, Object> stack;
517
518
519
520
521 final List<DependencyNode> parentNodes;
522
523
524
525
526 final List<String> parentScopes;
527
528
529
530
531 final List<Boolean> parentOptionals;
532
533
534
535
536
537 final List<NodeInfo> parentInfos;
538
539
540
541
542
543 final ConflictContext conflictCtx;
544
545
546
547
548
549 final ScopeContext scopeCtx;
550
551
552
553
554 final VersionSelector versionSelector;
555
556
557
558
559 final ScopeSelector scopeSelector;
560
561
562
563
564 final ScopeDeriver scopeDeriver;
565
566
567
568
569 final OptionalitySelector optionalitySelector;
570
571 State(
572 DependencyNode root,
573 Map<?, ?> conflictIds,
574 int conflictIdCount,
575 DependencyGraphTransformationContext context)
576 throws RepositoryException {
577 this.conflictIds = conflictIds;
578 this.verbosity = getVerbosity(context.getSession());
579 potentialAncestorIds = new HashSet<>(conflictIdCount * 2);
580 resolvedIds = new HashMap<>(conflictIdCount * 2);
581 items = new ArrayList<>(256);
582 infos = new IdentityHashMap<>(64);
583 stack = new IdentityHashMap<>(64);
584 parentNodes = new ArrayList<>(64);
585 parentScopes = new ArrayList<>(64);
586 parentOptionals = new ArrayList<>(64);
587 parentInfos = new ArrayList<>(64);
588 conflictCtx = new ConflictContext(root, conflictIds, items);
589 scopeCtx = new ScopeContext(null, null);
590 versionSelector = ConflictResolver.this.versionSelector.getInstance(root, context);
591 scopeSelector = ConflictResolver.this.scopeSelector.getInstance(root, context);
592 scopeDeriver = ConflictResolver.this.scopeDeriver.getInstance(root, context);
593 optionalitySelector = ConflictResolver.this.optionalitySelector.getInstance(root, context);
594 }
595
596 void prepare(Object conflictId, Collection<Object> cyclicPredecessors) {
597 currentId = conflictId;
598 conflictCtx.conflictId = conflictId;
599 conflictCtx.winner = null;
600 conflictCtx.scope = null;
601 conflictCtx.optional = null;
602 items.clear();
603 infos.clear();
604 if (cyclicPredecessors != null) {
605 potentialAncestorIds.addAll(cyclicPredecessors);
606 }
607 }
608
609 void finish() {
610 List<DependencyNode> previousParent = null;
611 int previousDepth = 0;
612 totalConflictItems += items.size();
613 for (ListIterator<ConflictItem> iterator = items.listIterator(items.size()); iterator.hasPrevious(); ) {
614 ConflictItem item = iterator.previous();
615 if (item.parent == previousParent) {
616 item.depth = previousDepth;
617 } else if (item.parent != null) {
618 previousParent = item.parent;
619 NodeInfo info = infos.get(previousParent);
620 previousDepth = info.minDepth + 1;
621 item.depth = previousDepth;
622 }
623 }
624 potentialAncestorIds.add(currentId);
625 }
626
627 void winner() {
628 resolvedIds.put(currentId, (conflictCtx.winner != null) ? conflictCtx.winner.node : null);
629 }
630
631 boolean loser(DependencyNode node, Object conflictId) {
632 DependencyNode winner = resolvedIds.get(conflictId);
633 return winner != null && winner != node;
634 }
635
636 boolean push(DependencyNode node, Object conflictId) throws RepositoryException {
637 if (conflictId == null) {
638 if (node.getDependency() != null) {
639 if (node.getData().get(NODE_DATA_WINNER) != null) {
640 return false;
641 }
642 throw new RepositoryException("missing conflict id for node " + node);
643 }
644 } else if (!potentialAncestorIds.contains(conflictId)) {
645 return false;
646 }
647
648 List<DependencyNode> graphNode = node.getChildren();
649 if (stack.put(graphNode, Boolean.TRUE) != null) {
650 return false;
651 }
652
653 int depth = depth();
654 String scope = deriveScope(node, conflictId);
655 boolean optional = deriveOptional(node, conflictId);
656 NodeInfo info = infos.get(graphNode);
657 if (info == null) {
658 info = new NodeInfo(depth, scope, optional);
659 infos.put(graphNode, info);
660 parentInfos.add(info);
661 parentNodes.add(node);
662 parentScopes.add(scope);
663 parentOptionals.add(optional);
664 } else {
665 int changes = info.update(depth, scope, optional);
666 if (changes == 0) {
667 stack.remove(graphNode);
668 return false;
669 }
670 parentInfos.add(null);
671 parentNodes.add(node);
672 parentScopes.add(scope);
673 parentOptionals.add(optional);
674 if (info.children != null) {
675 if ((changes & NodeInfo.CHANGE_SCOPE) != 0) {
676 ListIterator<ConflictItem> itemIterator = info.children.listIterator(info.children.size());
677 while (itemIterator.hasPrevious()) {
678 ConflictItem item = itemIterator.previous();
679 String childScope = deriveScope(item.node, null);
680 item.addScope(childScope);
681 }
682 }
683 if ((changes & NodeInfo.CHANGE_OPTIONAL) != 0) {
684 ListIterator<ConflictItem> itemIterator = info.children.listIterator(info.children.size());
685 while (itemIterator.hasPrevious()) {
686 ConflictItem item = itemIterator.previous();
687 boolean childOptional = deriveOptional(item.node, null);
688 item.addOptional(childOptional);
689 }
690 }
691 }
692 }
693
694 return true;
695 }
696
697 void pop() {
698 int last = parentInfos.size() - 1;
699 parentInfos.remove(last);
700 parentScopes.remove(last);
701 parentOptionals.remove(last);
702 DependencyNode node = parentNodes.remove(last);
703 stack.remove(node.getChildren());
704 }
705
706 void add(DependencyNode node) throws RepositoryException {
707 DependencyNode parent = parent();
708 if (parent == null) {
709 ConflictItem item = newConflictItem(parent, node);
710 items.add(item);
711 } else {
712 NodeInfo info = parentInfos.get(parentInfos.size() - 1);
713 if (info != null) {
714 ConflictItem item = newConflictItem(parent, node);
715 info.add(item);
716 items.add(item);
717 }
718 }
719 }
720
721 private ConflictItem newConflictItem(DependencyNode parent, DependencyNode node) throws RepositoryException {
722 return new ConflictItem(parent, node, deriveScope(node, null), deriveOptional(node, null));
723 }
724
725 private int depth() {
726 return parentNodes.size();
727 }
728
729 private DependencyNode parent() {
730 int size = parentNodes.size();
731 return (size <= 0) ? null : parentNodes.get(size - 1);
732 }
733
734 private String deriveScope(DependencyNode node, Object conflictId) throws RepositoryException {
735 if ((node.getManagedBits() & DependencyNode.MANAGED_SCOPE) != 0
736 || (conflictId != null && resolvedIds.containsKey(conflictId))) {
737 return scope(node.getDependency());
738 }
739
740 int depth = parentNodes.size();
741 scopes(depth, node.getDependency());
742 if (depth > 0) {
743 scopeDeriver.deriveScope(scopeCtx);
744 }
745 return scopeCtx.derivedScope;
746 }
747
748 private void scopes(int parent, Dependency child) {
749 scopeCtx.parentScope = (parent > 0) ? parentScopes.get(parent - 1) : null;
750 scopeCtx.derivedScope = scope(child);
751 scopeCtx.childScope = scope(child);
752 }
753
754 private String scope(Dependency dependency) {
755 return (dependency != null) ? dependency.getScope() : null;
756 }
757
758 private boolean deriveOptional(DependencyNode node, Object conflictId) {
759 Dependency dep = node.getDependency();
760 boolean optional = (dep != null) && dep.isOptional();
761 if (optional
762 || (node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL) != 0
763 || (conflictId != null && resolvedIds.containsKey(conflictId))) {
764 return optional;
765 }
766 int depth = parentNodes.size();
767 return (depth > 0) ? parentOptionals.get(depth - 1) : false;
768 }
769 }
770
771
772
773
774
775
776
777
778 public static final class ScopeContext {
779
780 String parentScope;
781
782 String childScope;
783
784 String derivedScope;
785
786
787
788
789
790
791
792
793
794 public ScopeContext(String parentScope, String childScope) {
795 this.parentScope = (parentScope != null) ? parentScope : "";
796 derivedScope = (childScope != null) ? childScope : "";
797 this.childScope = (childScope != null) ? childScope : "";
798 }
799
800
801
802
803
804
805
806 public String getParentScope() {
807 return parentScope;
808 }
809
810
811
812
813
814
815
816 public String getChildScope() {
817 return childScope;
818 }
819
820
821
822
823
824
825
826 public String getDerivedScope() {
827 return derivedScope;
828 }
829
830
831
832
833
834
835 public void setDerivedScope(String derivedScope) {
836 this.derivedScope = (derivedScope != null) ? derivedScope : "";
837 }
838 }
839
840
841
842
843
844
845
846 public static final class ConflictItem {
847
848
849 final List<DependencyNode> parent;
850
851
852 final Artifact artifact;
853
854
855 DependencyNode node;
856
857 int depth;
858
859
860 Object scopes;
861
862
863 int optionalities;
864
865
866
867
868 public static final int OPTIONAL_FALSE = 0x01;
869
870
871
872
873 public static final int OPTIONAL_TRUE = 0x02;
874
875 ConflictItem(DependencyNode parent, DependencyNode node, String scope, boolean optional) {
876 if (parent != null) {
877 this.parent = parent.getChildren();
878 this.artifact = parent.getArtifact();
879 } else {
880 this.parent = null;
881 this.artifact = null;
882 }
883 this.node = node;
884 this.scopes = scope;
885 this.optionalities = optional ? OPTIONAL_TRUE : OPTIONAL_FALSE;
886 }
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901 public ConflictItem(
902 DependencyNode parent, DependencyNode node, int depth, int optionalities, String... scopes) {
903 this.parent = (parent != null) ? parent.getChildren() : null;
904 this.artifact = (parent != null) ? parent.getArtifact() : null;
905 this.node = node;
906 this.depth = depth;
907 this.optionalities = optionalities;
908 this.scopes = Arrays.asList(scopes);
909 }
910
911
912
913
914
915
916
917 public boolean isSibling(ConflictItem item) {
918 return parent == item.parent;
919 }
920
921
922
923
924
925
926 public DependencyNode getNode() {
927 return node;
928 }
929
930
931
932
933
934
935 public Dependency getDependency() {
936 return node.getDependency();
937 }
938
939
940
941
942
943
944
945
946 public int getDepth() {
947 return depth;
948 }
949
950
951
952
953
954
955
956
957 @SuppressWarnings("unchecked")
958 public Collection<String> getScopes() {
959 if (scopes instanceof String) {
960 return Collections.singleton((String) scopes);
961 }
962 return (Collection<String>) scopes;
963 }
964
965 @SuppressWarnings("unchecked")
966 void addScope(String scope) {
967 if (scopes instanceof Collection) {
968 ((Collection<String>) scopes).add(scope);
969 } else if (!scopes.equals(scope)) {
970 Collection<Object> set = new HashSet<>();
971 set.add(scopes);
972 set.add(scope);
973 scopes = set;
974 }
975 }
976
977
978
979
980
981
982
983
984
985 public int getOptionalities() {
986 return optionalities;
987 }
988
989 void addOptional(boolean optional) {
990 optionalities |= optional ? OPTIONAL_TRUE : OPTIONAL_FALSE;
991 }
992
993 @Override
994 public String toString() {
995 return node + " @ " + depth + " < " + artifact;
996 }
997 }
998
999
1000
1001
1002
1003
1004
1005
1006
1007 public static final class ConflictContext {
1008
1009 final DependencyNode root;
1010
1011 final Map<?, ?> conflictIds;
1012
1013 final Collection<ConflictItem> items;
1014
1015 Object conflictId;
1016
1017 ConflictItem winner;
1018
1019 String scope;
1020
1021 Boolean optional;
1022
1023 ConflictContext(DependencyNode root, Map<?, ?> conflictIds, Collection<ConflictItem> items) {
1024 this.root = root;
1025 this.conflictIds = conflictIds;
1026 this.items = Collections.unmodifiableCollection(items);
1027 }
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040 public ConflictContext(
1041 DependencyNode root,
1042 Object conflictId,
1043 Map<DependencyNode, Object> conflictIds,
1044 Collection<ConflictItem> items) {
1045 this(root, conflictIds, items);
1046 this.conflictId = conflictId;
1047 }
1048
1049
1050
1051
1052
1053
1054 public DependencyNode getRoot() {
1055 return root;
1056 }
1057
1058
1059
1060
1061
1062
1063
1064 public boolean isIncluded(DependencyNode node) {
1065 return conflictId.equals(conflictIds.get(node));
1066 }
1067
1068
1069
1070
1071
1072
1073 public Collection<ConflictItem> getItems() {
1074 return items;
1075 }
1076
1077
1078
1079
1080
1081
1082 public ConflictItem getWinner() {
1083 return winner;
1084 }
1085
1086
1087
1088
1089
1090
1091 public void setWinner(ConflictItem winner) {
1092 this.winner = winner;
1093 }
1094
1095
1096
1097
1098
1099
1100 public String getScope() {
1101 return scope;
1102 }
1103
1104
1105
1106
1107
1108
1109 public void setScope(String scope) {
1110 this.scope = scope;
1111 }
1112
1113
1114
1115
1116
1117
1118 public Boolean getOptional() {
1119 return optional;
1120 }
1121
1122
1123
1124
1125
1126
1127 public void setOptional(Boolean optional) {
1128 this.optional = optional;
1129 }
1130
1131 @Override
1132 public String toString() {
1133 return winner + " @ " + scope + " < " + items;
1134 }
1135 }
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145 public abstract static class VersionSelector {
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161 public VersionSelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
1162 throws RepositoryException {
1163 return this;
1164 }
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 public abstract void selectVersion(ConflictContext context) throws RepositoryException;
1176 }
1177
1178
1179
1180
1181
1182
1183
1184
1185 public abstract static class ScopeSelector {
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201 public ScopeSelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
1202 throws RepositoryException {
1203 return this;
1204 }
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215 public abstract void selectScope(ConflictContext context) throws RepositoryException;
1216 }
1217
1218
1219
1220
1221
1222
1223
1224 public abstract static class ScopeDeriver {
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240 public ScopeDeriver getInstance(DependencyNode root, DependencyGraphTransformationContext context)
1241 throws RepositoryException {
1242 return this;
1243 }
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253 public abstract void deriveScope(ScopeContext context) throws RepositoryException;
1254 }
1255
1256
1257
1258
1259
1260
1261
1262
1263 public abstract static class OptionalitySelector {
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279 public OptionalitySelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
1280 throws RepositoryException {
1281 return this;
1282 }
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293 public abstract void selectOptionality(ConflictContext context) throws RepositoryException;
1294 }
1295 }