/******************************************************************************* * This file is part of logisim-evolution. * * logisim-evolution is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * logisim-evolution is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>. * * Original code by Carl Burch (http://www.cburch.com), 2011. * Subsequent modifications by : * + Haute École Spécialisée Bernoise * http://www.bfh.ch * + Haute École du paysage, d'ingénierie et d'architecture de Genève * http://hepia.hesge.ch/ * + Haute École d'Ingénierie et de Gestion du Canton de Vaud * http://www.heig-vd.ch/ * The project is currently maintained by : * + REDS Institute - HEIG-VD * Yverdon-les-Bains, Switzerland * http://reds.heig-vd.ch *******************************************************************************/ package com.cburch.logisim.util; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; public class Dag { private static class Node { @SuppressWarnings("unused") Object data; HashSet<Node> succs = new HashSet<Node>(); // of Nodes int numPreds = 0; boolean mark; Node(Object data) { this.data = data; } } private HashMap<Object, Node> nodes = new HashMap<Object, Node>(); public Dag() { } public boolean addEdge(Object srcData, Object dstData) { if (!canFollow(dstData, srcData)) return false; Node src = createNode(srcData); Node dst = createNode(dstData); if (src.succs.add(dst)) ++dst.numPreds; // add since not already present return true; } private boolean canFollow(Node query, Node base) { if (base == query) return false; // mark all as unvisited for (Node n : nodes.values()) { n.mark = false; // will become true once reached } // Search starting at query: If base is found, then it follows // the query already, and so query cannot follow base. LinkedList<Node> fringe = new LinkedList<Node>(); fringe.add(query); while (!fringe.isEmpty()) { Node n = fringe.removeFirst(); for (Node next : n.succs) { if (!next.mark) { if (next == base) return false; next.mark = true; fringe.addLast(next); } } } return true; } public boolean canFollow(Object query, Object base) { Node queryNode = findNode(query); Node baseNode = findNode(base); if (baseNode == null || queryNode == null) { return !base.equals(query); } else { return canFollow(queryNode, baseNode); } } private Node createNode(Object data) { Node ret = findNode(data); if (ret != null) return ret; if (data == null) return null; ret = new Node(data); nodes.put(data, ret); return ret; } private Node findNode(Object data) { if (data == null) return null; return nodes.get(data); } public boolean hasPredecessors(Object data) { Node from = findNode(data); return from != null && from.numPreds != 0; } public boolean hasSuccessors(Object data) { Node to = findNode(data); return to != null && !to.succs.isEmpty(); } public boolean removeEdge(Object srcData, Object dstData) { // returns true if the edge could be removed Node src = findNode(srcData); Node dst = findNode(dstData); if (src == null || dst == null) return false; if (!src.succs.remove(dst)) return false; --dst.numPreds; if (dst.numPreds == 0 && dst.succs.isEmpty()) nodes.remove(dstData); if (src.numPreds == 0 && src.succs.isEmpty()) nodes.remove(srcData); return true; } public void removeNode(Object data) { Node n = findNode(data); if (n == null) return; for (Iterator<Node> it = n.succs.iterator(); it.hasNext();) { Node succ = it.next(); --(succ.numPreds); if (succ.numPreds == 0 && succ.succs.isEmpty()) it.remove(); } if (n.numPreds > 0) { for (Iterator<Node> it = nodes.values().iterator(); it.hasNext();) { Node q = it.next(); if (q.succs.remove(n) && q.numPreds == 0 && q.succs.isEmpty()) it.remove(); } } } }