/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.graph.dhns.core; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.gephi.data.attributes.api.AttributeController; import org.gephi.data.attributes.api.AttributeModel; import org.gephi.data.attributes.api.AttributeRowFactory; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphListener; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphSettings; import org.gephi.graph.api.HierarchicalDirectedGraph; import org.gephi.graph.api.HierarchicalGraph; import org.gephi.graph.api.HierarchicalMixedGraph; import org.gephi.graph.api.HierarchicalUndirectedGraph; import org.gephi.graph.api.MixedGraph; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.UndirectedGraph; import org.gephi.graph.api.GraphView; import org.gephi.graph.dhns.DhnsGraphController; import org.gephi.graph.dhns.edge.AbstractEdge; import org.gephi.graph.dhns.edge.iterators.AbstractEdgeIterator; import org.gephi.graph.dhns.graph.HierarchicalDirectedGraphImpl; import org.gephi.graph.dhns.graph.HierarchicalGraphImpl; import org.gephi.graph.dhns.graph.HierarchicalMixedGraphImpl; import org.gephi.graph.dhns.graph.HierarchicalUndirectedGraphImpl; import org.gephi.graph.dhns.graph.iterators.EdgeIterableImpl; import org.gephi.graph.dhns.graph.iterators.NodeIterableImpl; import org.gephi.graph.dhns.node.iterators.AbstractNodeIterator; import org.gephi.graph.dhns.predicate.Predicate; import org.gephi.project.api.Workspace; import org.openide.util.Lookup; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Main class of the DHNS (Durable Hierarchical Network Structure) graph structure.. * * @author Mathieu Bastian */ public class Dhns implements GraphModel { //Core private final Workspace workspace; private final DhnsGraphController controller; private GraphStructure graphStructure; private GraphVersion graphVersion; private final EventManager eventManager; private final SettingsManager settingsManager; private final GraphFactoryImpl factory; private final DuplicateManager duplicateManager; //Type private boolean directed = false; private boolean undirected = false; private boolean mixed = false; //Locking private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public Dhns(DhnsGraphController controller, Workspace workspace) { this.controller = controller; this.workspace = workspace; graphVersion = new GraphVersion(); eventManager = new EventManager(this); settingsManager = new SettingsManager(this); graphStructure = new GraphStructure(this); duplicateManager = new DuplicateManager(this); eventManager.start(); //AttributeFactory AttributeRowFactory attributeRowFactory = null; if (workspace != null) { AttributeModel attributeModel = Lookup.getDefault().lookup(AttributeController.class).getModel(workspace); if (attributeModel != null) { attributeRowFactory = attributeModel.rowFactory(); } } factory = new GraphFactoryImpl(controller.getIDGen(), attributeRowFactory); init(); } public void init() { } public DhnsGraphController getController() { return controller; } public GraphStructure getGraphStructure() { return graphStructure; } public GraphVersion getGraphVersion() { return graphVersion; } public EventManager getEventManager() { return eventManager; } public IDGen getIdGen() { return controller.getIDGen(); } public SettingsManager getSettingsManager() { return settingsManager; } public DuplicateManager getDuplicateManager() { return duplicateManager; } public NodeIterable newNodeIterable(AbstractNodeIterator iterator) { return new NodeIterableImpl(iterator, readWriteLock.readLock()); } public EdgeIterable newEdgeIterable(AbstractEdgeIterator iterator) { return new EdgeIterableImpl(iterator, readWriteLock.readLock()); } public NodeIterable newNodeIterable(AbstractNodeIterator iterator, Predicate<Node> predicate) { return new NodeIterableImpl(iterator, readWriteLock.readLock()); } public EdgeIterable newEdgeIterable(AbstractEdgeIterator iterator, Predicate<AbstractEdge> predicate) { return new EdgeIterableImpl(iterator, readWriteLock.readLock(), predicate); } //Locking public void readLock() { /*if (SwingUtilities.isEventDispatchThread()) { Throwable r = new RuntimeException(); int i = 0; for (i = 0; i < r.getStackTrace().length; i++) { if (!r.getStackTrace()[i].toString().startsWith("org.gephi.graph.dhns")) { break; } } System.err.println("WARNING: readLock() on the EDT - " + r.getStackTrace()[i].toString()); }*/ //String t = Thread.currentThread().toString(); //Logger.getLogger("").log(Level.WARNING, "{0} read lock", Thread.currentThread()); readWriteLock.readLock().lock(); } public void readUnlock() { readWriteLock.readLock().unlock(); } public void readUnlockAll() { ReentrantReadWriteLock lock = readWriteLock; final int nReadLocks = lock.getReadHoldCount(); for (int n = 0; n < nReadLocks; n++) { lock.readLock().unlock(); } } public void writeLock() { if (readWriteLock.getReadHoldCount() > 0) { throw new IllegalMonitorStateException("Impossible to acquire a write lock when currently holding a read lock. Use toArray() methods on NodeIterable and EdgeIterable to avoid holding a readLock."); } /*if (SwingUtilities.isEventDispatchThread()) { Throwable r = new RuntimeException(); int i = 0; for (i = 0; i < r.getStackTrace().length; i++) { if (!r.getStackTrace()[i].toString().startsWith("org.gephi.graph.dhns")) { break; } } System.err.println("WARNING: writeLock() on the EDT - " + r.getStackTrace()[i].toString()); }*/ //Logger.getLogger("").log(Level.WARNING, "{0} write lock", Thread.currentThread()); readWriteLock.writeLock().lock(); } public void writeUnlock() { //Logger.getLogger("").log(Level.WARNING, "{0} write unlock", Thread.currentThread()); readWriteLock.writeLock().unlock(); } public ReentrantReadWriteLock getReadWriteLock() { return readWriteLock; } //Type public void touchDirected() { if (undirected || mixed) { touchMixed(); } else { directed = true; } } public void touchUndirected() { if (directed || mixed) { touchMixed(); } else { undirected = true; } } public void touchMixed() { directed = false; undirected = false; mixed = true; } //API public GraphFactoryImpl factory() { return factory; } public boolean isDirected() { return directed; } public boolean isMixed() { return mixed; } public boolean isUndirected() { return undirected; } public void setDirected(boolean directed) { this.directed = directed; } public void setUndirected(boolean undirected) { this.undirected = undirected; } public void setMixed(boolean mixed) { this.mixed = mixed; } public boolean isHierarchical() { return graphStructure.getMainView().getStructure().getTreeHeight() - 1 > 0; //height>0 } public void addGraphListener(GraphListener graphListener) { eventManager.addGraphListener(graphListener); } public void removeGraphListener(GraphListener graphListener) { eventManager.removeGraphListener(graphListener); } public Graph getGraph() { if (directed) { return getDirectedGraph(); } else if (undirected) { return getUndirectedGraph(); } else if (mixed) { return getMixedGraph(); } else { return getDirectedGraph(); } } public DirectedGraph getDirectedGraph() { return new HierarchicalDirectedGraphImpl(this, graphStructure.getMainView()); } public UndirectedGraph getUndirectedGraph() { return new HierarchicalUndirectedGraphImpl(this, graphStructure.getMainView()); } public MixedGraph getMixedGraph() { return new HierarchicalMixedGraphImpl(this, graphStructure.getMainView()); } public HierarchicalGraph getHierarchicalGraph() { if (directed) { return getHierarchicalDirectedGraph(); } else if (undirected) { return getHierarchicalUndirectedGraph(); } else if (mixed) { return getHierarchicalMixedGraph(); } else { return getHierarchicalDirectedGraph(); } } public HierarchicalDirectedGraph getHierarchicalDirectedGraph() { return new HierarchicalDirectedGraphImpl(this, graphStructure.getMainView()); } public HierarchicalMixedGraph getHierarchicalMixedGraph() { return new HierarchicalMixedGraphImpl(this, graphStructure.getMainView()); } public HierarchicalUndirectedGraph getHierarchicalUndirectedGraph() { return new HierarchicalUndirectedGraphImpl(this, graphStructure.getMainView()); } public DirectedGraph getDirectedGraph(GraphView view) { return new HierarchicalDirectedGraphImpl(this, (GraphViewImpl) view); } public Graph getGraph(GraphView view) { if (directed) { return getDirectedGraph(view); } else if (undirected) { return getUndirectedGraph(view); } else if (mixed) { return getMixedGraph(view); } else { return getDirectedGraph(view); } } public HierarchicalDirectedGraph getHierarchicalDirectedGraph(GraphView view) { return new HierarchicalDirectedGraphImpl(this, (GraphViewImpl) view); } public HierarchicalGraph getHierarchicalGraph(GraphView view) { if (directed) { return getHierarchicalDirectedGraph(view); } else if (undirected) { return getHierarchicalUndirectedGraph(view); } else if (mixed) { return getHierarchicalMixedGraph(view); } else { return getHierarchicalDirectedGraph(view); } } public HierarchicalMixedGraph getHierarchicalMixedGraph(GraphView view) { return new HierarchicalMixedGraphImpl(this, (GraphViewImpl) view); } public HierarchicalUndirectedGraph getHierarchicalUndirectedGraph(GraphView view) { return new HierarchicalUndirectedGraphImpl(this, (GraphViewImpl) view); } public MixedGraph getMixedGraph(GraphView view) { return new HierarchicalMixedGraphImpl(this, (GraphViewImpl) view); } public UndirectedGraph getUndirectedGraph(GraphView view) { return new HierarchicalUndirectedGraphImpl(this, (GraphViewImpl) view); } public Graph getGraphVisible() { if (directed) { return getDirectedGraph(graphStructure.getVisibleView()); } else if (undirected) { return getUndirectedGraph(graphStructure.getVisibleView()); } else if (mixed) { return getMixedGraph(graphStructure.getVisibleView()); } else { return getDirectedGraph(graphStructure.getVisibleView()); } } public DirectedGraph getDirectedGraphVisible() { return getDirectedGraph(graphStructure.getVisibleView()); } public UndirectedGraph getUndirectedGraphVisible() { return getUndirectedGraph(graphStructure.getVisibleView()); } public MixedGraph getMixedGraphVisible() { return getMixedGraph(graphStructure.getVisibleView()); } public HierarchicalGraph getHierarchicalGraphVisible() { if (directed) { return getHierarchicalDirectedGraph(graphStructure.getVisibleView()); } else if (undirected) { return getHierarchicalUndirectedGraph(graphStructure.getVisibleView()); } else if (mixed) { return getHierarchicalMixedGraph(graphStructure.getVisibleView()); } else { return getHierarchicalDirectedGraph(graphStructure.getVisibleView()); } } public HierarchicalDirectedGraph getHierarchicalDirectedGraphVisible() { return getHierarchicalDirectedGraph(graphStructure.getVisibleView()); } public HierarchicalMixedGraph getHierarchicalMixedGraphVisible() { return getHierarchicalMixedGraph(graphStructure.getVisibleView()); } public HierarchicalUndirectedGraph getHierarchicalUndirectedGraphVisible() { return getHierarchicalUndirectedGraph(graphStructure.getVisibleView()); } public GraphSettings settings() { return settingsManager; } public void pushFrom(Graph graph) { if (graph == null) { throw new NullPointerException(); } HierarchicalGraphImpl graphImpl = (HierarchicalGraphImpl) graph; if (graphImpl.getGraphModel() == this) { throw new IllegalArgumentException("The graph must be from a different Workspace"); } Dhns source = (Dhns) graphImpl.getGraphModel(); source.getDuplicateManager().duplicate(this, (GraphViewImpl) graphImpl.getView()); graphVersion.incNodeAndEdgeVersion(); // eventManager.fireEvent(EventType.NODES_AND_EDGES_UPDATED); } public void pushNodes(Graph graph, Node[] nodes) { if (graph == null) { throw new NullPointerException(); } HierarchicalGraphImpl graphImpl = (HierarchicalGraphImpl) graph; if (graphImpl.getGraphModel() == this) { throw new IllegalArgumentException("The graph must be from a different Workspace"); } Dhns source = (Dhns) graphImpl.getGraphModel(); source.getDuplicateManager().duplicateNodes(this, nodes); graphVersion.incNodeAndEdgeVersion(); } public void clear() { graphVersion = new GraphVersion(); graphStructure = new GraphStructure(this); } public void readXML(Element element) { } public Element writeXML(Document document) { return null; } public GraphModel copy() { return null; } public GraphView newView() { return graphStructure.getNewView(); } public GraphView copyView(GraphView view) { return graphStructure.copyView((GraphViewImpl) view); } public void destroyView(GraphView view) { graphStructure.destroyView((GraphViewImpl) view); } public void setVisibleView(GraphView view) { graphStructure.setVisibleView(view != null ? (GraphViewImpl) view : null); } public GraphView getVisibleView() { return graphStructure.getVisibleView(); } public Workspace getWorkspace() { return workspace; } }