001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.util.graph.transformer;
020
021import java.util.Arrays;
022import java.util.Collection;
023
024import org.eclipse.aether.ConfigurationProperties;
025import org.eclipse.aether.RepositoryException;
026import org.eclipse.aether.RepositorySystemSession;
027import org.eclipse.aether.collection.DependencyGraphTransformationContext;
028import org.eclipse.aether.collection.DependencyGraphTransformer;
029import org.eclipse.aether.graph.Dependency;
030import org.eclipse.aether.graph.DependencyNode;
031import org.eclipse.aether.util.ConfigUtils;
032
033import static java.util.Objects.requireNonNull;
034
035/**
036 * Abstract base class for dependency graph transformers that resolve version and scope conflicts among dependencies.
037 * For a given set of conflicting nodes, one node will be chosen as the winner. How losing nodes are handled depends
038 * on the configured verbosity level: they may be removed entirely, have their children removed, or be left in place
039 * with conflict information. The exact rules by which a winning node and its effective scope are determined are
040 * controlled by user-supplied implementations of {@link VersionSelector}, {@link ScopeSelector},
041 * {@link OptionalitySelector} and {@link ScopeDeriver}.
042 * <p>
043 * <strong>Available Implementations:</strong>
044 * <ul>
045 * <li><strong>{@link ClassicConflictResolver}</strong> - Original implementation (O(N²) worst-case)</li>
046 * <li><strong>{@link PathConflictResolver}</strong> - Not yet recommended for production; high-performance implementation with O(N) complexity</li>
047 * </ul>
048 * <p>
049 * <strong>Implementation Selection Guide:</strong>
050 * <ul>
051 * <li><strong>All projects:</strong> Use {@link ClassicConflictResolver} for optimal correctness and Maven 3.x behavior</li>
052 * <li><strong>Experimenters:</strong> Use {@link PathConflictResolver} but no guarantees it will work</li>
053 * </ul>
054 * <p>
055 * <strong>Usage Example:</strong>
056 * <pre>{@code
057 * // Classic resolver
058 * DependencyGraphTransformer legacyTransformer = new ChainedDependencyGraphTransformer(
059 *     new ClassicConflictResolver(
060 *         new NearestVersionSelector(),
061 *         new JavaScopeSelector(),
062 *         new SimpleOptionalitySelector(),
063 *         new JavaScopeDeriver()),
064 *     // other transformers...
065 * );
066 * }</pre>
067 * <p>
068 * <strong>Verbosity Levels and Conflict Handling:</strong>
069 * <ul>
070 * <li><strong>NONE (default):</strong> Creates a clean dependency tree without duplicate artifacts.
071 *     Losing nodes are completely removed from the graph, so are cycles as well.</li>
072 * <li><strong>STANDARD:</strong> Retains losing nodes for analysis but removes their children to prevent
073 *     duplicate dependencies. Special handling for version ranges: redundant nodes may still be removed
074 *     if multiple versions of the same artifact exist. Losing nodes link back to the winner via
075 *     {@link #NODE_DATA_WINNER} and preserve original scope/optionality information. This mode removes cycles only,
076 *     while conflict nodes/duplicates are left in place. Graphs in this verbosity level cannot be resolved,
077 *     their purpose is for analysis only.</li>
078 * <li><strong>FULL:</strong> Preserves the complete original graph structure including all conflicts and cycles.
079 *     All nodes remain with their children, but conflict information is recorded for analysis.
080 *     Graphs in this verbosity level cannot be resolved, their purpose is for analysis only.</li>
081 * </ul>
082 * The verbosity level is controlled by the {@link #CONFIG_PROP_VERBOSE} configuration property.
083 * <p>
084 * <strong>Conflict Metadata:</strong> In STANDARD and FULL modes, the keys {@link #NODE_DATA_ORIGINAL_SCOPE}
085 * and {@link #NODE_DATA_ORIGINAL_OPTIONALITY} are used to store the original scope and optionality of each node.
086 * Obviously, dependency trees with verbosity STANDARD or FULL are not suitable for artifact resolution unless
087 * a filter is employed to exclude the duplicate dependencies.
088 * <p>
089 * <strong>Conflict ID Processing Pipeline:</strong>
090 * <ol>
091 * <li><strong>{@link ConflictMarker}:</strong> Assigns conflict IDs based on GACE (groupId:artifactId:classifier:extension)
092 *     coordinates, grouping artifacts that differ only in version (partitions the graph, assigning same conflict IDs
093 *     to nodes belonging to same conflict group).</li>
094 * <li><strong>{@link ConflictIdSorter}:</strong> Creates topological ordering of conflict IDs and detects cycles</li>
095 * <li><strong>ConflictResolver implementation:</strong> Uses the sorted conflict IDs to resolve conflicts in dependency order</li>
096 * </ol>
097 * This transformer will query the keys {@link TransformationContextKeys#CONFLICT_IDS},
098 * {@link TransformationContextKeys#SORTED_CONFLICT_IDS}, {@link TransformationContextKeys#CYCLIC_CONFLICT_IDS} for
099 * 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 */
105public 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}