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