package jnpf.elnService.formula; import jnpf.elnEntity.formula.ElnTemplateFormulaBindingEntity; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Set; public class FormulaDependencyGraph { public Result validate(Collection bindings) { Map nodes = new LinkedHashMap<>(); Map resultOwners = new HashMap<>(); for (ElnTemplateFormulaBindingEntity item : bindings == null ? Collections.emptyList() : bindings) { if (item != null && "ACTIVE".equals(item.getBangdingZhuangtai())) { nodes.put(item.getId(), item); resultOwners.put(item.getJieguoBangdingId(), item.getId()); } } Map> edges = new LinkedHashMap<>(); Map indegrees = new LinkedHashMap<>(); for (String id : nodes.keySet()) { edges.put(id, new LinkedHashSet<>()); indegrees.put(id, 0); } for (ElnTemplateFormulaBindingEntity consumer : nodes.values()) { for (String fieldBindingId : safeValues(consumer.getCanshuYingshe())) { String producer = resultOwners.get(fieldBindingId); if (producer != null && edges.get(producer).add(consumer.getId())) { indegrees.put(consumer.getId(), indegrees.get(consumer.getId()) + 1); } } } PriorityQueue ready = new PriorityQueue<>(); indegrees.forEach((id, degree) -> { if (degree == 0) ready.add(id); }); List order = new ArrayList<>(); while (!ready.isEmpty()) { String current = ready.poll(); order.add(current); List next = new ArrayList<>(edges.get(current)); next.sort(Comparator.naturalOrder()); for (String target : next) { int degree = indegrees.get(target) - 1; indegrees.put(target, degree); if (degree == 0) ready.add(target); } } if (order.size() == nodes.size()) return Result.valid(order); return Result.cycle(findCycle(edges)); } private static Collection safeValues(Map values) { return values == null ? Collections.emptyList() : values.values(); } private static List findCycle(Map> edges) { Map colors = new HashMap<>(); List stack = new ArrayList<>(); List ids = new ArrayList<>(edges.keySet()); Collections.sort(ids); for (String id : ids) { List cycle = dfs(id, edges, colors, stack); if (!cycle.isEmpty()) return cycle; } return Collections.emptyList(); } private static List dfs(String id, Map> edges, Map colors, List stack) { int color = colors.getOrDefault(id, 0); if (color == 2) return Collections.emptyList(); if (color == 1) { int start = stack.indexOf(id); List cycle = new ArrayList<>(stack.subList(start, stack.size())); cycle.add(id); return cycle; } colors.put(id, 1); stack.add(id); List targets = new ArrayList<>(edges.getOrDefault(id, Collections.emptySet())); Collections.sort(targets); for (String target : targets) { List cycle = dfs(target, edges, colors, stack); if (!cycle.isEmpty()) return cycle; } stack.remove(stack.size() - 1); colors.put(id, 2); return Collections.emptyList(); } public static final class Result { private final List topologicalOrder; private final List cyclePath; private Result(List order, List cycle) { this.topologicalOrder = Collections.unmodifiableList(new ArrayList<>(order)); this.cyclePath = Collections.unmodifiableList(new ArrayList<>(cycle)); } static Result valid(List order) { return new Result(order, Collections.emptyList()); } static Result cycle(List cycle) { return new Result(Collections.emptyList(), cycle); } public boolean isValid() { return cyclePath.isEmpty(); } public List getTopologicalOrder() { return topologicalOrder; } public List getCyclePath() { return cyclePath; } } }