/******************************************************************************* * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * The Chisel Group, University of Victoria *******************************************************************************/ package org.eclipse.zest.layouts.algorithms.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import org.eclipse.zest.layouts.LayoutEntity; import org.eclipse.zest.layouts.LayoutRelationship; import org.eclipse.zest.layouts.algorithms.AbstractLayoutAlgorithm; import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; /** * Checks for cycles in the given graph. * * @author Casey Best */ public class CycleChecker { /** * Tests if there is a directed cirlce in the graph formed by the given entities and relationships. * @param entities The entities in the graph to check * @param relationships The relationships in the graph to check * @param cycle Populated with the cycle encountered, if there is one. * @throws RuntimeException Thrown if entities doesn't contain all of the endpoints for each relationship in relationships * @return <code>true</code> if there is a directed circle. * Otherwise, <code>false</code>. */ public static boolean hasDirectedCircles(LayoutEntity[] entities, LayoutRelationship[] relationships, List cycle) { if (!AbstractLayoutAlgorithm.verifyInput(entities, relationships)) { throw new RuntimeException ("The endpoints of the relationships aren't contained in the entities list."); } //Enumeration enum; //Iterator iterator; Hashtable endPoints = new Hashtable(); // Initialize the relation(transitive) vector. for (int i = 0; i < relationships.length; i++) { LayoutRelationship rel = relationships[i]; //Add the relationship to the source endpoint Object subject = rel.getSourceInLayout(); List rels = (List) endPoints.get(subject); if (rels == null) { rels = new ArrayList(); endPoints.put(subject, rels); } if (!rels.contains(rel)) rels.add(rel); } boolean hasCyle = hasCycle(new ArrayList(Arrays.asList(entities)), endPoints, cycle); return hasCyle; } /** * Check passed in nodes for a cycle */ private static boolean hasCycle(List nodesToCheck, Hashtable endPoints, List cycle) { while (nodesToCheck.size() > 0) { Object checkNode = nodesToCheck.get(0); List checkedNodes = new ArrayList(); if (hasCycle(checkNode, new ArrayList(), null, endPoints, checkedNodes, cycle)) { return true; } nodesToCheck.removeAll(checkedNodes); } return false; } /** * Checks all the nodes attached to the nodeToCheck node for a cycle. All nodes * checked are placed in nodePathSoFar. * * @returns true if there is a cycle */ private static boolean hasCycle(Object nodeToCheck, List nodePathSoFar, SimpleRelationship cameFrom, Hashtable endPoints, List nodesChecked, List cycle) { if (nodePathSoFar.contains(nodeToCheck)) { cycle.addAll(nodePathSoFar); cycle.add(nodeToCheck); return true; } nodePathSoFar.add(nodeToCheck); nodesChecked.add(nodeToCheck); List relations = (List) endPoints.get(nodeToCheck); if (relations != null) { for (Iterator iter = relations.iterator(); iter.hasNext();) { SimpleRelationship rel = (SimpleRelationship) iter.next(); if (cameFrom == null || !rel.equals(cameFrom)) { Object currentNode = null; currentNode = rel.getDestinationInLayout(); if (hasCycle(currentNode, nodePathSoFar, rel, endPoints, nodesChecked, cycle)) { return true; } } } } nodePathSoFar.remove(nodeToCheck); return false; } }