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.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.ConfigurationProperties;
35  import org.eclipse.aether.RepositoryException;
36  import org.eclipse.aether.RepositorySystemSession;
37  import org.eclipse.aether.artifact.Artifact;
38  import org.eclipse.aether.collection.DependencyGraphTransformationContext;
39  import org.eclipse.aether.collection.DependencyGraphTransformer;
40  import org.eclipse.aether.graph.DefaultDependencyNode;
41  import org.eclipse.aether.graph.Dependency;
42  import org.eclipse.aether.graph.DependencyNode;
43  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
44  
45  import static java.util.Objects.requireNonNull;
46  
47  /**
48   * A dependency graph transformer that resolves version and scope conflicts among dependencies. For a given set of
49   * conflicting nodes, one node will be chosen as the winner and the other nodes are removed from the dependency graph.
50   * The exact rules by which a winning node and its effective scope are determined are controlled by user-supplied
51   * implementations of {@link VersionSelector}, {@link ScopeSelector}, {@link OptionalitySelector} and
52   * {@link ScopeDeriver}.
53   * <p>
54   * By default, this graph transformer will turn the dependency graph into a tree without duplicate artifacts. Using the
55   * configuration property {@link #CONFIG_PROP_VERBOSE}, a verbose mode can be enabled where the graph is still turned
56   * into a tree but all nodes participating in a conflict are retained. The nodes that were rejected during conflict
57   * resolution have no children and link back to the winner node via the {@link #NODE_DATA_WINNER} key in their custom
58   * data. Additionally, the keys {@link #NODE_DATA_ORIGINAL_SCOPE} and {@link #NODE_DATA_ORIGINAL_OPTIONALITY} are used
59   * to store the original scope and optionality of each node. Obviously, the resulting dependency tree is not suitable
60   * for artifact resolution unless a filter is employed to exclude the duplicate dependencies.
61   * <p>
62   * This transformer will query the keys {@link TransformationContextKeys#CONFLICT_IDS},
63   * {@link TransformationContextKeys#SORTED_CONFLICT_IDS}, {@link TransformationContextKeys#CYCLIC_CONFLICT_IDS} for
64   * existing information about conflict ids. In absence of this information, it will automatically invoke the
65   * {@link ConflictIdSorter} to calculate it.
66   */
67  public final class ConflictResolver implements DependencyGraphTransformer {
68  
69      /**
70       * The key in the repository session's {@link org.eclipse.aether.RepositorySystemSession#getConfigProperties()
71       * configuration properties} used to store a {@link Boolean} flag controlling the transformer's verbose mode.
72       * Accepted values are Boolean types, String type (where "true" would be interpreted as {@code true})
73       * or Verbosity enum instances.
74       *
75       * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
76       * @configurationType {@link java.lang.Object}
77       * @configurationDefaultValue "NONE"
78       */
79      public static final String CONFIG_PROP_VERBOSE = ConfigurationProperties.PREFIX_AETHER + "conflictResolver.verbose";
80  
81      /**
82       * The enum representing verbosity levels of conflict resolver.
83       *
84       * @since 1.9.8
85       */
86      public enum Verbosity {
87          /**
88           * Verbosity level to be used in all "common" resolving use cases (ie. dependencies to build class path). The
89           * {@link ConflictResolver} in this mode will trim down the graph to the barest minimum: will not leave
90           * any conflicting node in place, hence no conflicts will be present in transformed graph either.
91           */
92          NONE,
93  
94          /**
95           * Verbosity level to be used in "analyze" resolving use cases (ie. dependency convergence calculations). The
96           * {@link ConflictResolver} in this mode will remove any redundant collected nodes, in turn it will leave one
97           * with recorded conflicting information. This mode corresponds to "classic verbose" mode when
98           * {@link #CONFIG_PROP_VERBOSE} was set to {@code true}. Obviously, the resulting dependency tree is not
99           * suitable for artifact resolution unless a filter is employed to exclude the duplicate dependencies.
100          */
101         STANDARD,
102 
103         /**
104          * Verbosity level to be used in "analyze" resolving use cases (ie. dependency convergence calculations). The
105          * {@link ConflictResolver} in this mode will not remove any collected node, in turn it will record on all
106          * eliminated nodes the conflicting information. Obviously, the resulting dependency tree is not suitable
107          * for artifact resolution unless a filter is employed to exclude the duplicate dependencies.
108          */
109         FULL
110     }
111 
112     /**
113      * Helper method that uses {@link RepositorySystemSession} and {@link #CONFIG_PROP_VERBOSE} key to figure out
114      * current {@link Verbosity}: if {@link Boolean} or {@code String} found, returns {@link Verbosity#STANDARD}
115      * or {@link Verbosity#NONE}, depending on value (string is parsed with {@link Boolean#parseBoolean(String)}
116      * for {@code true} or {@code false} correspondingly. This is to retain "existing" behavior, where the config
117      * key accepted only these values.
118      * Since 1.9.8 release, this key may contain {@link Verbosity} enum instance as well, in which case that instance
119      * is returned.
120      * This method never returns {@code null}.
121      */
122     private static Verbosity getVerbosity(RepositorySystemSession session) {
123         final Object verbosityValue = session.getConfigProperties().get(CONFIG_PROP_VERBOSE);
124         if (verbosityValue instanceof Boolean) {
125             return (Boolean) verbosityValue ? Verbosity.STANDARD : Verbosity.NONE;
126         } else if (verbosityValue instanceof String) {
127             return Boolean.parseBoolean(verbosityValue.toString()) ? Verbosity.STANDARD : Verbosity.NONE;
128         } else if (verbosityValue instanceof Verbosity) {
129             return (Verbosity) verbosityValue;
130         } else if (verbosityValue != null) {
131             throw new IllegalArgumentException("Unsupported Verbosity configuration: " + verbosityValue);
132         }
133         return Verbosity.NONE;
134     }
135 
136     /**
137      * The key in the dependency node's {@link DependencyNode#getData() custom data} under which a reference to the
138      * {@link DependencyNode} which has won the conflict is stored.
139      */
140     public static final String NODE_DATA_WINNER = "conflict.winner";
141 
142     /**
143      * The key in the dependency node's {@link DependencyNode#getData() custom data} under which the scope of the
144      * dependency before scope derivation and conflict resolution is stored.
145      */
146     public static final String NODE_DATA_ORIGINAL_SCOPE = "conflict.originalScope";
147 
148     /**
149      * The key in the dependency node's {@link DependencyNode#getData() custom data} under which the optional flag of
150      * the dependency before derivation and conflict resolution is stored.
151      */
152     public static final String NODE_DATA_ORIGINAL_OPTIONALITY = "conflict.originalOptionality";
153 
154     private final VersionSelector versionSelector;
155 
156     private final ScopeSelector scopeSelector;
157 
158     private final ScopeDeriver scopeDeriver;
159 
160     private final OptionalitySelector optionalitySelector;
161 
162     /**
163      * Creates a new conflict resolver instance with the specified hooks.
164      *
165      * @param versionSelector The version selector to use, must not be {@code null}.
166      * @param scopeSelector The scope selector to use, must not be {@code null}.
167      * @param optionalitySelector The optionality selector ot use, must not be {@code null}.
168      * @param scopeDeriver The scope deriver to use, must not be {@code null}.
169      */
170     public ConflictResolver(
171             VersionSelector versionSelector,
172             ScopeSelector scopeSelector,
173             OptionalitySelector optionalitySelector,
174             ScopeDeriver scopeDeriver) {
175         this.versionSelector = requireNonNull(versionSelector, "version selector cannot be null");
176         this.scopeSelector = requireNonNull(scopeSelector, "scope selector cannot be null");
177         this.optionalitySelector = requireNonNull(optionalitySelector, "optionality selector cannot be null");
178         this.scopeDeriver = requireNonNull(scopeDeriver, "scope deriver cannot be null");
179     }
180 
181     public DependencyNode transformGraph(DependencyNode node, DependencyGraphTransformationContext context)
182             throws RepositoryException {
183         requireNonNull(node, "node cannot be null");
184         requireNonNull(context, "context cannot be null");
185         List<?> sortedConflictIds = (List<?>) context.get(TransformationContextKeys.SORTED_CONFLICT_IDS);
186         if (sortedConflictIds == null) {
187             ConflictIdSorter sorter = new ConflictIdSorter();
188             sorter.transformGraph(node, context);
189 
190             sortedConflictIds = (List<?>) context.get(TransformationContextKeys.SORTED_CONFLICT_IDS);
191         }
192 
193         @SuppressWarnings("unchecked")
194         Map<String, Object> stats = (Map<String, Object>) context.get(TransformationContextKeys.STATS);
195         long time1 = System.nanoTime();
196 
197         @SuppressWarnings("unchecked")
198         Collection<Collection<?>> conflictIdCycles =
199                 (Collection<Collection<?>>) context.get(TransformationContextKeys.CYCLIC_CONFLICT_IDS);
200         if (conflictIdCycles == null) {
201             throw new RepositoryException("conflict id cycles have not been identified");
202         }
203 
204         Map<?, ?> conflictIds = (Map<?, ?>) context.get(TransformationContextKeys.CONFLICT_IDS);
205         if (conflictIds == null) {
206             throw new RepositoryException("conflict groups have not been identified");
207         }
208 
209         Map<Object, Collection<Object>> cyclicPredecessors = new HashMap<>();
210         for (Collection<?> cycle : conflictIdCycles) {
211             for (Object conflictId : cycle) {
212                 Collection<Object> predecessors = cyclicPredecessors.computeIfAbsent(conflictId, k -> new HashSet<>());
213                 predecessors.addAll(cycle);
214             }
215         }
216 
217         State state = new State(node, conflictIds, sortedConflictIds.size(), context);
218         for (Iterator<?> it = sortedConflictIds.iterator(); it.hasNext(); ) {
219             Object conflictId = it.next();
220 
221             // reset data structures for next graph walk
222             state.prepare(conflictId, cyclicPredecessors.get(conflictId));
223 
224             // find nodes with the current conflict id and while walking the graph (more deeply), nuke leftover losers
225             gatherConflictItems(node, state);
226 
227             // now that we know the min depth of the parents, update depth of conflict items
228             state.finish();
229 
230             // earlier runs might have nuked all parents of the current conflict id, so it might not exist anymore
231             if (!state.items.isEmpty()) {
232                 ConflictContext ctx = state.conflictCtx;
233                 state.versionSelector.selectVersion(ctx);
234                 if (ctx.winner == null) {
235                     throw new RepositoryException("conflict resolver did not select winner among " + state.items);
236                 }
237                 DependencyNode winner = ctx.winner.node;
238 
239                 state.scopeSelector.selectScope(ctx);
240                 if (Verbosity.NONE != state.verbosity) {
241                     winner.setData(
242                             NODE_DATA_ORIGINAL_SCOPE, winner.getDependency().getScope());
243                 }
244                 winner.setScope(ctx.scope);
245 
246                 state.optionalitySelector.selectOptionality(ctx);
247                 if (Verbosity.NONE != state.verbosity) {
248                     winner.setData(
249                             NODE_DATA_ORIGINAL_OPTIONALITY,
250                             winner.getDependency().isOptional());
251                 }
252                 winner.setOptional(ctx.optional);
253 
254                 removeLosers(state);
255             }
256 
257             // record the winner so we can detect leftover losers during future graph walks
258             state.winner();
259 
260             // in case of cycles, trigger final graph walk to ensure all leftover losers are gone
261             if (!it.hasNext() && !conflictIdCycles.isEmpty() && state.conflictCtx.winner != null) {
262                 DependencyNode winner = state.conflictCtx.winner.node;
263                 state.prepare(state, null);
264                 gatherConflictItems(winner, state);
265             }
266         }
267 
268         if (stats != null) {
269             long time2 = System.nanoTime();
270             stats.put("ConflictResolver.totalTime", time2 - time1);
271             stats.put("ConflictResolver.conflictItemCount", state.totalConflictItems);
272         }
273 
274         return node;
275     }
276 
277     private boolean gatherConflictItems(DependencyNode node, State state) throws RepositoryException {
278         Object conflictId = state.conflictIds.get(node);
279         if (state.currentId.equals(conflictId)) {
280             // found it, add conflict item (if not already done earlier by another path)
281             state.add(node);
282             // we don't recurse here so we might miss losers beneath us, those will be nuked during future walks below
283         } else if (state.loser(node, conflictId)) {
284             // found a leftover loser (likely in a cycle) of an already processed conflict id, tell caller to nuke it
285             return false;
286         } else if (state.push(node, conflictId)) {
287             // found potential parent, no cycle and not visisted before with the same derived scope, so recurse
288             for (Iterator<DependencyNode> it = node.getChildren().iterator(); it.hasNext(); ) {
289                 DependencyNode child = it.next();
290                 if (!gatherConflictItems(child, state)) {
291                     it.remove();
292                 }
293             }
294             state.pop();
295         }
296         return true;
297     }
298 
299     private static void removeLosers(State state) {
300         ConflictItem winner = state.conflictCtx.winner;
301         String winnerArtifactId = ArtifactIdUtils.toId(winner.node.getArtifact());
302         List<DependencyNode> previousParent = null;
303         ListIterator<DependencyNode> childIt = null;
304         HashSet<String> toRemoveIds = new HashSet<>();
305         for (ConflictItem item : state.items) {
306             if (item == winner) {
307                 continue;
308             }
309             if (item.parent != previousParent) {
310                 childIt = item.parent.listIterator();
311                 previousParent = item.parent;
312             }
313             while (childIt.hasNext()) {
314                 DependencyNode child = childIt.next();
315                 if (child == item.node) {
316                     // NONE: just remove it and done
317                     if (Verbosity.NONE == state.verbosity) {
318                         childIt.remove();
319                         break;
320                     }
321 
322                     // STANDARD: doing extra bookkeeping to select "which nodes to remove"
323                     if (Verbosity.STANDARD == state.verbosity) {
324                         String childArtifactId = ArtifactIdUtils.toId(child.getArtifact());
325                         // if two IDs are equal, it means "there is nearest", not conflict per se.
326                         // In that case we do NOT allow this child to be removed (but remove others)
327                         // and this keeps us safe from iteration (and in general, version) ordering
328                         // as we explicitly leave out ID that is "nearest found" state.
329                         //
330                         // This tackles version ranges mostly, where ranges are turned into list of
331                         // several nodes in collector (as many were discovered, ie. from metadata), and
332                         // old code would just "mark" the first hit as conflict, and remove the rest,
333                         // even if rest could contain "more suitable" version, that is not conflicting/diverging.
334                         // This resulted in verbose mode transformed tree, that was misrepresenting things
335                         // for dependency convergence calculations: it represented state like parent node
336                         // depends on "wrong" version (diverge), while "right" version was present (but removed)
337                         // as well, as it was contained in parents version range.
338                         if (!Objects.equals(winnerArtifactId, childArtifactId)) {
339                             toRemoveIds.add(childArtifactId);
340                         }
341                     }
342 
343                     // FULL: just record the facts
344                     DependencyNode loser = new DefaultDependencyNode(child);
345                     loser.setData(NODE_DATA_WINNER, winner.node);
346                     loser.setData(
347                             NODE_DATA_ORIGINAL_SCOPE, loser.getDependency().getScope());
348                     loser.setData(
349                             NODE_DATA_ORIGINAL_OPTIONALITY,
350                             loser.getDependency().isOptional());
351                     loser.setScope(item.getScopes().iterator().next());
352                     loser.setChildren(Collections.emptyList());
353                     childIt.set(loser);
354                     item.node = loser;
355                     break;
356                 }
357             }
358         }
359 
360         // 2nd pass to apply "standard" verbosity: leaving only 1 loser, but with care
361         if (Verbosity.STANDARD == state.verbosity && !toRemoveIds.isEmpty()) {
362             previousParent = null;
363             for (ConflictItem item : state.items) {
364                 if (item == winner) {
365                     continue;
366                 }
367                 if (item.parent != previousParent) {
368                     childIt = item.parent.listIterator();
369                     previousParent = item.parent;
370                 }
371                 while (childIt.hasNext()) {
372                     DependencyNode child = childIt.next();
373                     if (child == item.node) {
374                         String childArtifactId = ArtifactIdUtils.toId(child.getArtifact());
375                         if (toRemoveIds.contains(childArtifactId)
376                                 && relatedSiblingsCount(child.getArtifact(), item.parent) > 1) {
377                             childIt.remove();
378                         }
379                         break;
380                     }
381                 }
382             }
383         }
384 
385         // there might still be losers beneath the winner (e.g. in case of cycles)
386         // those will be nuked during future graph walks when we include the winner in the recursion
387     }
388 
389     private static long relatedSiblingsCount(Artifact artifact, List<DependencyNode> parent) {
390         String ga = artifact.getGroupId() + ":" + artifact.getArtifactId();
391         return parent.stream()
392                 .map(DependencyNode::getArtifact)
393                 .filter(a -> ga.equals(a.getGroupId() + ":" + a.getArtifactId()))
394                 .count();
395     }
396 
397     static final class NodeInfo {
398 
399         /**
400          * The smallest depth at which the node was seen, used for "the" depth of its conflict items.
401          */
402         int minDepth;
403 
404         /**
405          * The set of derived scopes the node was visited with, used to check whether an already seen node needs to be
406          * revisited again in context of another scope. To conserve memory, we start with {@code String} and update to
407          * {@code Set<String>} if needed.
408          */
409         Object derivedScopes;
410 
411         /**
412          * The set of derived optionalities the node was visited with, used to check whether an already seen node needs
413          * to be revisited again in context of another optionality. To conserve memory, encoded as bit field (bit 0 ->
414          * optional=false, bit 1 -> optional=true).
415          */
416         int derivedOptionalities;
417 
418         /**
419          * The conflict items which are immediate children of the node, used to easily update those conflict items after
420          * a new parent scope/optionality was encountered.
421          */
422         List<ConflictItem> children;
423 
424         static final int CHANGE_SCOPE = 0x01;
425 
426         static final int CHANGE_OPTIONAL = 0x02;
427 
428         private static final int OPT_FALSE = 0x01;
429 
430         private static final int OPT_TRUE = 0x02;
431 
432         NodeInfo(int depth, String derivedScope, boolean optional) {
433             minDepth = depth;
434             derivedScopes = derivedScope;
435             derivedOptionalities = optional ? OPT_TRUE : OPT_FALSE;
436         }
437 
438         @SuppressWarnings("unchecked")
439         int update(int depth, String derivedScope, boolean optional) {
440             if (depth < minDepth) {
441                 minDepth = depth;
442             }
443             int changes;
444             if (derivedScopes.equals(derivedScope)) {
445                 changes = 0;
446             } else if (derivedScopes instanceof Collection) {
447                 changes = ((Collection<String>) derivedScopes).add(derivedScope) ? CHANGE_SCOPE : 0;
448             } else {
449                 Collection<String> scopes = new HashSet<>();
450                 scopes.add((String) derivedScopes);
451                 scopes.add(derivedScope);
452                 derivedScopes = scopes;
453                 changes = CHANGE_SCOPE;
454             }
455             int bit = optional ? OPT_TRUE : OPT_FALSE;
456             if ((derivedOptionalities & bit) == 0) {
457                 derivedOptionalities |= bit;
458                 changes |= CHANGE_OPTIONAL;
459             }
460             return changes;
461         }
462 
463         void add(ConflictItem item) {
464             if (children == null) {
465                 children = new ArrayList<>(1);
466             }
467             children.add(item);
468         }
469     }
470 
471     final class State {
472 
473         /**
474          * The conflict id currently processed.
475          */
476         Object currentId;
477 
478         /**
479          * Stats counter.
480          */
481         int totalConflictItems;
482 
483         /**
484          * Flag whether we should keep losers in the graph to enable visualization/troubleshooting of conflicts.
485          */
486         final Verbosity verbosity;
487 
488         /**
489          * A mapping from conflict id to winner node, helps to recognize nodes that have their effective
490          * scope&optionality set or are leftovers from previous removals.
491          */
492         final Map<Object, DependencyNode> resolvedIds;
493 
494         /**
495          * The set of conflict ids which could apply to ancestors of nodes with the current conflict id, used to avoid
496          * recursion early on. This is basically a superset of the key set of resolvedIds, the additional ids account
497          * for cyclic dependencies.
498          */
499         final Collection<Object> potentialAncestorIds;
500 
501         /**
502          * The output from the conflict marker
503          */
504         final Map<?, ?> conflictIds;
505 
506         /**
507          * The conflict items we have gathered so far for the current conflict id.
508          */
509         final List<ConflictItem> items;
510 
511         /**
512          * The (conceptual) mapping from nodes to extra infos, technically keyed by the node's child list which better
513          * captures the identity of a node since we're basically concerned with effects towards children.
514          */
515         final Map<List<DependencyNode>, NodeInfo> infos;
516 
517         /**
518          * The set of nodes on the DFS stack to detect cycles, technically keyed by the node's child list to match the
519          * dirty graph structure produced by the dependency collector for cycles.
520          */
521         final Map<List<DependencyNode>, Object> stack;
522 
523         /**
524          * The stack of parent nodes.
525          */
526         final List<DependencyNode> parentNodes;
527 
528         /**
529          * The stack of derived scopes for parent nodes.
530          */
531         final List<String> parentScopes;
532 
533         /**
534          * The stack of derived optional flags for parent nodes.
535          */
536         final List<Boolean> parentOptionals;
537 
538         /**
539          * The stack of node infos for parent nodes, may contain {@code null} which is used to disable creating new
540          * conflict items when visiting their parent again (conflict items are meant to be unique by parent-node combo).
541          */
542         final List<NodeInfo> parentInfos;
543 
544         /**
545          * The conflict context passed to the version/scope/optionality selectors, updated as we move along rather than
546          * recreated to avoid tmp objects.
547          */
548         final ConflictContext conflictCtx;
549 
550         /**
551          * The scope context passed to the scope deriver, updated as we move along rather than recreated to avoid tmp
552          * objects.
553          */
554         final ScopeContext scopeCtx;
555 
556         /**
557          * The effective version selector, i.e. after initialization.
558          */
559         final VersionSelector versionSelector;
560 
561         /**
562          * The effective scope selector, i.e. after initialization.
563          */
564         final ScopeSelector scopeSelector;
565 
566         /**
567          * The effective scope deriver, i.e. after initialization.
568          */
569         final ScopeDeriver scopeDeriver;
570 
571         /**
572          * The effective optionality selector, i.e. after initialization.
573          */
574         final OptionalitySelector optionalitySelector;
575 
576         State(
577                 DependencyNode root,
578                 Map<?, ?> conflictIds,
579                 int conflictIdCount,
580                 DependencyGraphTransformationContext context)
581                 throws RepositoryException {
582             this.conflictIds = conflictIds;
583             this.verbosity = getVerbosity(context.getSession());
584             potentialAncestorIds = new HashSet<>(conflictIdCount * 2);
585             resolvedIds = new HashMap<>(conflictIdCount * 2);
586             items = new ArrayList<>(256);
587             infos = new IdentityHashMap<>(64);
588             stack = new IdentityHashMap<>(64);
589             parentNodes = new ArrayList<>(64);
590             parentScopes = new ArrayList<>(64);
591             parentOptionals = new ArrayList<>(64);
592             parentInfos = new ArrayList<>(64);
593             conflictCtx = new ConflictContext(root, conflictIds, items);
594             scopeCtx = new ScopeContext(null, null);
595             versionSelector = ConflictResolver.this.versionSelector.getInstance(root, context);
596             scopeSelector = ConflictResolver.this.scopeSelector.getInstance(root, context);
597             scopeDeriver = ConflictResolver.this.scopeDeriver.getInstance(root, context);
598             optionalitySelector = ConflictResolver.this.optionalitySelector.getInstance(root, context);
599         }
600 
601         void prepare(Object conflictId, Collection<Object> cyclicPredecessors) {
602             currentId = conflictId;
603             conflictCtx.conflictId = conflictId;
604             conflictCtx.winner = null;
605             conflictCtx.scope = null;
606             conflictCtx.optional = null;
607             items.clear();
608             infos.clear();
609             if (cyclicPredecessors != null) {
610                 potentialAncestorIds.addAll(cyclicPredecessors);
611             }
612         }
613 
614         void finish() {
615             List<DependencyNode> previousParent = null;
616             int previousDepth = 0;
617             totalConflictItems += items.size();
618             for (ListIterator<ConflictItem> iterator = items.listIterator(items.size()); iterator.hasPrevious(); ) {
619                 ConflictItem item = iterator.previous();
620                 if (item.parent == previousParent) {
621                     item.depth = previousDepth;
622                 } else if (item.parent != null) {
623                     previousParent = item.parent;
624                     NodeInfo info = infos.get(previousParent);
625                     previousDepth = info.minDepth + 1;
626                     item.depth = previousDepth;
627                 }
628             }
629             potentialAncestorIds.add(currentId);
630         }
631 
632         void winner() {
633             resolvedIds.put(currentId, (conflictCtx.winner != null) ? conflictCtx.winner.node : null);
634         }
635 
636         boolean loser(DependencyNode node, Object conflictId) {
637             DependencyNode winner = resolvedIds.get(conflictId);
638             return winner != null && winner != node;
639         }
640 
641         boolean push(DependencyNode node, Object conflictId) throws RepositoryException {
642             if (conflictId == null) {
643                 if (node.getDependency() != null) {
644                     if (node.getData().get(NODE_DATA_WINNER) != null) {
645                         return false;
646                     }
647                     throw new RepositoryException("missing conflict id for node " + node);
648                 }
649             } else if (!potentialAncestorIds.contains(conflictId)) {
650                 return false;
651             }
652 
653             List<DependencyNode> graphNode = node.getChildren();
654             if (stack.put(graphNode, Boolean.TRUE) != null) {
655                 return false;
656             }
657 
658             int depth = depth();
659             String scope = deriveScope(node, conflictId);
660             boolean optional = deriveOptional(node, conflictId);
661             NodeInfo info = infos.get(graphNode);
662             if (info == null) {
663                 info = new NodeInfo(depth, scope, optional);
664                 infos.put(graphNode, info);
665                 parentInfos.add(info);
666                 parentNodes.add(node);
667                 parentScopes.add(scope);
668                 parentOptionals.add(optional);
669             } else {
670                 int changes = info.update(depth, scope, optional);
671                 if (changes == 0) {
672                     stack.remove(graphNode);
673                     return false;
674                 }
675                 parentInfos.add(null); // disable creating new conflict items, we update the existing ones below
676                 parentNodes.add(node);
677                 parentScopes.add(scope);
678                 parentOptionals.add(optional);
679                 if (info.children != null) {
680                     if ((changes & NodeInfo.CHANGE_SCOPE) != 0) {
681                         ListIterator<ConflictItem> itemIterator = info.children.listIterator(info.children.size());
682                         while (itemIterator.hasPrevious()) {
683                             ConflictItem item = itemIterator.previous();
684                             String childScope = deriveScope(item.node, null);
685                             item.addScope(childScope);
686                         }
687                     }
688                     if ((changes & NodeInfo.CHANGE_OPTIONAL) != 0) {
689                         ListIterator<ConflictItem> itemIterator = info.children.listIterator(info.children.size());
690                         while (itemIterator.hasPrevious()) {
691                             ConflictItem item = itemIterator.previous();
692                             boolean childOptional = deriveOptional(item.node, null);
693                             item.addOptional(childOptional);
694                         }
695                     }
696                 }
697             }
698 
699             return true;
700         }
701 
702         void pop() {
703             int last = parentInfos.size() - 1;
704             parentInfos.remove(last);
705             parentScopes.remove(last);
706             parentOptionals.remove(last);
707             DependencyNode node = parentNodes.remove(last);
708             stack.remove(node.getChildren());
709         }
710 
711         void add(DependencyNode node) throws RepositoryException {
712             DependencyNode parent = parent();
713             if (parent == null) {
714                 ConflictItem item = newConflictItem(parent, node);
715                 items.add(item);
716             } else {
717                 NodeInfo info = parentInfos.get(parentInfos.size() - 1);
718                 if (info != null) {
719                     ConflictItem item = newConflictItem(parent, node);
720                     info.add(item);
721                     items.add(item);
722                 }
723             }
724         }
725 
726         private ConflictItem newConflictItem(DependencyNode parent, DependencyNode node) throws RepositoryException {
727             return new ConflictItem(parent, node, deriveScope(node, null), deriveOptional(node, null));
728         }
729 
730         private int depth() {
731             return parentNodes.size();
732         }
733 
734         private DependencyNode parent() {
735             int size = parentNodes.size();
736             return (size <= 0) ? null : parentNodes.get(size - 1);
737         }
738 
739         private String deriveScope(DependencyNode node, Object conflictId) throws RepositoryException {
740             if ((node.getManagedBits() & DependencyNode.MANAGED_SCOPE) != 0
741                     || (conflictId != null && resolvedIds.containsKey(conflictId))) {
742                 return scope(node.getDependency());
743             }
744 
745             int depth = parentNodes.size();
746             scopes(depth, node.getDependency());
747             if (depth > 0) {
748                 scopeDeriver.deriveScope(scopeCtx);
749             }
750             return scopeCtx.derivedScope;
751         }
752 
753         private void scopes(int parent, Dependency child) {
754             scopeCtx.parentScope = (parent > 0) ? parentScopes.get(parent - 1) : null;
755             scopeCtx.derivedScope = scope(child);
756             scopeCtx.childScope = scope(child);
757         }
758 
759         private String scope(Dependency dependency) {
760             return (dependency != null) ? dependency.getScope() : null;
761         }
762 
763         private boolean deriveOptional(DependencyNode node, Object conflictId) {
764             Dependency dep = node.getDependency();
765             boolean optional = (dep != null) && dep.isOptional();
766             if (optional
767                     || (node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL) != 0
768                     || (conflictId != null && resolvedIds.containsKey(conflictId))) {
769                 return optional;
770             }
771             int depth = parentNodes.size();
772             return (depth > 0) ? parentOptionals.get(depth - 1) : false;
773         }
774     }
775 
776     /**
777      * A context used to hold information that is relevant for deriving the scope of a child dependency.
778      *
779      * @see ScopeDeriver
780      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
781      *                change without notice and only exists to enable unit testing.
782      */
783     public static final class ScopeContext {
784 
785         String parentScope;
786 
787         String childScope;
788 
789         String derivedScope;
790 
791         /**
792          * Creates a new scope context with the specified properties.
793          *
794          * @param parentScope The scope of the parent dependency, may be {@code null}.
795          * @param childScope The scope of the child dependency, may be {@code null}.
796          * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
797          *              change without notice and only exists to enable unit testing.
798          */
799         public ScopeContext(String parentScope, String childScope) {
800             this.parentScope = (parentScope != null) ? parentScope : "";
801             derivedScope = (childScope != null) ? childScope : "";
802             this.childScope = (childScope != null) ? childScope : "";
803         }
804 
805         /**
806          * Gets the scope of the parent dependency. This is usually the scope that was derived by earlier invocations of
807          * the scope deriver.
808          *
809          * @return The scope of the parent dependency, never {@code null}.
810          */
811         public String getParentScope() {
812             return parentScope;
813         }
814 
815         /**
816          * Gets the original scope of the child dependency. This is the scope that was declared in the artifact
817          * descriptor of the parent dependency.
818          *
819          * @return The original scope of the child dependency, never {@code null}.
820          */
821         public String getChildScope() {
822             return childScope;
823         }
824 
825         /**
826          * Gets the derived scope of the child dependency. This is initially equal to {@link #getChildScope()} until the
827          * scope deriver makes changes.
828          *
829          * @return The derived scope of the child dependency, never {@code null}.
830          */
831         public String getDerivedScope() {
832             return derivedScope;
833         }
834 
835         /**
836          * Sets the derived scope of the child dependency.
837          *
838          * @param derivedScope The derived scope of the dependency, may be {@code null}.
839          */
840         public void setDerivedScope(String derivedScope) {
841             this.derivedScope = (derivedScope != null) ? derivedScope : "";
842         }
843     }
844 
845     /**
846      * A conflicting dependency.
847      *
848      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
849      *                change without notice and only exists to enable unit testing.
850      */
851     public static final class ConflictItem {
852 
853         // nodes can share child lists, we care about the unique owner of a child node which is the child list
854         final List<DependencyNode> parent;
855 
856         // only for debugging/toString() to help identify the parent node(s)
857         final Artifact artifact;
858 
859         // is mutable as removeLosers will mutate it (if Verbosity==STANDARD)
860         DependencyNode node;
861 
862         int depth;
863 
864         // we start with String and update to Set<String> if needed
865         Object scopes;
866 
867         // bit field of OPTIONAL_FALSE and OPTIONAL_TRUE
868         int optionalities;
869 
870         /**
871          * Bit flag indicating whether one or more paths consider the dependency non-optional.
872          */
873         public static final int OPTIONAL_FALSE = 0x01;
874 
875         /**
876          * Bit flag indicating whether one or more paths consider the dependency optional.
877          */
878         public static final int OPTIONAL_TRUE = 0x02;
879 
880         ConflictItem(DependencyNode parent, DependencyNode node, String scope, boolean optional) {
881             if (parent != null) {
882                 this.parent = parent.getChildren();
883                 this.artifact = parent.getArtifact();
884             } else {
885                 this.parent = null;
886                 this.artifact = null;
887             }
888             this.node = node;
889             this.scopes = scope;
890             this.optionalities = optional ? OPTIONAL_TRUE : OPTIONAL_FALSE;
891         }
892 
893         /**
894          * Creates a new conflict item with the specified properties.
895          *
896          * @param parent The parent node of the conflicting dependency, may be {@code null}.
897          * @param node The conflicting dependency, must not be {@code null}.
898          * @param depth The zero-based depth of the conflicting dependency.
899          * @param optionalities The optionalities the dependency was encountered with, encoded as a bit field consisting
900          *            of {@link ConflictResolver.ConflictItem#OPTIONAL_TRUE} and
901          *            {@link ConflictResolver.ConflictItem#OPTIONAL_FALSE}.
902          * @param scopes The derived scopes of the conflicting dependency, must not be {@code null}.
903          * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
904          *              change without notice and only exists to enable unit testing.
905          */
906         public ConflictItem(
907                 DependencyNode parent, DependencyNode node, int depth, int optionalities, String... scopes) {
908             this.parent = (parent != null) ? parent.getChildren() : null;
909             this.artifact = (parent != null) ? parent.getArtifact() : null;
910             this.node = node;
911             this.depth = depth;
912             this.optionalities = optionalities;
913             this.scopes = Arrays.asList(scopes);
914         }
915 
916         /**
917          * Determines whether the specified conflict item is a sibling of this item.
918          *
919          * @param item The other conflict item, must not be {@code null}.
920          * @return {@code true} if the given item has the same parent as this item, {@code false} otherwise.
921          */
922         public boolean isSibling(ConflictItem item) {
923             return parent == item.parent;
924         }
925 
926         /**
927          * Gets the dependency node involved in the conflict.
928          *
929          * @return The involved dependency node, never {@code null}.
930          */
931         public DependencyNode getNode() {
932             return node;
933         }
934 
935         /**
936          * Gets the dependency involved in the conflict, short for {@code getNode.getDependency()}.
937          *
938          * @return The involved dependency, never {@code null}.
939          */
940         public Dependency getDependency() {
941             return node.getDependency();
942         }
943 
944         /**
945          * Gets the zero-based depth at which the conflicting node occurs in the graph. As such, the depth denotes the
946          * number of parent nodes. If actually multiple paths lead to the node, the return value denotes the smallest
947          * possible depth.
948          *
949          * @return The zero-based depth of the node in the graph.
950          */
951         public int getDepth() {
952             return depth;
953         }
954 
955         /**
956          * Gets the derived scopes of the dependency. In general, the same dependency node could be reached via
957          * different paths and each path might result in a different derived scope.
958          *
959          * @see ScopeDeriver
960          * @return The (read-only) set of derived scopes of the dependency, never {@code null}.
961          */
962         @SuppressWarnings("unchecked")
963         public Collection<String> getScopes() {
964             if (scopes instanceof String) {
965                 return Collections.singleton((String) scopes);
966             }
967             return (Collection<String>) scopes;
968         }
969 
970         @SuppressWarnings("unchecked")
971         void addScope(String scope) {
972             if (scopes instanceof Collection) {
973                 ((Collection<String>) scopes).add(scope);
974             } else if (!scopes.equals(scope)) {
975                 Collection<Object> set = new HashSet<>();
976                 set.add(scopes);
977                 set.add(scope);
978                 scopes = set;
979             }
980         }
981 
982         /**
983          * Gets the derived optionalities of the dependency. In general, the same dependency node could be reached via
984          * different paths and each path might result in a different derived optionality.
985          *
986          * @return A bit field consisting of {@link ConflictResolver.ConflictItem#OPTIONAL_FALSE} and/or
987          *         {@link ConflictResolver.ConflictItem#OPTIONAL_TRUE} indicating the derived optionalities the
988          *         dependency was encountered with.
989          */
990         public int getOptionalities() {
991             return optionalities;
992         }
993 
994         void addOptional(boolean optional) {
995             optionalities |= optional ? OPTIONAL_TRUE : OPTIONAL_FALSE;
996         }
997 
998         @Override
999         public String toString() {
1000             return node + " @ " + depth + " < " + artifact;
1001         }
1002     }
1003 
1004     /**
1005      * A context used to hold information that is relevant for resolving version and scope conflicts.
1006      *
1007      * @see VersionSelector
1008      * @see ScopeSelector
1009      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
1010      *                change without notice and only exists to enable unit testing.
1011      */
1012     public static final class ConflictContext {
1013 
1014         final DependencyNode root;
1015 
1016         final Map<?, ?> conflictIds;
1017 
1018         final Collection<ConflictItem> items;
1019 
1020         Object conflictId;
1021 
1022         ConflictItem winner;
1023 
1024         String scope;
1025 
1026         Boolean optional;
1027 
1028         ConflictContext(DependencyNode root, Map<?, ?> conflictIds, Collection<ConflictItem> items) {
1029             this.root = root;
1030             this.conflictIds = conflictIds;
1031             this.items = Collections.unmodifiableCollection(items);
1032         }
1033 
1034         /**
1035          * Creates a new conflict context.
1036          *
1037          * @param root The root node of the dependency graph, must not be {@code null}.
1038          * @param conflictId The conflict id for the set of conflicting dependencies in this context, must not be
1039          *            {@code null}.
1040          * @param conflictIds The mapping from dependency node to conflict id, must not be {@code null}.
1041          * @param items The conflict items in this context, must not be {@code null}.
1042          * @noreference This class is not intended to be instantiated by clients in production code, the constructor may
1043          *              change without notice and only exists to enable unit testing.
1044          */
1045         public ConflictContext(
1046                 DependencyNode root,
1047                 Object conflictId,
1048                 Map<DependencyNode, Object> conflictIds,
1049                 Collection<ConflictItem> items) {
1050             this(root, conflictIds, items);
1051             this.conflictId = conflictId;
1052         }
1053 
1054         /**
1055          * Gets the root node of the dependency graph being transformed.
1056          *
1057          * @return The root node of the dependeny graph, never {@code null}.
1058          */
1059         public DependencyNode getRoot() {
1060             return root;
1061         }
1062 
1063         /**
1064          * Determines whether the specified dependency node belongs to this conflict context.
1065          *
1066          * @param node The dependency node to check, must not be {@code null}.
1067          * @return {@code true} if the given node belongs to this conflict context, {@code false} otherwise.
1068          */
1069         public boolean isIncluded(DependencyNode node) {
1070             return conflictId.equals(conflictIds.get(node));
1071         }
1072 
1073         /**
1074          * Gets the collection of conflict items in this context.
1075          *
1076          * @return The (read-only) collection of conflict items in this context, never {@code null}.
1077          */
1078         public Collection<ConflictItem> getItems() {
1079             return items;
1080         }
1081 
1082         /**
1083          * Gets the conflict item which has been selected as the winner among the conflicting dependencies.
1084          *
1085          * @return The winning conflict item or {@code null} if not set yet.
1086          */
1087         public ConflictItem getWinner() {
1088             return winner;
1089         }
1090 
1091         /**
1092          * Sets the conflict item which has been selected as the winner among the conflicting dependencies.
1093          *
1094          * @param winner The winning conflict item, may be {@code null}.
1095          */
1096         public void setWinner(ConflictItem winner) {
1097             this.winner = winner;
1098         }
1099 
1100         /**
1101          * Gets the effective scope of the winning dependency.
1102          *
1103          * @return The effective scope of the winning dependency or {@code null} if none.
1104          */
1105         public String getScope() {
1106             return scope;
1107         }
1108 
1109         /**
1110          * Sets the effective scope of the winning dependency.
1111          *
1112          * @param scope The effective scope, may be {@code null}.
1113          */
1114         public void setScope(String scope) {
1115             this.scope = scope;
1116         }
1117 
1118         /**
1119          * Gets the effective optional flag of the winning dependency.
1120          *
1121          * @return The effective optional flag or {@code null} if none.
1122          */
1123         public Boolean getOptional() {
1124             return optional;
1125         }
1126 
1127         /**
1128          * Sets the effective optional flag of the winning dependency.
1129          *
1130          * @param optional The effective optional flag, may be {@code null}.
1131          */
1132         public void setOptional(Boolean optional) {
1133             this.optional = optional;
1134         }
1135 
1136         @Override
1137         public String toString() {
1138             return winner + " @ " + scope + " < " + items;
1139         }
1140     }
1141 
1142     /**
1143      * An extension point of {@link ConflictResolver} that determines the winner among conflicting dependencies. The
1144      * winning node (and its children) will be retained in the dependency graph, the other nodes will get removed. The
1145      * version selector does not need to deal with potential scope conflicts, these will be addressed afterwards by the
1146      * {@link ScopeSelector}.
1147      * <p>
1148      * <strong>Note:</strong> Implementations must be stateless.
1149      */
1150     public abstract static class VersionSelector {
1151 
1152         /**
1153          * Retrieves the version selector for use during the specified graph transformation. The conflict resolver calls
1154          * this method once per
1155          * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
1156          * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
1157          * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
1158          * default implementation simply returns the current instance which is appropriate for implementations which do
1159          * not require auxiliary data.
1160          *
1161          * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
1162          * @param context The graph transformation context, must not be {@code null}.
1163          * @return The scope deriver to use for the given graph transformation, never {@code null}.
1164          * @throws RepositoryException If the instance could not be retrieved.
1165          */
1166         public VersionSelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
1167                 throws RepositoryException {
1168             return this;
1169         }
1170 
1171         /**
1172          * Determines the winning node among conflicting dependencies. Implementations will usually iterate
1173          * {@link ConflictContext#getItems()}, inspect {@link ConflictItem#getNode()} and eventually call
1174          * {@link ConflictContext#setWinner(ConflictResolver.ConflictItem)} to deliver the winner. Failure to select a
1175          * winner will automatically fail the entire conflict resolution.
1176          *
1177          * @param context The conflict context, must not be {@code null}.
1178          * @throws RepositoryException If the version selection failed.
1179          */
1180         public abstract void selectVersion(ConflictContext context) throws RepositoryException;
1181     }
1182 
1183     /**
1184      * An extension point of {@link ConflictResolver} that determines the effective scope of a dependency from a
1185      * potentially conflicting set of {@link ScopeDeriver derived scopes}. The scope selector gets invoked after the
1186      * {@link VersionSelector} has picked the winning node.
1187      * <p>
1188      * <strong>Note:</strong> Implementations must be stateless.
1189      */
1190     public abstract static class ScopeSelector {
1191 
1192         /**
1193          * Retrieves the scope selector for use during the specified graph transformation. The conflict resolver calls
1194          * this method once per
1195          * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
1196          * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
1197          * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
1198          * default implementation simply returns the current instance which is appropriate for implementations which do
1199          * not require auxiliary data.
1200          *
1201          * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
1202          * @param context The graph transformation context, must not be {@code null}.
1203          * @return The scope selector to use for the given graph transformation, never {@code null}.
1204          * @throws RepositoryException If the instance could not be retrieved.
1205          */
1206         public ScopeSelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
1207                 throws RepositoryException {
1208             return this;
1209         }
1210 
1211         /**
1212          * Determines the effective scope of the dependency given by {@link ConflictContext#getWinner()}.
1213          * Implementations will usually iterate {@link ConflictContext#getItems()}, inspect
1214          * {@link ConflictItem#getScopes()} and eventually call {@link ConflictContext#setScope(String)} to deliver the
1215          * effective scope.
1216          *
1217          * @param context The conflict context, must not be {@code null}.
1218          * @throws RepositoryException If the scope selection failed.
1219          */
1220         public abstract void selectScope(ConflictContext context) throws RepositoryException;
1221     }
1222 
1223     /**
1224      * An extension point of {@link ConflictResolver} that determines the scope of a dependency in relation to the scope
1225      * of its parent.
1226      * <p>
1227      * <strong>Note:</strong> Implementations must be stateless.
1228      */
1229     public abstract static class ScopeDeriver {
1230 
1231         /**
1232          * Retrieves the scope deriver for use during the specified graph transformation. The conflict resolver calls
1233          * this method once per
1234          * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
1235          * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
1236          * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
1237          * default implementation simply returns the current instance which is appropriate for implementations which do
1238          * not require auxiliary data.
1239          *
1240          * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
1241          * @param context The graph transformation context, must not be {@code null}.
1242          * @return The scope deriver to use for the given graph transformation, never {@code null}.
1243          * @throws RepositoryException If the instance could not be retrieved.
1244          */
1245         public ScopeDeriver getInstance(DependencyNode root, DependencyGraphTransformationContext context)
1246                 throws RepositoryException {
1247             return this;
1248         }
1249 
1250         /**
1251          * Determines the scope of a dependency in relation to the scope of its parent. Implementors need to call
1252          * {@link ScopeContext#setDerivedScope(String)} to deliver the result of their calculation. If said method is
1253          * not invoked, the conflict resolver will assume the scope of the child dependency remains unchanged.
1254          *
1255          * @param context The scope context, must not be {@code null}.
1256          * @throws RepositoryException If the scope deriviation failed.
1257          */
1258         public abstract void deriveScope(ScopeContext context) throws RepositoryException;
1259     }
1260 
1261     /**
1262      * An extension point of {@link ConflictResolver} that determines the effective optional flag of a dependency from a
1263      * potentially conflicting set of derived optionalities. The optionality selector gets invoked after the
1264      * {@link VersionSelector} has picked the winning node.
1265      * <p>
1266      * <strong>Note:</strong> Implementations must be stateless.
1267      */
1268     public abstract static class OptionalitySelector {
1269 
1270         /**
1271          * Retrieves the optionality selector for use during the specified graph transformation. The conflict resolver
1272          * calls this method once per
1273          * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
1274          * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
1275          * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
1276          * default implementation simply returns the current instance which is appropriate for implementations which do
1277          * not require auxiliary data.
1278          *
1279          * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
1280          * @param context The graph transformation context, must not be {@code null}.
1281          * @return The optionality selector to use for the given graph transformation, never {@code null}.
1282          * @throws RepositoryException If the instance could not be retrieved.
1283          */
1284         public OptionalitySelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
1285                 throws RepositoryException {
1286             return this;
1287         }
1288 
1289         /**
1290          * Determines the effective optional flag of the dependency given by {@link ConflictContext#getWinner()}.
1291          * Implementations will usually iterate {@link ConflictContext#getItems()}, inspect
1292          * {@link ConflictItem#getOptionalities()} and eventually call {@link ConflictContext#setOptional(Boolean)} to
1293          * deliver the effective optional flag.
1294          *
1295          * @param context The conflict context, must not be {@code null}.
1296          * @throws RepositoryException If the optionality selection failed.
1297          */
1298         public abstract void selectOptionality(ConflictContext context) throws RepositoryException;
1299     }
1300 }