/** * Copyright (c) 2015 committers of YAKINDU and others. * 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: * committers of YAKINDU - initial API and implementation * */ package org.yakindu.sct.model.sgraph.validation; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** This class implements a generic depth first search algorithm, * which can be applied to any structure which can be interpreted as a * graph. Since the interpretation of an application specific structure as a graph can't be * done by this algorithm implementation, you must implement a subclass * . * The current implementation only allows to perform the algorithm once. * * @author Axel Terfloth */ @SuppressWarnings("all") abstract public class DFS { /** Indicates whether the algorithm has been performed or not.*/ private boolean done; /** A map of all visited elements. */ private Map myVisitedElements; /** An iterator of root elements */ protected Iterator myElements; /** The number of traversed links. */ public int linkCount; /** Default algorithm constructor. */ public DFS() { done = false; myVisitedElements = new HashMap(); myElements = null; linkCount = 0; } /** Performs the depth first traversion of all elements */ private synchronized void perform() { if (done) return; while (myElements.hasNext()) { Object element = myElements.next(); if (!isVisited(element)) { visit(element, 1); } } } /** Performs the graph traversion with all elements which are contained in the * specified collection. This means that every element of this collection and all * elements which are reachable from these elements will be visited. */ public synchronized void perform(Collection collection) { this.perform(collection.iterator()); } /** Performs the graph traversion with all elements which are contained in the * specified iterator. This means that every element of this collection and all * elements which are reachable from these elements will be visited. */ public synchronized void perform(Iterator iterator) { myElements = iterator; this.perform(); } /** Performs the graph traversion with a single root element. So this root element and * all elements which are reachable from this root elements will be visited. */ public synchronized void perform(Object rootElement) { List list = new ArrayList(); list.add(rootElement); this.perform(list.iterator()); } /** Visits an element. */ private int visit(Object element, int depth) { int minDepth = depth; if (isVisited(element)) { return getVisitedDepth(element); } setVisited(element, depth); beginVisit(element, depth); Iterator itr = getElementLinks(element); if (itr != null) { while (itr.hasNext()) { int childDepth = visit(itr.next(), depth + 1); minDepth = ((childDepth < minDepth) && (childDepth > -1)) ? childDepth : minDepth; linkCount++; } } setVisited(element, -1); endVisit(element, depth, minDepth); return (minDepth < depth) ? minDepth : depth; } /** This hook method will be invoked when the DFS begins the visit of an element. * Since there is no return parameter this method will not effect the algorithms * behaviour. * * @param element The element wich will be visited. * @param depth The distance to a root element of the graph traversion. */ public void beginVisit(Object element, int depth) { } /** This hook method will be invoked when the DFS finishes the visit of an element. * Since there is no return parameter this method will not effect the algorithms * behaviour. * * @param element The element wich will be visited. * @param depth The distance to a root element of the graph traversion. * @param minDepth If this parameter is smaller than the depth parameter then * an element is reachable from the current element from which the current element is * reachable, which means there is a reachablitiy cycle within the graph. */ public void endVisit(Object element, int depth, int minDepth) { } /** This hook method will be invoked to provide an iterator of off elements * which are directly reachable from the current element. * * Subclasses must overwrite this method. * * @param element The current element. * @return An iterator of directly reachable elements. */ public abstract Iterator getElementLinks(Object element); /** Returns true if this element was already visited. */ public boolean isVisited(Object element) { return myVisitedElements.containsKey(element); } /** */ private void setVisited(Object element, int depth) { myVisitedElements.put(element, new Integer(depth)); } /** Returns the path depth number if the element is in the current path or -1 if it isn't.*/ public int getVisitedDepth(Object element) { if (myVisitedElements.containsKey(element)) { return ((Integer) myVisitedElements.get(element)).intValue(); } return -1; } }