/******************************************************************************* * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro * 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: * Tamas Szabo - initial API and implementation *******************************************************************************/ package org.eclipse.incquery.runtime.base.itc.alg.counting; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.incquery.runtime.base.itc.alg.misc.ITcRelation; import org.eclipse.incquery.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; import org.eclipse.incquery.runtime.base.itc.igraph.IBiDirectionalWrapper; import org.eclipse.incquery.runtime.base.itc.igraph.IGraphDataSource; import org.eclipse.incquery.runtime.base.itc.igraph.IGraphObserver; import org.eclipse.incquery.runtime.base.itc.igraph.ITcDataSource; import org.eclipse.incquery.runtime.base.itc.igraph.ITcObserver; /** * This class is the optimized implementation of the Counting algorithm. * * @author Tamas Szabo * * @param <V> * the type parameter of the nodes in the graph data source */ public class CountingAlg<V> implements IGraphObserver<V>, ITcDataSource<V> { private static final long serialVersionUID = -2383210800242398869L; private CountingTcRelation<V> tc = null; private CountingTcRelation<V> dtc = null; private IBiDirectionalGraphDataSource<V> gds = null; private ArrayList<ITcObserver<V>> observers; /** * Constructs a new Counting algorithm and initializes the transitive closure relation with the given graph data * source. Attach itself on the graph data source as an observer. * * @param gds * the graph data source instance */ public CountingAlg(IGraphDataSource<V> gds) { if (gds instanceof IBiDirectionalGraphDataSource<?>) { this.gds = (IBiDirectionalGraphDataSource<V>) gds; } else { this.gds = new IBiDirectionalWrapper<V>(gds); } observers = new ArrayList<ITcObserver<V>>(); tc = new CountingTcRelation<V>(true); dtc = new CountingTcRelation<V>(false); initTc(); gds.attachObserver(this); } /** * Initializes the transitive closure relation. */ private void initTc() { this.setTcRelation(CountingTcRelation.createFrom(gds)); } @Override public void edgeInserted(V source, V target) { if (!source.equals(target)) { deriveTc(source, target, 1); } } @Override public void edgeDeleted(V source, V target) { if (!source.equals(target)) { deriveTc(source, target, -1); } } @Override public void nodeInserted(V n) { } @Override public void nodeDeleted(V n) { this.tc.deleteTupleEnd(n); } /** * Derives the transitive closure relation when an edge is inserted or deleted. * * @param source * the source of the edge * @param target * the target of the edge * @param dCount * the value is -1 if an edge was deleted and +1 if an edge was inserted */ private void deriveTc(V source, V target, int dCount) { // if (dCount == 1 && isReachable(target, source)) { // System.out.println("The graph contains cycle with (" + source + ","+ target + ") edge!"); // } dtc.clear(); Set<V> tupEnds = null; // 1. d(tc(x,y)) :- d(l(x,y)) if (tc.addTuple(source, target, dCount)) { dtc.addTuple(source, target, dCount); notifyTcObservers(source, target, dCount); } // 2. d(tc(x,y)) :- d(l(x,z)) & tc(z,y) tupEnds = tc.getTupleEnds(target); if (tupEnds != null) { for (V tupEnd : tupEnds) { if (!tupEnd.equals(source)) { if (tc.addTuple(source, tupEnd, dCount)) { dtc.addTuple(source, tupEnd, dCount); notifyTcObservers(source, tupEnd, dCount); } } } } // 3. d(tc(x,y)) :- lv(x,z) & d(tc(z,y)) CountingTcRelation<V> newTuples = new CountingTcRelation<V>(false); CountingTcRelation<V> tmp = null; List<V> nodes = null; newTuples.union(dtc); while (!newTuples.isEmpty()) { tmp = dtc; dtc = newTuples; newTuples = tmp; newTuples.clear(); for (V tS : dtc.getTupleStarts()) { nodes = gds.getSourceNodes(tS); if (nodes != null) { for (V nS : nodes) { tupEnds = dtc.getTupleEnds(tS); if (tupEnds != null) { for (V tT : tupEnds) { if (!nS.equals(tT)) { if (tc.addTuple(nS, tT, dCount)) { newTuples.addTuple(nS, tT, dCount); notifyTcObservers(nS, tT, dCount); } } } } } } } } // System.out.println(tc); } public ITcRelation<V> getTcRelation() { return this.tc; } public void setTcRelation(CountingTcRelation<V> tc) { this.tc = tc; } @Override public boolean isReachable(V source, V target) { return tc.containsTuple(source, target); } @Override public void attachObserver(ITcObserver<V> to) { this.observers.add(to); } @Override public void detachObserver(ITcObserver<V> to) { this.observers.remove(to); } @Override public Set<V> getAllReachableTargets(V source) { Set<V> targets = new HashSet<V>(); if (tc.getTupleEnds(source) != null) { targets.addAll(tc.getTupleEnds(source)); } return targets; } @Override public Set<V> getAllReachableSources(V target) { Set<V> sources = new HashSet<V>(); if (tc.getTupleStarts(target) != null) { sources.addAll(tc.getTupleStarts(target)); } return sources; } private void notifyTcObservers(V source, V target, int dir) { for (ITcObserver<V> o : observers) { if (dir == 1) o.tupleInserted(source, target); if (dir == -1) o.tupleDeleted(source, target); } } @Override public void dispose() { tc.clear(); dtc.clear(); this.gds.detachObserver(this); } }