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}