in maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/ConflictResolver.java [181:275]
public DependencyNode transformGraph(DependencyNode node, DependencyGraphTransformationContext context)
throws RepositoryException {
requireNonNull(node, "node cannot be null");
requireNonNull(context, "context cannot be null");
List<?> sortedConflictIds = (List<?>) context.get(TransformationContextKeys.SORTED_CONFLICT_IDS);
if (sortedConflictIds == null) {
ConflictIdSorter sorter = new ConflictIdSorter();
sorter.transformGraph(node, context);
sortedConflictIds = (List<?>) context.get(TransformationContextKeys.SORTED_CONFLICT_IDS);
}
@SuppressWarnings("unchecked")
Map<String, Object> stats = (Map<String, Object>) context.get(TransformationContextKeys.STATS);
long time1 = System.nanoTime();
@SuppressWarnings("unchecked")
Collection<Collection<?>> conflictIdCycles =
(Collection<Collection<?>>) context.get(TransformationContextKeys.CYCLIC_CONFLICT_IDS);
if (conflictIdCycles == null) {
throw new RepositoryException("conflict id cycles have not been identified");
}
Map<?, ?> conflictIds = (Map<?, ?>) context.get(TransformationContextKeys.CONFLICT_IDS);
if (conflictIds == null) {
throw new RepositoryException("conflict groups have not been identified");
}
Map<Object, Collection<Object>> cyclicPredecessors = new HashMap<>();
for (Collection<?> cycle : conflictIdCycles) {
for (Object conflictId : cycle) {
Collection<Object> predecessors = cyclicPredecessors.computeIfAbsent(conflictId, k -> new HashSet<>());
predecessors.addAll(cycle);
}
}
State state = new State(node, conflictIds, sortedConflictIds.size(), context);
for (Iterator<?> it = sortedConflictIds.iterator(); it.hasNext(); ) {
Object conflictId = it.next();
// reset data structures for next graph walk
state.prepare(conflictId, cyclicPredecessors.get(conflictId));
// find nodes with the current conflict id and while walking the graph (more deeply), nuke leftover losers
gatherConflictItems(node, state);
// now that we know the min depth of the parents, update depth of conflict items
state.finish();
// earlier runs might have nuked all parents of the current conflict id, so it might not exist anymore
if (!state.items.isEmpty()) {
ConflictContext ctx = state.conflictCtx;
state.versionSelector.selectVersion(ctx);
if (ctx.winner == null) {
throw new RepositoryException("conflict resolver did not select winner among " + state.items);
}
DependencyNode winner = ctx.winner.node;
state.scopeSelector.selectScope(ctx);
if (Verbosity.NONE != state.verbosity) {
winner.setData(
NODE_DATA_ORIGINAL_SCOPE, winner.getDependency().getScope());
}
winner.setScope(ctx.scope);
state.optionalitySelector.selectOptionality(ctx);
if (Verbosity.NONE != state.verbosity) {
winner.setData(
NODE_DATA_ORIGINAL_OPTIONALITY,
winner.getDependency().isOptional());
}
winner.setOptional(ctx.optional);
removeLosers(state);
}
// record the winner so we can detect leftover losers during future graph walks
state.winner();
// in case of cycles, trigger final graph walk to ensure all leftover losers are gone
if (!it.hasNext() && !conflictIdCycles.isEmpty() && state.conflictCtx.winner != null) {
DependencyNode winner = state.conflictCtx.winner.node;
state.prepare(state, null);
gatherConflictItems(winner, state);
}
}
if (stats != null) {
long time2 = System.nanoTime();
stats.put("ConflictResolver.totalTime", time2 - time1);
stats.put("ConflictResolver.conflictItemCount", state.totalConflictItems);
}
return node;
}