DifferentiateResult (Delta delta, DifferentiateParameters params) { Iterable<NodeSource> deltaSources = delta.getSources(); Set<NodeSource> allProcessedSources = Iterators.collect(Iterators.flat(Arrays.asList(delta.getBaseSources(), deltaSources, delta.getDeletedSources())), new HashSet<>()); Set<Node<?, ?>> nodesBefore = Iterators.collect(Iterators.flat(Iterators.map(allProcessedSources, s -> getNodes(s))), Containers.createCustomPolicySet(DiffCapable::isSame, DiffCapable::diffHashCode)); Set<Node<?, ?>> nodesAfter = Iterators.collect(Iterators.flat(Iterators.map(deltaSources, s -> delta.getNodes(s))), Containers.createCustomPolicySet(DiffCapable::isSame, DiffCapable::diffHashCode)); // do not process 'removed' per-source file. This works when a class comes from exactly one source, but might not work, if a class can be associated with several sources // better make a node-diff over all compiled sources => the sets of removed, added, deleted _nodes_ will be more accurate and reflecting reality List<Node<?, ?>> deletedNodes = Iterators.collect(Iterators.filter(nodesBefore, n -> !nodesAfter.contains(n)), new ArrayList<>()); if (!params.isCalculateAffected()) { return new DifferentiateResult() { @Override public Delta getDelta() { return delta; } @Override public Iterable<Node<?, ?>> getDeletedNodes() { return deletedNodes; } @Override public Iterable<NodeSource> getAffectedSources() { return Collections.emptyList(); } }; } var diffContext = new DifferentiateContext() { private final Predicate<Node<?, ?>> ANY_CONSTRAINT = node -> true; final Set<NodeSource> compiledSources = deltaSources instanceof Set? (Set<NodeSource>)deltaSources : Iterators.collect(deltaSources, new HashSet<>()); final Map<Usage, Predicate<Node<?, ?>>> affectedUsages = new HashMap<>(); final Set<BiPredicate<Node<?, ?>, Usage>> usageQueries = new HashSet<>(); final Set<NodeSource> affectedSources = new HashSet<>(); @Override public DifferentiateParameters getParams() { return params; } @Override public @NotNull Graph getGraph() { return DependencyGraphImpl.this; } @Override public @NotNull Delta getDelta() { return delta; } @Override public boolean isCompiled(NodeSource src) { return compiledSources.contains(src); } @Override public void affectUsage(@NotNull Usage usage) { affectedUsages.put(usage, ANY_CONSTRAINT); } @Override public void affectUsage(@NotNull Usage usage, @NotNull Predicate<Node<?, ?>> constraint) { Predicate<Node<?, ?>> prevConstraint = affectedUsages.put(usage, constraint); if (prevConstraint != null) { affectedUsages.put(usage, prevConstraint == ANY_CONSTRAINT? ANY_CONSTRAINT : prevConstraint.or(constraint)); } } @Override public void affectUsage(@NotNull BiPredicate<Node<?, ?>, Usage> usageQuery) { usageQueries.add(usageQuery); } @Override public void affectNodeSource(@NotNull NodeSource source) { affectedSources.add(source); } boolean isNodeAffected(Node<?, ?> node) { for (Usage usage : node.getUsages()) { Predicate<Node<?, ?>> constraint = affectedUsages.get(usage); if (constraint != null && constraint.test(node)) { return true; } for (BiPredicate<Node<?, ?>, Usage> query : usageQueries) { if (query.test(node, usage)) { return true; } } } return false; } }; boolean incremental = true; for (DifferentiateStrategy diffStrategy : ourDifferentiateStrategies) { if (!diffStrategy.differentiate(diffContext, nodesBefore, nodesAfter)) { incremental = false; break; } } if (!incremental) { return DifferentiateResult.createNonIncremental(delta, deletedNodes); } Set<NodeSource> affectedSources = new HashSet<>(); Set<ReferenceID> dependingOnDeleted = Iterators.collect(Iterators.flat(Iterators.map(deletedNodes, n -> getDependingNodes(n.getReferenceID()))), new HashSet<>()); for (ReferenceID dep : dependingOnDeleted) { for (NodeSource src : getSources(dep)) { affectedSources.add(src); } } Iterable<ReferenceID> changedScopeNodes = Iterators.unique(Iterators.flat(Iterators.map(nodesAfter, n -> n.getReferenceID()), Iterators.map(diffContext.affectedUsages.keySet(), u -> u.getElementOwner()))); for (ReferenceID dependent : Iterators.unique(Iterators.filter(Iterators.flat(Iterators.map(changedScopeNodes, id -> getDependingNodes(id))), id -> !dependingOnDeleted.contains(id)))) { for (NodeSource depSrc : getSources(dependent)) { if (!affectedSources.contains(depSrc)) { boolean affectSource = false; for (var depNode : getNodes(depSrc)) { if (diffContext.isNodeAffected(depNode)) { affectSource = true; for (DifferentiateStrategy strategy : ourDifferentiateStrategies) { if (!strategy.isIncremental(diffContext, depNode)) { return DifferentiateResult.createNonIncremental(delta, deletedNodes); } } } } if (affectSource) { affectedSources.add(depSrc); } } } } // do not include sources that were already compiled affectedSources.removeAll(allProcessedSources); // ensure sources explicitly marked by strategies are affected, even if these sources were compiled initially affectedSources.addAll(diffContext.affectedSources); return new DifferentiateResult() { @Override public Delta getDelta() { return delta; } @Override public Iterable<Node<?, ?>> getDeletedNodes() { return deletedNodes; } @Override public Iterable<NodeSource> getAffectedSources() { return affectedSources; } }; }