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.Arrays;
22  import java.util.Collection;
23  
24  import org.eclipse.aether.ConfigurationProperties;
25  import org.eclipse.aether.RepositoryException;
26  import org.eclipse.aether.RepositorySystemSession;
27  import org.eclipse.aether.collection.DependencyGraphTransformationContext;
28  import org.eclipse.aether.collection.DependencyGraphTransformer;
29  import org.eclipse.aether.graph.Dependency;
30  import org.eclipse.aether.graph.DependencyNode;
31  import org.eclipse.aether.util.ConfigUtils;
32  
33  import static java.util.Objects.requireNonNull;
34  
35  /**
36   * Abstract base class for dependency graph transformers that resolve version and scope conflicts among dependencies.
37   * For a given set of conflicting nodes, one node will be chosen as the winner. How losing nodes are handled depends
38   * on the configured verbosity level: they may be removed entirely, have their children removed, or be left in place
39   * with conflict information. The exact rules by which a winning node and its effective scope are determined are
40   * controlled by user-supplied implementations of {@link VersionSelector}, {@link ScopeSelector},
41   * {@link OptionalitySelector} and {@link ScopeDeriver}.
42   * <p>
43   * <strong>Available Implementations:</strong>
44   * <ul>
45   * <li><strong>{@link PathConflictResolver}</strong> - Recommended high-performance implementation with O(N) complexity</li>
46   * <li><strong>{@link ClassicConflictResolver}</strong> - Legacy implementation for backward compatibility (O(N²) worst-case)</li>
47   * </ul>
48   * <p>
49   * <strong>Implementation Selection Guide:</strong>
50   * <ul>
51   * <li><strong>New Projects:</strong> Use {@link PathConflictResolver} for optimal performance</li>
52   * <li><strong>Large Multi-Module Projects:</strong> Use {@link PathConflictResolver} to avoid performance bottlenecks</li>
53   * <li><strong>Maven 4+ Environments:</strong> Use {@link PathConflictResolver} for best build performance</li>
54   * <li><strong>Legacy Compatibility:</strong> Use {@link ClassicConflictResolver} only when exact Maven 3.x behavior is required</li>
55   * </ul>
56   * <p>
57   * <strong>Usage Example:</strong>
58   * <pre>{@code
59   * // Recommended: High-performance path-based resolver
60   * DependencyGraphTransformer transformer = new ChainedDependencyGraphTransformer(
61   *     new PathConflictResolver(
62   *         new NearestVersionSelector(),
63   *         new JavaScopeSelector(),
64   *         new SimpleOptionalitySelector(),
65   *         new JavaScopeDeriver()),
66   *     // other transformers...
67   * );
68   *
69   * // Legacy: Classic resolver for backward compatibility
70   * DependencyGraphTransformer legacyTransformer = new ChainedDependencyGraphTransformer(
71   *     new ClassicConflictResolver(
72   *         new NearestVersionSelector(),
73   *         new JavaScopeSelector(),
74   *         new SimpleOptionalitySelector(),
75   *         new JavaScopeDeriver()),
76   *     // other transformers...
77   * );
78   * }</pre>
79   * <p>
80   * <strong>Verbosity Levels and Conflict Handling:</strong>
81   * <ul>
82   * <li><strong>NONE (default):</strong> Creates a clean dependency tree without duplicate artifacts.
83   *     Losing nodes are completely removed from the graph, so are cycles as well.</li>
84   * <li><strong>STANDARD:</strong> Retains losing nodes for analysis but removes their children to prevent
85   *     duplicate dependencies. Special handling for version ranges: redundant nodes may still be removed
86   *     if multiple versions of the same artifact exist. Losing nodes link back to the winner via
87   *     {@link #NODE_DATA_WINNER} and preserve original scope/optionality information. This mode removes cycles only,
88   *     while conflict nodes/duplicates are left in place. Graphs in this verbosity level cannot be resolved,
89   *     their purpose is for analysis only.</li>
90   * <li><strong>FULL:</strong> Preserves the complete original graph structure including all conflicts and cycles.
91   *     All nodes remain with their children, but conflict information is recorded for analysis.
92   *     Graphs in this verbosity level cannot be resolved, their purpose is for analysis only.</li>
93   * </ul>
94   * The verbosity level is controlled by the {@link #CONFIG_PROP_VERBOSE} configuration property.
95   * <p>
96   * <strong>Conflict Metadata:</strong> In STANDARD and FULL modes, the keys {@link #NODE_DATA_ORIGINAL_SCOPE}
97   * and {@link #NODE_DATA_ORIGINAL_OPTIONALITY} are used to store the original scope and optionality of each node.
98   * Obviously, dependency trees with verbosity STANDARD or FULL are not suitable for artifact resolution unless
99   * a filter is employed to exclude the duplicate dependencies.
100  * <p>
101  * <strong>Conflict ID Processing Pipeline:</strong>
102  * <ol>
103  * <li><strong>{@link ConflictMarker}:</strong> Assigns conflict IDs based on GACE (groupId:artifactId:classifier:extension)
104  *     coordinates, grouping artifacts that differ only in version (partitions the graph, assigning same conflict IDs
105  *     to nodes belonging to same conflict group).</li>
106  * <li><strong>{@link ConflictIdSorter}:</strong> Creates topological ordering of conflict IDs and detects cycles</li>
107  * <li><strong>ConflictResolver implementation:</strong> Uses the sorted conflict IDs to resolve conflicts in dependency order</li>
108  * </ol>
109  * This transformer will query the keys {@link TransformationContextKeys#CONFLICT_IDS},
110  * {@link TransformationContextKeys#SORTED_CONFLICT_IDS}, {@link TransformationContextKeys#CYCLIC_CONFLICT_IDS} for
111  * existing information about conflict ids. In absence of this information, it will automatically invoke the
112  * {@link ConflictIdSorter} to calculate it.
113  *
114  * @see PathConflictResolver
115  * @see ClassicConflictResolver
116  */
117 public class ConflictResolver implements DependencyGraphTransformer {
118 
119     /**
120      * The key in the repository session's {@link org.eclipse.aether.RepositorySystemSession#getConfigProperties()
121      * configuration properties} used to store a {@link Boolean} flag controlling the transformer's verbose mode.
122      * Accepted values are Boolean types, String type (where "true" would be interpreted as {@code true})
123      * or Verbosity enum instances.
124      *
125      * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
126      * @configurationType {@link java.lang.Object}
127      * @configurationDefaultValue "NONE"
128      */
129     public static final String CONFIG_PROP_VERBOSE = ConfigurationProperties.PREFIX_AETHER + "conflictResolver.verbose";
130 
131     /**
132      * The name of the conflict resolver implementation to use: "path" (default) or "classic" (same as Maven 3).
133      *
134      * @since 2.0.11
135      * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
136      * @configurationType {@link java.lang.String}
137      * @configurationDefaultValue {@link #DEFAULT_CONFLICT_RESOLVER_IMPL}
138      */
139     public static final String CONFIG_PROP_CONFLICT_RESOLVER_IMPL =
140             ConfigurationProperties.PREFIX_AETHER + "conflictResolver.impl";
141 
142     public static final String CLASSIC_CONFLICT_RESOLVER = "classic";
143     public static final String PATH_CONFLICT_RESOLVER = "path";
144 
145     public static final String DEFAULT_CONFLICT_RESOLVER_IMPL = PATH_CONFLICT_RESOLVER;
146 
147     /**
148      * The enum representing verbosity levels of conflict resolver.
149      *
150      * @since 1.9.8
151      */
152     public enum Verbosity {
153         /**
154          * Verbosity level to be used in all "common" resolving use cases (ie. dependencies to build class path). The
155          * {@link ConflictResolver} in this mode will trim down the graph to the barest minimum: will not leave
156          * any conflicting node in place, hence no conflicts will be present in transformed graph either.
157          */
158         NONE,
159 
160         /**
161          * Verbosity level to be used in "analyze" resolving use cases (ie. dependency convergence calculations). The
162          * {@link ConflictResolver} in this mode will remove any redundant collected nodes, in turn it will leave one
163          * with recorded conflicting information. This mode corresponds to "classic verbose" mode when
164          * {@link #CONFIG_PROP_VERBOSE} was set to {@code true}. Obviously, the resulting dependency tree is not
165          * suitable for artifact resolution unless a filter is employed to exclude the duplicate dependencies.
166          */
167         STANDARD,
168 
169         /**
170          * Verbosity level to be used in "analyze" resolving use cases (ie. dependency convergence calculations). The
171          * {@link ConflictResolver} in this mode will not remove any collected node, in turn it will record on all
172          * eliminated nodes the conflicting information. Obviously, the resulting dependency tree is not suitable
173          * for artifact resolution unless a filter is employed to exclude the duplicate dependencies.
174          */
175         FULL
176     }
177 
178     /**
179      * Helper method that uses {@link RepositorySystemSession} and {@link #CONFIG_PROP_VERBOSE} key to figure out
180      * current {@link Verbosity}: if {@link Boolean} or {@code String} found, returns {@link Verbosity#STANDARD}
181      * or {@link Verbosity#NONE}, depending on value (string is parsed with {@link Boolean#parseBoolean(String)}
182      * for {@code true} or {@code false} correspondingly. This is to retain "existing" behavior, where the config
183      * key accepted only these values.
184      * Since 1.9.8 release, this key may contain {@link Verbosity} enum instance as well, in which case that instance
185      * is returned.
186      * This method never returns {@code null}.
187      */
188     public static Verbosity getVerbosity(RepositorySystemSession session) {
189         final Object verbosityValue = session.getConfigProperties().get(CONFIG_PROP_VERBOSE);
190         if (verbosityValue instanceof Boolean) {
191             return (Boolean) verbosityValue ? Verbosity.STANDARD : Verbosity.NONE;
192         } else if (verbosityValue instanceof String) {
193             return Boolean.parseBoolean(verbosityValue.toString()) ? Verbosity.STANDARD : Verbosity.NONE;
194         } else if (verbosityValue instanceof Verbosity) {
195             return (Verbosity) verbosityValue;
196         } else if (verbosityValue != null) {
197             throw new IllegalArgumentException("Unsupported Verbosity configuration: " + verbosityValue);
198         }
199         return Verbosity.NONE;
200     }
201 
202     /**
203      * The key in the dependency node's {@link DependencyNode#getData() custom data} under which a reference to the
204      * {@link DependencyNode} which has won the conflict is stored.
205      */
206     public static final String NODE_DATA_WINNER = "conflict.winner";
207 
208     /**
209      * The key in the dependency node's {@link DependencyNode#getData() custom data} under which the scope of the
210      * dependency before scope derivation and conflict resolution is stored.
211      */
212     public static final String NODE_DATA_ORIGINAL_SCOPE = "conflict.originalScope";
213 
214     /**
215      * The key in the dependency node's {@link DependencyNode#getData() custom data} under which the optional flag of
216      * the dependency before derivation and conflict resolution is stored.
217      */
218     public static final String NODE_DATA_ORIGINAL_OPTIONALITY = "conflict.originalOptionality";
219 
220     private final ConflictResolver.VersionSelector versionSelector;
221     private final ConflictResolver.ScopeSelector scopeSelector;
222     private final ConflictResolver.ScopeDeriver scopeDeriver;
223     private final ConflictResolver.OptionalitySelector optionalitySelector;
224 
225     /**
226      * No arg ctor for subclasses and default cases.
227      */
228     protected ConflictResolver() {
229         this.versionSelector = null;
230         this.scopeSelector = null;
231         this.scopeDeriver = null;
232         this.optionalitySelector = null;
233     }
234 
235     /**
236      * Creates a new conflict resolver instance with the specified hooks that delegates to configured conflict resolver
237      * dynamically.
238      *
239      * @param versionSelector The version selector to use, must not be {@code null}.
240      * @param scopeSelector The scope selector to use, must not be {@code null}.
241      * @param optionalitySelector The optionality selector ot use, must not be {@code null}.
242      * @param scopeDeriver The scope deriver to use, must not be {@code null}.
243      */
244     public ConflictResolver(
245             VersionSelector versionSelector,
246             ScopeSelector scopeSelector,
247             OptionalitySelector optionalitySelector,
248             ScopeDeriver scopeDeriver) {
249         this.versionSelector = requireNonNull(versionSelector, "version selector cannot be null");
250         this.scopeSelector = requireNonNull(scopeSelector, "scope selector cannot be null");
251         this.optionalitySelector = requireNonNull(optionalitySelector, "optionality selector cannot be null");
252         this.scopeDeriver = requireNonNull(scopeDeriver, "scope deriver cannot be null");
253     }
254 
255     @Override
256     public DependencyNode transformGraph(DependencyNode node, DependencyGraphTransformationContext context)
257             throws RepositoryException {
258         String cf = ConfigUtils.getString(
259                 context.getSession(), DEFAULT_CONFLICT_RESOLVER_IMPL, CONFIG_PROP_CONFLICT_RESOLVER_IMPL);
260         ConflictResolver delegate;
261         if (PATH_CONFLICT_RESOLVER.equals(cf)) {
262             delegate = new PathConflictResolver(versionSelector, scopeSelector, optionalitySelector, scopeDeriver);
263         } else if (CLASSIC_CONFLICT_RESOLVER.equals(cf)) {
264             delegate = new ClassicConflictResolver(versionSelector, scopeSelector, optionalitySelector, scopeDeriver);
265         } else {
266             throw new IllegalArgumentException("Unknown conflict resolver: " + cf + "; known are "
267                     + Arrays.asList(PATH_CONFLICT_RESOLVER, CLASSIC_CONFLICT_RESOLVER));
268         }
269         return delegate.transformGraph(node, context);
270     }
271 
272     /**
273      * A context used to hold information that is relevant for deriving the scope of a child dependency.
274      *
275      * @see ScopeDeriver
276      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
277      *                change without notice and only exists to enable unit testing.
278      */
279     public abstract static class ScopeContext {
280         /**
281          * Gets the scope of the parent dependency. This is usually the scope that was derived by earlier invocations of
282          * the scope deriver.
283          *
284          * @return The scope of the parent dependency, never {@code null}.
285          */
286         public abstract String getParentScope();
287 
288         /**
289          * Gets the original scope of the child dependency. This is the scope that was declared in the artifact
290          * descriptor of the parent dependency.
291          *
292          * @return The original scope of the child dependency, never {@code null}.
293          */
294         public abstract String getChildScope();
295 
296         /**
297          * Gets the derived scope of the child dependency. This is initially equal to {@link #getChildScope()} until the
298          * scope deriver makes changes.
299          *
300          * @return The derived scope of the child dependency, never {@code null}.
301          */
302         public abstract String getDerivedScope();
303 
304         /**
305          * Sets the derived scope of the child dependency.
306          *
307          * @param derivedScope The derived scope of the dependency, may be {@code null}.
308          */
309         public abstract void setDerivedScope(String derivedScope);
310     }
311 
312     /**
313      * A conflicting dependency.
314      *
315      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
316      *                change without notice and only exists to enable unit testing.
317      */
318     public abstract static class ConflictItem {
319         /**
320          * Determines whether the specified conflict item is a sibling of this item.
321          *
322          * @param item The other conflict item, must not be {@code null}.
323          * @return {@code true} if the given item has the same parent as this item, {@code false} otherwise.
324          */
325         public abstract boolean isSibling(ConflictItem item);
326 
327         /**
328          * Gets the dependency node involved in the conflict.
329          *
330          * @return The involved dependency node, never {@code null}.
331          */
332         public abstract DependencyNode getNode();
333 
334         /**
335          * Gets the dependency involved in the conflict, short for {@code getNode.getDependency()}.
336          *
337          * @return The involved dependency, never {@code null}.
338          */
339         public abstract Dependency getDependency();
340 
341         /**
342          * Gets the zero-based depth at which the conflicting node occurs in the graph. As such, the depth denotes the
343          * number of parent nodes. If actually multiple paths lead to the node, the return value denotes the smallest
344          * possible depth.
345          *
346          * @return The zero-based depth of the node in the graph.
347          */
348         public abstract int getDepth();
349 
350         /**
351          * Gets the derived scopes of the dependency. In general, the same dependency node could be reached via
352          * different paths and each path might result in a different derived scope.
353          *
354          * @see ScopeDeriver
355          * @return The (read-only) set of derived scopes of the dependency, never {@code null}.
356          */
357         public abstract Collection<String> getScopes();
358 
359         /**
360          * Bit flag indicating whether one or more paths consider the dependency non-optional.
361          */
362         public static final int OPTIONAL_FALSE = 0x01;
363 
364         /**
365          * Bit flag indicating whether one or more paths consider the dependency optional.
366          */
367         public static final int OPTIONAL_TRUE = 0x02;
368 
369         /**
370          * Gets the derived optionalities of the dependency. In general, the same dependency node could be reached via
371          * different paths and each path might result in a different derived optionality.
372          *
373          * @return A bit field consisting of {@link ConflictResolver.ConflictItem#OPTIONAL_FALSE} and/or
374          *         {@link ConflictResolver.ConflictItem#OPTIONAL_TRUE} indicating the derived optionalities the
375          *         dependency was encountered with.
376          */
377         public abstract int getOptionalities();
378     }
379 
380     /**
381      * A context used to hold information that is relevant for resolving version and scope conflicts.
382      *
383      * @see VersionSelector
384      * @see ScopeSelector
385      * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
386      *                change without notice and only exists to enable unit testing.
387      */
388     public abstract static class ConflictContext {
389         /**
390          * Gets the root node of the dependency graph being transformed.
391          *
392          * @return The root node of the dependency graph, never {@code null}.
393          */
394         public abstract DependencyNode getRoot();
395 
396         /**
397          * Determines whether the specified dependency node belongs to this conflict context.
398          *
399          * @param node The dependency node to check, must not be {@code null}.
400          * @return {@code true} if the given node belongs to this conflict context, {@code false} otherwise.
401          */
402         public abstract boolean isIncluded(DependencyNode node);
403 
404         /**
405          * Gets the collection of conflict items in this context.
406          *
407          * @return The (read-only) collection of conflict items in this context, never {@code null}.
408          */
409         public abstract Collection<ConflictItem> getItems();
410 
411         /**
412          * Gets the conflict item which has been selected as the winner among the conflicting dependencies.
413          *
414          * @return The winning conflict item or {@code null} if not set yet.
415          */
416         public abstract ConflictItem getWinner();
417 
418         /**
419          * Sets the conflict item which has been selected as the winner among the conflicting dependencies.
420          *
421          * @param winner The winning conflict item, may be {@code null}.
422          */
423         public abstract void setWinner(ConflictItem winner);
424 
425         /**
426          * Gets the effective scope of the winning dependency.
427          *
428          * @return The effective scope of the winning dependency or {@code null} if none.
429          */
430         public abstract String getScope();
431 
432         /**
433          * Sets the effective scope of the winning dependency.
434          *
435          * @param scope The effective scope, may be {@code null}.
436          */
437         public abstract void setScope(String scope);
438 
439         /**
440          * Gets the effective optional flag of the winning dependency.
441          *
442          * @return The effective optional flag or {@code null} if none.
443          */
444         public abstract Boolean getOptional();
445 
446         /**
447          * Sets the effective optional flag of the winning dependency.
448          *
449          * @param optional The effective optional flag, may be {@code null}.
450          */
451         public abstract void setOptional(Boolean optional);
452     }
453 
454     /**
455      * An extension point of {@link ConflictResolver} that determines the winner among conflicting dependencies. The
456      * winning node (and its children) will be retained in the dependency graph, the other nodes will get removed. The
457      * version selector does not need to deal with potential scope conflicts, these will be addressed afterwards by the
458      * {@link ScopeSelector}.
459      * <p>
460      * <strong>Note:</strong> Implementations must be stateless.
461      */
462     public abstract static class VersionSelector {
463 
464         /**
465          * Retrieves the version selector for use during the specified graph transformation. The conflict resolver calls
466          * this method once per
467          * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
468          * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
469          * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
470          * default implementation simply returns the current instance which is appropriate for implementations which do
471          * not require auxiliary data.
472          *
473          * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
474          * @param context The graph transformation context, must not be {@code null}.
475          * @return The scope deriver to use for the given graph transformation, never {@code null}.
476          * @throws RepositoryException If the instance could not be retrieved.
477          */
478         public VersionSelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
479                 throws RepositoryException {
480             return this;
481         }
482 
483         /**
484          * Determines the winning node among conflicting dependencies. Implementations will usually iterate
485          * {@link ConflictContext#getItems()}, inspect {@link ConflictItem#getNode()} and eventually call
486          * {@link ConflictContext#setWinner(ConflictResolver.ConflictItem)} to deliver the winner. Failure to select a
487          * winner will automatically fail the entire conflict resolution.
488          *
489          * @param context The conflict context, must not be {@code null}.
490          * @throws RepositoryException If the version selection failed.
491          */
492         public abstract void selectVersion(ConflictContext context) throws RepositoryException;
493     }
494 
495     /**
496      * An extension point of {@link ConflictResolver} that determines the effective scope of a dependency from a
497      * potentially conflicting set of {@link ScopeDeriver derived scopes}. The scope selector gets invoked after the
498      * {@link VersionSelector} has picked the winning node.
499      * <p>
500      * <strong>Note:</strong> Implementations must be stateless.
501      */
502     public abstract static class ScopeSelector {
503 
504         /**
505          * Retrieves the scope selector for use during the specified graph transformation. The conflict resolver calls
506          * this method once per
507          * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
508          * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
509          * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
510          * default implementation simply returns the current instance which is appropriate for implementations which do
511          * not require auxiliary data.
512          *
513          * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
514          * @param context The graph transformation context, must not be {@code null}.
515          * @return The scope selector to use for the given graph transformation, never {@code null}.
516          * @throws RepositoryException If the instance could not be retrieved.
517          */
518         public ScopeSelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
519                 throws RepositoryException {
520             return this;
521         }
522 
523         /**
524          * Determines the effective scope of the dependency given by {@link ConflictContext#getWinner()}.
525          * Implementations will usually iterate {@link ConflictContext#getItems()}, inspect
526          * {@link ConflictItem#getScopes()} and eventually call {@link ConflictContext#setScope(String)} to deliver the
527          * effective scope.
528          *
529          * @param context The conflict context, must not be {@code null}.
530          * @throws RepositoryException If the scope selection failed.
531          */
532         public abstract void selectScope(ConflictContext context) throws RepositoryException;
533     }
534 
535     /**
536      * An extension point of {@link ConflictResolver} that determines the scope of a dependency in relation to the scope
537      * of its parent.
538      * <p>
539      * <strong>Note:</strong> Implementations must be stateless.
540      */
541     public abstract static class ScopeDeriver {
542 
543         /**
544          * Retrieves the scope deriver for use during the specified graph transformation. The conflict resolver calls
545          * this method once per
546          * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
547          * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
548          * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
549          * default implementation simply returns the current instance which is appropriate for implementations which do
550          * not require auxiliary data.
551          *
552          * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
553          * @param context The graph transformation context, must not be {@code null}.
554          * @return The scope deriver to use for the given graph transformation, never {@code null}.
555          * @throws RepositoryException If the instance could not be retrieved.
556          */
557         public ScopeDeriver getInstance(DependencyNode root, DependencyGraphTransformationContext context)
558                 throws RepositoryException {
559             return this;
560         }
561 
562         /**
563          * Determines the scope of a dependency in relation to the scope of its parent. Implementors need to call
564          * {@link ScopeContext#setDerivedScope(String)} to deliver the result of their calculation. If said method is
565          * not invoked, the conflict resolver will assume the scope of the child dependency remains unchanged.
566          *
567          * @param context The scope context, must not be {@code null}.
568          * @throws RepositoryException If the scope deriviation failed.
569          */
570         public abstract void deriveScope(ScopeContext context) throws RepositoryException;
571     }
572 
573     /**
574      * An extension point of {@link ConflictResolver} that determines the effective optional flag of a dependency from a
575      * potentially conflicting set of derived optionalities. The optionality selector gets invoked after the
576      * {@link VersionSelector} has picked the winning node.
577      * <p>
578      * <strong>Note:</strong> Implementations must be stateless.
579      */
580     public abstract static class OptionalitySelector {
581 
582         /**
583          * Retrieves the optionality selector for use during the specified graph transformation. The conflict resolver
584          * calls this method once per
585          * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
586          * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
587          * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
588          * default implementation simply returns the current instance which is appropriate for implementations which do
589          * not require auxiliary data.
590          *
591          * @param root The root node of the (possibly cyclic!) graph to transform, must not be {@code null}.
592          * @param context The graph transformation context, must not be {@code null}.
593          * @return The optionality selector to use for the given graph transformation, never {@code null}.
594          * @throws RepositoryException If the instance could not be retrieved.
595          */
596         public OptionalitySelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
597                 throws RepositoryException {
598             return this;
599         }
600 
601         /**
602          * Determines the effective optional flag of the dependency given by {@link ConflictContext#getWinner()}.
603          * Implementations will usually iterate {@link ConflictContext#getItems()}, inspect
604          * {@link ConflictItem#getOptionalities()} and eventually call {@link ConflictContext#setOptional(Boolean)} to
605          * deliver the effective optional flag.
606          *
607          * @param context The conflict context, must not be {@code null}.
608          * @throws RepositoryException If the optionality selection failed.
609          */
610         public abstract void selectOptionality(ConflictContext context) throws RepositoryException;
611     }
612 }