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 PathConflictResolver}</strong> - Recommended high-performance implementation with O(N) complexity</li>
046 * <li><strong>{@link ClassicConflictResolver}</strong> - Legacy implementation for backward compatibility (O(N²) worst-case)</li>
047 * </ul>
048 * <p>
049 * <strong>Implementation Selection Guide:</strong>
050 * <ul>
051 * <li><strong>New Projects:</strong> Use {@link PathConflictResolver} for optimal performance</li>
052 * <li><strong>Large Multi-Module Projects:</strong> Use {@link PathConflictResolver} to avoid performance bottlenecks</li>
053 * <li><strong>Maven 4+ Environments:</strong> Use {@link PathConflictResolver} for best build performance</li>
054 * <li><strong>Legacy Compatibility:</strong> Use {@link ClassicConflictResolver} only when exact Maven 3.x behavior is required</li>
055 * </ul>
056 * <p>
057 * <strong>Usage Example:</strong>
058 * <pre>{@code
059 * // Recommended: High-performance path-based resolver
060 * DependencyGraphTransformer transformer = new ChainedDependencyGraphTransformer(
061 *     new PathConflictResolver(
062 *         new NearestVersionSelector(),
063 *         new JavaScopeSelector(),
064 *         new SimpleOptionalitySelector(),
065 *         new JavaScopeDeriver()),
066 *     // other transformers...
067 * );
068 *
069 * // Legacy: Classic resolver for backward compatibility
070 * DependencyGraphTransformer legacyTransformer = new ChainedDependencyGraphTransformer(
071 *     new ClassicConflictResolver(
072 *         new NearestVersionSelector(),
073 *         new JavaScopeSelector(),
074 *         new SimpleOptionalitySelector(),
075 *         new JavaScopeDeriver()),
076 *     // other transformers...
077 * );
078 * }</pre>
079 * <p>
080 * <strong>Verbosity Levels and Conflict Handling:</strong>
081 * <ul>
082 * <li><strong>NONE (default):</strong> Creates a clean dependency tree without duplicate artifacts.
083 *     Losing nodes are completely removed from the graph, so are cycles as well.</li>
084 * <li><strong>STANDARD:</strong> Retains losing nodes for analysis but removes their children to prevent
085 *     duplicate dependencies. Special handling for version ranges: redundant nodes may still be removed
086 *     if multiple versions of the same artifact exist. Losing nodes link back to the winner via
087 *     {@link #NODE_DATA_WINNER} and preserve original scope/optionality information. This mode removes cycles only,
088 *     while conflict nodes/duplicates are left in place. Graphs in this verbosity level cannot be resolved,
089 *     their purpose is for analysis only.</li>
090 * <li><strong>FULL:</strong> Preserves the complete original graph structure including all conflicts and cycles.
091 *     All nodes remain with their children, but conflict information is recorded for analysis.
092 *     Graphs in this verbosity level cannot be resolved, their purpose is for analysis only.</li>
093 * </ul>
094 * The verbosity level is controlled by the {@link #CONFIG_PROP_VERBOSE} configuration property.
095 * <p>
096 * <strong>Conflict Metadata:</strong> In STANDARD and FULL modes, the keys {@link #NODE_DATA_ORIGINAL_SCOPE}
097 * and {@link #NODE_DATA_ORIGINAL_OPTIONALITY} are used to store the original scope and optionality of each node.
098 * Obviously, dependency trees with verbosity STANDARD or FULL are not suitable for artifact resolution unless
099 * 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 */
117public 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 and cycles, in turn it will
163         * leave one 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 nor cycle, in turn it will record
172         * on all 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 and possible cycles.
174         * Because of left in cycles, user of this verbosity level should ensure that graph post-processing does not
175         * contain elements that would explode on them. In other words, session should be modified with proper
176         * graph transformers.
177         *
178         * @see RepositorySystemSession#getDependencyGraphTransformer()
179         */
180        FULL
181    }
182
183    /**
184     * Helper method that uses {@link RepositorySystemSession} and {@link #CONFIG_PROP_VERBOSE} key to figure out
185     * current {@link Verbosity}: if {@link Boolean} or {@code String} found, returns {@link Verbosity#STANDARD}
186     * or {@link Verbosity#NONE}, depending on value (string is parsed with {@link Boolean#parseBoolean(String)}
187     * for {@code true} or {@code false} correspondingly. This is to retain "existing" behavior, where the config
188     * key accepted only these values.
189     * Since 1.9.8 release, this key may contain {@link Verbosity} enum instance as well, in which case that instance
190     * is returned.
191     * This method never returns {@code null}.
192     */
193    public static Verbosity getVerbosity(RepositorySystemSession session) {
194        final Object verbosityValue = session.getConfigProperties().get(CONFIG_PROP_VERBOSE);
195        if (verbosityValue instanceof Boolean) {
196            return (Boolean) verbosityValue ? Verbosity.STANDARD : Verbosity.NONE;
197        } else if (verbosityValue instanceof String) {
198            return Boolean.parseBoolean(verbosityValue.toString()) ? Verbosity.STANDARD : Verbosity.NONE;
199        } else if (verbosityValue instanceof Verbosity) {
200            return (Verbosity) verbosityValue;
201        } else if (verbosityValue != null) {
202            throw new IllegalArgumentException("Unsupported Verbosity configuration: " + verbosityValue);
203        }
204        return Verbosity.NONE;
205    }
206
207    /**
208     * The key in the dependency node's {@link DependencyNode#getData() custom data} under which a reference to the
209     * {@link DependencyNode} which has won the conflict is stored.
210     */
211    public static final String NODE_DATA_WINNER = "conflict.winner";
212
213    /**
214     * The key in the dependency node's {@link DependencyNode#getData() custom data} under which the scope of the
215     * dependency before scope derivation and conflict resolution is stored.
216     */
217    public static final String NODE_DATA_ORIGINAL_SCOPE = "conflict.originalScope";
218
219    /**
220     * The key in the dependency node's {@link DependencyNode#getData() custom data} under which the optional flag of
221     * the dependency before derivation and conflict resolution is stored.
222     */
223    public static final String NODE_DATA_ORIGINAL_OPTIONALITY = "conflict.originalOptionality";
224
225    private final ConflictResolver.VersionSelector versionSelector;
226    private final ConflictResolver.ScopeSelector scopeSelector;
227    private final ConflictResolver.ScopeDeriver scopeDeriver;
228    private final ConflictResolver.OptionalitySelector optionalitySelector;
229
230    /**
231     * No arg ctor for subclasses and default cases.
232     */
233    protected ConflictResolver() {
234        this.versionSelector = null;
235        this.scopeSelector = null;
236        this.scopeDeriver = null;
237        this.optionalitySelector = null;
238    }
239
240    /**
241     * Creates a new conflict resolver instance with the specified hooks that delegates to configured conflict resolver
242     * dynamically.
243     *
244     * @param versionSelector the version selector to use, must not be {@code null}
245     * @param scopeSelector the scope selector to use, must not be {@code null}
246     * @param optionalitySelector the optionality selector ot use, must not be {@code null}
247     * @param scopeDeriver the scope deriver to use, must not be {@code null}
248     */
249    public ConflictResolver(
250            VersionSelector versionSelector,
251            ScopeSelector scopeSelector,
252            OptionalitySelector optionalitySelector,
253            ScopeDeriver scopeDeriver) {
254        this.versionSelector = requireNonNull(versionSelector, "version selector cannot be null");
255        this.scopeSelector = requireNonNull(scopeSelector, "scope selector cannot be null");
256        this.optionalitySelector = requireNonNull(optionalitySelector, "optionality selector cannot be null");
257        this.scopeDeriver = requireNonNull(scopeDeriver, "scope deriver cannot be null");
258    }
259
260    @Override
261    public DependencyNode transformGraph(DependencyNode node, DependencyGraphTransformationContext context)
262            throws RepositoryException {
263        String cf = ConfigUtils.getString(
264                context.getSession(), DEFAULT_CONFLICT_RESOLVER_IMPL, CONFIG_PROP_CONFLICT_RESOLVER_IMPL);
265        ConflictResolver delegate;
266        if (PATH_CONFLICT_RESOLVER.equals(cf)) {
267            delegate = new PathConflictResolver(versionSelector, scopeSelector, optionalitySelector, scopeDeriver);
268        } else if (CLASSIC_CONFLICT_RESOLVER.equals(cf)) {
269            delegate = new ClassicConflictResolver(versionSelector, scopeSelector, optionalitySelector, scopeDeriver);
270        } else {
271            throw new IllegalArgumentException("Unknown conflict resolver: " + cf + "; known are "
272                    + Arrays.asList(PATH_CONFLICT_RESOLVER, CLASSIC_CONFLICT_RESOLVER));
273        }
274        return delegate.transformGraph(node, context);
275    }
276
277    /**
278     * A context used to hold information that is relevant for deriving the scope of a child dependency.
279     *
280     * @see ScopeDeriver
281     * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
282     *                change without notice and only exists to enable unit testing
283     */
284    public abstract static class ScopeContext {
285        /**
286         * Gets the scope of the parent dependency. This is usually the scope that was derived by earlier invocations of
287         * the scope deriver.
288         *
289         * @return the scope of the parent dependency, never {@code null}
290         */
291        public abstract String getParentScope();
292
293        /**
294         * Gets the original scope of the child dependency. This is the scope that was declared in the artifact
295         * descriptor of the parent dependency.
296         *
297         * @return the original scope of the child dependency, never {@code null}
298         */
299        public abstract String getChildScope();
300
301        /**
302         * Gets the derived scope of the child dependency. This is initially equal to {@link #getChildScope()} until the
303         * scope deriver makes changes.
304         *
305         * @return the derived scope of the child dependency, never {@code null}
306         */
307        public abstract String getDerivedScope();
308
309        /**
310         * Sets the derived scope of the child dependency.
311         *
312         * @param derivedScope the derived scope of the dependency, may be {@code null}
313         */
314        public abstract void setDerivedScope(String derivedScope);
315    }
316
317    /**
318     * A conflicting dependency.
319     *
320     * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
321     *                change without notice and only exists to enable unit testing
322     */
323    public abstract static class ConflictItem {
324        /**
325         * Determines whether the specified conflict item is a sibling of this item.
326         *
327         * @param item the other conflict item, must not be {@code null}
328         * @return {@code true} if the given item has the same parent as this item, {@code false} otherwise
329         */
330        public abstract boolean isSibling(ConflictItem item);
331
332        /**
333         * Gets the dependency node involved in the conflict.
334         *
335         * @return the involved dependency node, never {@code null}
336         */
337        public abstract DependencyNode getNode();
338
339        /**
340         * Gets the dependency involved in the conflict, short for {@code getNode.getDependency()}.
341         *
342         * @return the involved dependency, never {@code null}
343         */
344        public abstract Dependency getDependency();
345
346        /**
347         * Gets the zero-based depth at which the conflicting node occurs in the graph. As such, the depth denotes the
348         * number of parent nodes. If actually multiple paths lead to the node, the return value denotes the smallest
349         * possible depth.
350         *
351         * @return the zero-based depth of the node in the graph
352         */
353        public abstract int getDepth();
354
355        /**
356         * Gets the derived scopes of the dependency. In general, the same dependency node could be reached via
357         * different paths and each path might result in a different derived scope.
358         *
359         * @return the (read-only) set of derived scopes of the dependency, never {@code null}
360         * @see ScopeDeriver
361         */
362        public abstract Collection<String> getScopes();
363
364        /**
365         * Bit flag indicating whether one or more paths consider the dependency non-optional.
366         */
367        public static final int OPTIONAL_FALSE = 0x01;
368
369        /**
370         * Bit flag indicating whether one or more paths consider the dependency optional.
371         */
372        public static final int OPTIONAL_TRUE = 0x02;
373
374        /**
375         * Gets the derived optionalities of the dependency. In general, the same dependency node could be reached via
376         * different paths and each path might result in a different derived optionality.
377         *
378         * @return a bit field consisting of {@link ConflictResolver.ConflictItem#OPTIONAL_FALSE} and/or
379         *         {@link ConflictResolver.ConflictItem#OPTIONAL_TRUE} indicating the derived optionalities the
380         *         dependency was encountered with
381         */
382        public abstract int getOptionalities();
383    }
384
385    /**
386     * A context used to hold information that is relevant for resolving version and scope conflicts.
387     *
388     * @see VersionSelector
389     * @see ScopeSelector
390     * @noinstantiate This class is not intended to be instantiated by clients in production code, the constructor may
391     *                change without notice and only exists to enable unit testing
392     */
393    public abstract static class ConflictContext {
394        /**
395         * Gets the root node of the dependency graph being transformed.
396         *
397         * @return the root node of the dependency graph, never {@code null}
398         */
399        public abstract DependencyNode getRoot();
400
401        /**
402         * Determines whether the specified dependency node belongs to this conflict context.
403         *
404         * @param node the dependency node to check, must not be {@code null}
405         * @return {@code true} if the given node belongs to this conflict context, {@code false} otherwise
406         */
407        public abstract boolean isIncluded(DependencyNode node);
408
409        /**
410         * Gets the collection of conflict items in this context.
411         *
412         * @return the (read-only) collection of conflict items in this context, never {@code null}
413         */
414        public abstract Collection<ConflictItem> getItems();
415
416        /**
417         * Gets the conflict item which has been selected as the winner among the conflicting dependencies.
418         *
419         * @return the winning conflict item or {@code null} if not set yet
420         */
421        public abstract ConflictItem getWinner();
422
423        /**
424         * Sets the conflict item which has been selected as the winner among the conflicting dependencies.
425         *
426         * @param winner the winning conflict item, may be {@code null}
427         */
428        public abstract void setWinner(ConflictItem winner);
429
430        /**
431         * Gets the effective scope of the winning dependency.
432         *
433         * @return the effective scope of the winning dependency or {@code null} if none
434         */
435        public abstract String getScope();
436
437        /**
438         * Sets the effective scope of the winning dependency.
439         *
440         * @param scope the effective scope, may be {@code null}
441         */
442        public abstract void setScope(String scope);
443
444        /**
445         * Gets the effective optional flag of the winning dependency.
446         *
447         * @return the effective optional flag or {@code null} if none
448         */
449        public abstract Boolean getOptional();
450
451        /**
452         * Sets the effective optional flag of the winning dependency.
453         *
454         * @param optional the effective optional flag, may be {@code null}
455         */
456        public abstract void setOptional(Boolean optional);
457    }
458
459    /**
460     * An extension point of {@link ConflictResolver} that determines the winner among conflicting dependencies. The
461     * winning node (and its children) will be retained in the dependency graph, the other nodes will get removed. The
462     * version selector does not need to deal with potential scope conflicts, these will be addressed afterwards by the
463     * {@link ScopeSelector}.
464     * <p>
465     * <strong>Note:</strong> Implementations must be stateless.
466     */
467    public abstract static class VersionSelector {
468
469        /**
470         * Retrieves the version selector for use during the specified graph transformation. The conflict resolver calls
471         * this method once per
472         * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
473         * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
474         * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
475         * default implementation simply returns the current instance which is appropriate for implementations which do
476         * not require auxiliary data.
477         *
478         * @param root the root node of the (possibly cyclic!) graph to transform, must not be {@code null}
479         * @param context the graph transformation context, must not be {@code null}
480         * @return the scope deriver to use for the given graph transformation, never {@code null}
481         * @throws RepositoryException if the instance could not be retrieved
482         */
483        public VersionSelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
484                throws RepositoryException {
485            return this;
486        }
487
488        /**
489         * Determines the winning node among conflicting dependencies. Implementations will usually iterate
490         * {@link ConflictContext#getItems()}, inspect {@link ConflictItem#getNode()} and eventually call
491         * {@link ConflictContext#setWinner(ConflictResolver.ConflictItem)} to deliver the winner. Failure to select a
492         * winner will automatically fail the entire conflict resolution.
493         *
494         * @param context the conflict context, must not be {@code null}
495         * @throws RepositoryException if the version selection failed
496         */
497        public abstract void selectVersion(ConflictContext context) throws RepositoryException;
498    }
499
500    /**
501     * An extension point of {@link ConflictResolver} that determines the effective scope of a dependency from a
502     * potentially conflicting set of {@link ScopeDeriver derived scopes}. The scope selector gets invoked after the
503     * {@link VersionSelector} has picked the winning node.
504     * <p>
505     * <strong>Note:</strong> Implementations must be stateless.
506     */
507    public abstract static class ScopeSelector {
508
509        /**
510         * Retrieves the scope selector for use during the specified graph transformation. The conflict resolver calls
511         * this method once per
512         * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
513         * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
514         * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
515         * default implementation simply returns the current instance which is appropriate for implementations which do
516         * not require auxiliary data.
517         *
518         * @param root the root node of the (possibly cyclic!) graph to transform, must not be {@code null}
519         * @param context the graph transformation context, must not be {@code null}
520         * @return the scope selector to use for the given graph transformation, never {@code null}
521         * @throws RepositoryException if the instance could not be retrieved
522         */
523        public ScopeSelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
524                throws RepositoryException {
525            return this;
526        }
527
528        /**
529         * Determines the effective scope of the dependency given by {@link ConflictContext#getWinner()}.
530         * Implementations will usually iterate {@link ConflictContext#getItems()}, inspect
531         * {@link ConflictItem#getScopes()} and eventually call {@link ConflictContext#setScope(String)} to deliver the
532         * effective scope.
533         *
534         * @param context the conflict context, must not be {@code null}
535         * @throws RepositoryException if the scope selection failed
536         */
537        public abstract void selectScope(ConflictContext context) throws RepositoryException;
538    }
539
540    /**
541     * An extension point of {@link ConflictResolver} that determines the scope of a dependency in relation to the scope
542     * of its parent.
543     * <p>
544     * <strong>Note:</strong> Implementations must be stateless.
545     */
546    public abstract static class ScopeDeriver {
547
548        /**
549         * Retrieves the scope deriver for use during the specified graph transformation. The conflict resolver calls
550         * this method once per
551         * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
552         * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
553         * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
554         * default implementation simply returns the current instance which is appropriate for implementations which do
555         * not require auxiliary data.
556         *
557         * @param root the root node of the (possibly cyclic!) graph to transform, must not be {@code null}
558         * @param context the graph transformation context, must not be {@code null}
559         * @return the scope deriver to use for the given graph transformation, never {@code null}
560         * @throws RepositoryException if the instance could not be retrieved
561         */
562        public ScopeDeriver getInstance(DependencyNode root, DependencyGraphTransformationContext context)
563                throws RepositoryException {
564            return this;
565        }
566
567        /**
568         * Determines the scope of a dependency in relation to the scope of its parent. Implementors need to call
569         * {@link ScopeContext#setDerivedScope(String)} to deliver the result of their calculation. If said method is
570         * not invoked, the conflict resolver will assume the scope of the child dependency remains unchanged.
571         *
572         * @param context the scope context, must not be {@code null}
573         * @throws RepositoryException if the scope deriviation failed
574         */
575        public abstract void deriveScope(ScopeContext context) throws RepositoryException;
576    }
577
578    /**
579     * An extension point of {@link ConflictResolver} that determines the effective optional flag of a dependency from a
580     * potentially conflicting set of derived optionalities. The optionality selector gets invoked after the
581     * {@link VersionSelector} has picked the winning node.
582     * <p>
583     * <strong>Note:</strong> Implementations must be stateless.
584     */
585    public abstract static class OptionalitySelector {
586
587        /**
588         * Retrieves the optionality selector for use during the specified graph transformation. The conflict resolver
589         * calls this method once per
590         * {@link ConflictResolver#transformGraph(DependencyNode, DependencyGraphTransformationContext)} invocation to
591         * allow implementations to prepare any auxiliary data that is needed for their operation. Given that
592         * implementations must be stateless, a new instance needs to be returned to hold such auxiliary data. The
593         * default implementation simply returns the current instance which is appropriate for implementations which do
594         * not require auxiliary data.
595         *
596         * @param root the root node of the (possibly cyclic!) graph to transform, must not be {@code null}
597         * @param context the graph transformation context, must not be {@code null}
598         * @return the optionality selector to use for the given graph transformation, never {@code null}
599         * @throws RepositoryException if the instance could not be retrieved
600         */
601        public OptionalitySelector getInstance(DependencyNode root, DependencyGraphTransformationContext context)
602                throws RepositoryException {
603            return this;
604        }
605
606        /**
607         * Determines the effective optional flag of the dependency given by {@link ConflictContext#getWinner()}.
608         * Implementations will usually iterate {@link ConflictContext#getItems()}, inspect
609         * {@link ConflictItem#getOptionalities()} and eventually call {@link ConflictContext#setOptional(Boolean)} to
610         * deliver the effective optional flag.
611         *
612         * @param context the conflict context, must not be {@code null}
613         * @throws RepositoryException if the optionality selection failed
614         */
615        public abstract void selectOptionality(ConflictContext context) throws RepositoryException;
616    }
617}