From b9a9255ec7a865bfabfd4078ec10d32c720bd27b Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Sun, 10 Apr 2016 13:35:50 +0700 Subject: [PATCH 01/15] added #HierarchicalGraphView api and unit tests --- .../java/org/gephi/graph/api/GraphModel.java | 12 +- .../graph/api/HierarchicalGraphView.java | 25 + .../graph/api/HierarchicalNodeGroup.java | 54 + .../gephi/graph/impl/AbstractGraphView.java | 105 ++ .../org/gephi/graph/impl/GraphModelImpl.java | 6 + .../java/org/gephi/graph/impl/GraphStore.java | 50 +- .../gephi/graph/impl/GraphViewDecorator.java | 13 +- .../org/gephi/graph/impl/GraphViewImpl.java | 125 +- .../org/gephi/graph/impl/GraphViewStore.java | 114 +- .../impl/HierarchicalGraphDecorator.java | 1574 +++++++++++++++++ .../graph/impl/HierarchicalGraphViewImpl.java | 513 ++++++ .../org/gephi/graph/impl/Serialization.java | 8 +- .../gephi/graph/impl/GraphViewStoreTest.java | 4 +- .../graph/impl/HierarchicalGraphTest.java | 115 ++ 14 files changed, 2576 insertions(+), 142 deletions(-) create mode 100644 store/src/main/java/org/gephi/graph/api/HierarchicalGraphView.java create mode 100644 store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java create mode 100644 store/src/main/java/org/gephi/graph/impl/AbstractGraphView.java create mode 100644 store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java create mode 100644 store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java create mode 100644 store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java diff --git a/store/src/main/java/org/gephi/graph/api/GraphModel.java b/store/src/main/java/org/gephi/graph/api/GraphModel.java index ff2ea072..b0311904 100644 --- a/store/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/store/src/main/java/org/gephi/graph/api/GraphModel.java @@ -15,11 +15,12 @@ */ package org.gephi.graph.api; +import org.gephi.graph.impl.GraphModelImpl; +import org.joda.time.DateTimeZone; + import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import org.gephi.graph.impl.GraphModelImpl; -import org.joda.time.DateTimeZone; /** * Graph API's entry point. @@ -351,6 +352,13 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce */ public GraphView createView(boolean node, boolean edge); + /** + * Creates a new hierarchical graph view. + * + * @return newly created hierarchical graph view + */ + public HierarchicalGraphView createHierarchicalView(); + /** * Creates a new graph view based on an existing view. * diff --git a/store/src/main/java/org/gephi/graph/api/HierarchicalGraphView.java b/store/src/main/java/org/gephi/graph/api/HierarchicalGraphView.java new file mode 100644 index 00000000..392c3c9a --- /dev/null +++ b/store/src/main/java/org/gephi/graph/api/HierarchicalGraphView.java @@ -0,0 +1,25 @@ +package org.gephi.graph.api; + +/** + * A hierarchical graph view which allows for collapsible sub-groups. Each node, + * whether it is collapsed or extended, should be in core in view. If a node is + * node is hidden by collapsed parent, it will omitted within + * directed/undirected graph queries. + */ +public interface HierarchicalGraphView extends GraphView { + /** + * @return Return iterator for each hierarchical node group. + */ + Iterable getGroups(); + + /** + * @return Return root node which first-tier nodes should be attached. + */ + HierarchicalNodeGroup getRoot(); + + /** + * @param node node within graph graph + * @return Return existing hierarchical group if available; else null. + */ + HierarchicalNodeGroup getGroup(Node node); +} diff --git a/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java new file mode 100644 index 00000000..1a64b085 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java @@ -0,0 +1,54 @@ +package org.gephi.graph.api; + +/** + * A group of nodes within a hierarchical graph view. + */ +public interface HierarchicalNodeGroup { + /** + * @return Returns true if the group is collapsed, which means the children + * are hidden from visible graph. + */ + boolean isCollapsed(); + + /** + * @return Returns true if the group is expanded, which means the children + * are visible within visible graph. + */ + boolean isExpanded(); + + /** + * Expand node, displaying children. + */ + void expand(); + + /** + * Collapse node, hiding children. + */ + void collapse(); + + /** + * @return Return iterator containing children nodes (not recursive). + */ + Iterable getNodes(); + + /** + * @return Return iterator containing children nodes. + */ + Iterable getNodes(boolean recursive); + + /** + * Add node to this group. + * + * @param node child node + * @return Returns child hierarchical group, if created; else null. + */ + HierarchicalNodeGroup addNode(Node node); + + /** + * Remove node from group. + * + * @param node child node + * @return Returns true if child hierarchical group was remoce; else false. + */ + boolean removeNode(Node node); +} diff --git a/store/src/main/java/org/gephi/graph/impl/AbstractGraphView.java b/store/src/main/java/org/gephi/graph/impl/AbstractGraphView.java new file mode 100644 index 00000000..0e6a71d7 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/AbstractGraphView.java @@ -0,0 +1,105 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphObserver; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.UndirectedSubgraph; + +abstract public class AbstractGraphView implements GraphView { + protected final GraphStore graphStore; + + protected final GraphAttributesImpl attributes; + + protected final boolean nodeView; + + protected final boolean edgeView; + + private Interval interval; + + private int storeId = GraphViewStore.NULL_VIEW; + + public AbstractGraphView(final GraphStore store, boolean nodes, boolean edges) { + this.graphStore = store; + this.nodeView = nodes; + this.edgeView = edges; + this.interval = Interval.INFINITY_INTERVAL; + this.attributes = new GraphAttributesImpl(); + } + + public AbstractGraphView(final AbstractGraphView view, boolean nodes, boolean edges) { + this.graphStore = view.graphStore; + this.nodeView = nodes; + this.edgeView = edges; + this.interval = view.interval; + this.attributes = new GraphAttributesImpl(); + } + + public int getStoreId() { + return this.storeId; + } + + protected void setStoreId(final int id) { + this.storeId = id; + } + + @Override + public boolean isDestroyed() { + return GraphViewStore.NULL_VIEW == this.storeId; + } + + @Override + public GraphModelImpl getGraphModel() { + return this.graphStore.graphModel; + } + + @Override + public boolean isMainView() { + return false; + } + + @Override + public boolean isNodeView() { + return this.nodeView; + } + + @Override + public boolean isEdgeView() { + return this.edgeView; + } + + public void setTimeInterval(Interval interval) { + if (interval == null) { + interval = Interval.INFINITY_INTERVAL; + } + this.interval = interval; + } + + @Override + public Interval getTimeInterval() { + return this.interval; + } + + abstract public DirectedSubgraph getDirectedGraph(); + + abstract public UndirectedSubgraph getUndirectedGraph(); + + abstract public boolean deepEquals(AbstractGraphView view); + + abstract public int deepHashCode(); + + abstract protected void viewDestroyed(); + + abstract protected void nodeAdded(NodeImpl node); + + abstract protected void nodeRemoved(NodeImpl node); + + abstract protected void edgeAdded(EdgeImpl edge); + + abstract protected void edgeRemoved(EdgeImpl edge); + + abstract protected GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff); + + abstract protected void destroyGraphObserver(GraphObserver graphObserver); +} diff --git a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index f7945b0c..86a52435 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -16,6 +16,7 @@ package org.gephi.graph.impl; import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.HierarchicalGraphView; import org.gephi.graph.api.Index; import org.gephi.graph.api.Table; import org.gephi.graph.api.TimeFormat; @@ -238,6 +239,11 @@ public GraphView createView(boolean node, boolean edge) { return store.viewStore.createView(node, edge); } + @Override + public HierarchicalGraphView createHierarchicalView() { + return store.viewStore.createHierarchicalView(); + } + @Override public GraphView copyView(GraphView view) { return store.viewStore.createView(view); diff --git a/store/src/main/java/org/gephi/graph/impl/GraphStore.java b/store/src/main/java/org/gephi/graph/impl/GraphStore.java index f28915eb..3a325396 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -15,16 +15,7 @@ */ package org.gephi.graph.impl; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Set; import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Origin; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.types.TimestampSet; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; @@ -32,39 +23,66 @@ import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.Origin; import org.gephi.graph.api.Subgraph; -import org.joda.time.DateTimeZone; +import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalSet; +import org.gephi.graph.api.types.TimestampSet; +import org.joda.time.DateTimeZone; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; public class GraphStore implements DirectedGraph, DirectedSubgraph { protected final GraphModelImpl graphModel; + protected final Configuration configuration; + // Stores protected final NodeStore nodeStore; + protected final EdgeStore edgeStore; + protected final EdgeTypeStore edgeTypeStore; + protected final TableImpl nodeTable; + protected final TableImpl edgeTable; + protected final GraphViewStore viewStore; + protected final TimeStore timeStore; + protected final GraphAttributesImpl attributes; + // Factory protected final GraphFactoryImpl factory; + // Lock protected final GraphLock lock; + // Version protected final GraphVersion version; + protected final List observers; + // Undirected protected final UndirectedDecorator undirectedDecorator; + // Main Graph view protected final GraphView mainGraphView; + // TimeFormat protected TimeFormat timeFormat; + // Time zone protected DateTimeZone timeZone; @@ -822,6 +840,7 @@ public boolean deepEquals(GraphStore obj) { protected class NodeIterableWrapper implements NodeIterable { protected final Iterator iterator; + protected final boolean blocking; public NodeIterableWrapper(Iterator iterator) { @@ -842,7 +861,10 @@ public Iterator iterator() { public Node[] toArray() { List list = new ArrayList(); for (; iterator.hasNext();) { - list.add(iterator.next()); + Node node = iterator.next(); + if (node != null) { + list.add(node); + } } return list.toArray(new Node[0]); } @@ -851,7 +873,10 @@ public Node[] toArray() { public Collection toCollection() { List list = new ArrayList(); for (; iterator.hasNext();) { - list.add(iterator.next()); + Node node = iterator.next(); + if (node != null) { + list.add(node); + } } return list; } @@ -867,6 +892,7 @@ public void doBreak() { protected class EdgeIterableWrapper implements EdgeIterable { protected final Iterator iterator; + protected final boolean blocking; public EdgeIterableWrapper(Iterator iterator) { diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index e5086098..c8b590fc 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -15,9 +15,6 @@ */ package org.gephi.graph.impl; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; @@ -30,6 +27,10 @@ import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + public class GraphViewDecorator implements DirectedSubgraph, UndirectedSubgraph { protected final boolean undirected; @@ -736,10 +737,10 @@ void checkValidViewObject(final GraphView view) { if (view == null) { throw new NullPointerException(); } - if (!(view instanceof GraphViewImpl)) { - throw new ClassCastException("Object must be a GraphViewImpl object"); + if (!(view instanceof AbstractGraphView)) { + throw new ClassCastException("Object must be a AbstractGraphView object"); } - if (((GraphViewImpl) view).graphStore != graphStore) { + if (((AbstractGraphView) view).graphStore != graphStore) { throw new RuntimeException("The view doesn't belong to this store"); } } diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index a4e7ce0c..501abc77 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -17,30 +17,26 @@ import cern.colt.bitvector.BitVector; import cern.colt.bitvector.QuickBitVector; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphObserver; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Node; import org.gephi.graph.api.UndirectedSubgraph; import org.gephi.graph.impl.EdgeStore.EdgeInOutIterator; -public class GraphViewImpl implements GraphView { +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +public class GraphViewImpl extends AbstractGraphView implements GraphView { // Data - protected final GraphStore graphStore; - protected final boolean nodeView; - protected final boolean edgeView; - protected final GraphAttributesImpl attributes; protected BitVector nodeBitVector; protected BitVector edgeBitVector; - protected int storeId; // Version protected final GraphVersion version; protected final List observers; @@ -53,14 +49,9 @@ public class GraphViewImpl implements GraphView { protected int[] typeCounts; protected int[] mutualEdgeTypeCounts; protected int mutualEdgesCount; - // Dynamic - protected Interval interval; public GraphViewImpl(final GraphStore store, boolean nodes, boolean edges) { - this.graphStore = store; - this.nodeView = nodes; - this.edgeView = edges; - this.attributes = new GraphAttributesImpl(); + super(store, nodes, edges); if (nodes) { this.nodeBitVector = new BitVector(store.nodeStore.maxStoreId()); } else { @@ -74,14 +65,10 @@ public GraphViewImpl(final GraphStore store, boolean nodes, boolean edges) { this.undirectedDecorator = new GraphViewDecorator(graphStore, this, true); this.version = graphStore.version != null ? new GraphVersion(directedDecorator) : null; this.observers = graphStore.version != null ? new ArrayList() : null; - this.interval = Interval.INFINITY_INTERVAL; } public GraphViewImpl(final GraphViewImpl view, boolean nodes, boolean edges) { - this.graphStore = view.graphStore; - this.nodeView = nodes; - this.edgeView = edges; - this.attributes = new GraphAttributesImpl(); + super(view, nodes, edges); if (nodes) { this.nodeBitVector = view.nodeBitVector.copy(); this.nodeCount = view.nodeCount; @@ -98,14 +85,15 @@ public GraphViewImpl(final GraphViewImpl view, boolean nodes, boolean edges) { this.undirectedDecorator = new GraphViewDecorator(graphStore, this, true); this.version = graphStore.version != null ? new GraphVersion(directedDecorator) : null; this.observers = graphStore.version != null ? new ArrayList() : null; - this.interval = view.interval; } - protected DirectedSubgraph getDirectedGraph() { + @Override + public DirectedSubgraph getDirectedGraph() { return directedDecorator; } - protected UndirectedSubgraph getUndirectedGraph() { + @Override + public UndirectedSubgraph getUndirectedGraph() { return undirectedDecorator; } @@ -563,42 +551,6 @@ public int getUndirectedEdgeCount(int type) { } @Override - public GraphModelImpl getGraphModel() { - return graphStore.graphModel; - } - - @Override - public boolean isMainView() { - return false; - } - - @Override - public boolean isNodeView() { - return nodeView; - } - - @Override - public boolean isEdgeView() { - return edgeView; - } - - public void setTimeInterval(Interval interval) { - if (interval == null) { - interval = Interval.INFINITY_INTERVAL; - } - this.interval = interval; - } - - @Override - public Interval getTimeInterval() { - return interval; - } - - @Override - public boolean isDestroyed() { - return storeId == GraphViewStore.NULL_VIEW; - } - protected GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { if (observers != null) { GraphObserverImpl observer = new GraphObserverImpl(graphStore, version, graph, withDiff); @@ -609,10 +561,11 @@ protected GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { return null; } - protected void destroyGraphObserver(GraphObserverImpl observer) { + @Override + protected void destroyGraphObserver(GraphObserver observer) { if (observers != null) { observers.remove(observer); - observer.destroyObserver(); + ((GraphObserverImpl) observer).destroyObserver(); } } @@ -625,6 +578,35 @@ protected void destroyAllObservers() { } } + @Override + protected void viewDestroyed() { + this.setStoreId(GraphViewStore.NULL_VIEW); + this.destroyAllObservers(); + } + + @Override + protected void nodeAdded(NodeImpl node) { + this.ensureNodeVectorSize(node); + } + + @Override + protected void nodeRemoved(NodeImpl node) { + this.removeNode(node); + } + + @Override + protected void edgeAdded(EdgeImpl edge) { + this.ensureEdgeVectorSize(edge); + if (this.nodeView && !this.edgeView) { + this.addEdgeInNodeView(edge); + } + } + + @Override + protected void edgeRemoved(EdgeImpl edge) { + this.removeEdge(edge); + } + protected void ensureNodeVectorSize(NodeImpl node) { int sid = node.storeId; if (sid >= nodeBitVector.size()) { @@ -726,6 +708,7 @@ private void ensureTypeCountArrayCapacity(int type) { } } + @Override public int deepHashCode() { int hash = 5; hash = 17 * hash + (this.nodeView ? 1 : 0); @@ -737,14 +720,19 @@ public int deepHashCode() { hash = 11 * hash + Arrays.hashCode(this.typeCounts); hash = 11 * hash + Arrays.hashCode(this.mutualEdgeTypeCounts); hash = 11 * hash + this.mutualEdgesCount; - hash = 11 * hash + (this.interval != null ? this.interval.hashCode() : 0); + hash = 11 * hash + (this.getTimeInterval() != null ? this.getTimeInterval().hashCode() : 0); return hash; } - public boolean deepEquals(GraphViewImpl obj) { - if (obj == null) { + @Override + public boolean deepEquals(AbstractGraphView view) { + if (view == null) { + return false; + } + if (!(view instanceof GraphViewImpl)) { return false; } + GraphViewImpl obj = (GraphViewImpl) view; if (this.nodeBitVector != obj.nodeBitVector && (this.nodeBitVector == null || !this.nodeBitVector .equals(obj.nodeBitVector))) { return false; @@ -774,7 +762,8 @@ public boolean deepEquals(GraphViewImpl obj) { if (this.mutualEdgesCount != obj.mutualEdgesCount) { return false; } - if (this.interval != obj.interval && (this.interval == null || !this.interval.equals(obj.interval))) { + if (this.getTimeInterval() != obj.getTimeInterval() && (this.getTimeInterval() == null || !this + .getTimeInterval().equals(obj.getTimeInterval()))) { return false; } return true; diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 599b485e..8d6b9ed3 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -17,11 +17,12 @@ import it.unimi.dsi.fastutil.ints.IntRBTreeSet; import it.unimi.dsi.fastutil.ints.IntSortedSet; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.HierarchicalGraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; @@ -35,7 +36,7 @@ public class GraphViewStore { // Data protected final IntSortedSet garbageQueue; protected final GraphStore graphStore; - protected GraphViewImpl[] views; + protected AbstractGraphView[] views; protected int length; // Visible view protected GraphView visibleView; @@ -45,11 +46,26 @@ public GraphViewStore(GraphStore graphStore) { throw new NullPointerException(); } this.graphStore = graphStore; - this.views = new GraphViewImpl[DEFAULT_VIEWS]; + this.views = new AbstractGraphView[DEFAULT_VIEWS]; this.garbageQueue = new IntRBTreeSet(); this.visibleView = graphStore.mainGraphView; } + public HierarchicalGraphView createHierarchicalView() { + return createHierarchicalView(true, true); + } + + public HierarchicalGraphView createHierarchicalView(boolean nodes, boolean edges) { + graphStore.autoWriteLock(); + try { + HierarchicalGraphViewImpl graphView = new HierarchicalGraphViewImpl(graphStore, nodes, edges); + addView(graphView); + return graphView; + } finally { + graphStore.autoWriteUnlock(); + } + } + public GraphViewImpl createView() { return createView(true, true); } @@ -82,11 +98,18 @@ public GraphViewImpl createView(GraphView view, boolean nodes, boolean edges) { } } else { checkNonNullViewObject(view); - checkViewExist((GraphViewImpl) view); - + checkViewExist((AbstractGraphView) view); graphStore.autoWriteLock(); try { - GraphViewImpl graphView = new GraphViewImpl((GraphViewImpl) view, nodes, edges); + final GraphViewImpl copy; + if (view instanceof GraphViewImpl) { + copy = (GraphViewImpl) view; + } else if (view instanceof HierarchicalGraphViewImpl) { + copy = ((HierarchicalGraphViewImpl) view).viewDelegate; + } else { + throw new IllegalArgumentException(); + } + GraphViewImpl graphView = new GraphViewImpl(copy, nodes, edges); addView(graphView); return graphView; } finally { @@ -102,25 +125,25 @@ public void destroyView(GraphView view) { TimeIndexStore nodeTimeStore = graphStore.timeStore.nodeIndexStore; if (nodeTimeStore != null) { - nodeTimeStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); + nodeTimeStore.deleteViewIndex(((AbstractGraphView) view).getDirectedGraph()); } TimeIndexStore edgeTimeStore = graphStore.timeStore.edgeIndexStore; if (edgeTimeStore != null) { - edgeTimeStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); + edgeTimeStore.deleteViewIndex(((AbstractGraphView) view).getDirectedGraph()); } IndexStore nodeIndexStore = graphStore.nodeTable.store.indexStore; if (nodeIndexStore != null) { - nodeIndexStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); + nodeIndexStore.deleteViewIndex(((AbstractGraphView) view).getDirectedGraph()); } IndexStore edgeIndexStore = graphStore.edgeTable.store.indexStore; if (edgeIndexStore != null) { - edgeIndexStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); + edgeIndexStore.deleteViewIndex(((AbstractGraphView) view).getDirectedGraph()); } - removeView((GraphViewImpl) view); + removeView((AbstractGraphView) view); } finally { graphStore.autoWriteUnlock(); } @@ -128,11 +151,11 @@ public void destroyView(GraphView view) { public void setTimeInterval(GraphView view, Interval interval) { checkNonNullViewObject(view); - checkViewExist((GraphViewImpl) view); + checkViewExist((AbstractGraphView) view); graphStore.autoWriteLock(); try { - GraphViewImpl graphView = (GraphViewImpl) view; + AbstractGraphView graphView = (AbstractGraphView) view; graphView.setTimeInterval(interval); } finally { graphStore.autoWriteUnlock(); @@ -143,8 +166,8 @@ public boolean contains(GraphView view) { graphStore.autoReadLock(); try { checkNonNullViewObject(view); - GraphViewImpl viewImpl = (GraphViewImpl) view; - int id = viewImpl.storeId; + AbstractGraphView viewImpl = (AbstractGraphView) view; + int id = viewImpl.getStoreId(); if (id != NULL_VIEW && id < length && views[id] == view) { return true; } @@ -165,12 +188,12 @@ public Subgraph getGraph(GraphView view) { if (view.isMainView()) { return graphStore.undirectedDecorator; } - return ((GraphViewImpl) view).getUndirectedGraph(); + return ((AbstractGraphView) view).getUndirectedGraph(); } else { if (view.isMainView()) { return graphStore; } - return ((GraphViewImpl) view).getDirectedGraph(); + return ((AbstractGraphView) view).getDirectedGraph(); } } @@ -182,7 +205,7 @@ public DirectedSubgraph getDirectedGraph(GraphView view) { } checkDirectedAllowed(); - return ((GraphViewImpl) view).getDirectedGraph(); + return ((AbstractGraphView) view).getDirectedGraph(); } public UndirectedSubgraph getUndirectedGraph(GraphView view) { @@ -191,7 +214,7 @@ public UndirectedSubgraph getUndirectedGraph(GraphView view) { if (view.isMainView()) { return graphStore.undirectedDecorator; } - return ((GraphViewImpl) view).getUndirectedGraph(); + return ((AbstractGraphView) view).getUndirectedGraph(); } public GraphView getVisibleView() { @@ -203,20 +226,20 @@ public void setVisibleView(GraphView view) { visibleView = graphStore.mainGraphView; } else { checkNonNullViewObject(view); - checkViewExist((GraphViewImpl) view); + checkViewExist((AbstractGraphView) view); visibleView = view; } } public GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { - GraphViewImpl graphViewImpl = (GraphViewImpl) graph.getView(); + AbstractGraphView graphViewImpl = (AbstractGraphView) graph.getView(); checkViewExist(graphViewImpl); return graphViewImpl.createGraphObserver(graph, withDiff); } public void destroyGraphObserver(GraphObserverImpl graphObserver) { - GraphViewImpl graphViewImpl = (GraphViewImpl) graphObserver.graph.getView(); + AbstractGraphView graphViewImpl = (AbstractGraphView) graphObserver.graph.getView(); checkViewExist(graphViewImpl); graphViewImpl.destroyGraphObserver(graphObserver); @@ -224,9 +247,9 @@ public void destroyGraphObserver(GraphObserverImpl graphObserver) { protected void addNode(NodeImpl node) { if (views.length > 0) { - for (GraphViewImpl view : views) { + for (AbstractGraphView view : views) { if (view != null) { - view.ensureNodeVectorSize(node); + view.nodeAdded(node); } } } @@ -234,9 +257,9 @@ protected void addNode(NodeImpl node) { protected void removeNode(NodeImpl node) { if (views.length > 0) { - for (GraphViewImpl view : views) { + for (AbstractGraphView view : views) { if (view != null) { - view.removeNode(node); + view.nodeRemoved(node); } } } @@ -244,13 +267,9 @@ protected void removeNode(NodeImpl node) { protected void addEdge(EdgeImpl edge) { if (views.length > 0) { - for (GraphViewImpl view : views) { + for (AbstractGraphView view : views) { if (view != null) { - view.ensureEdgeVectorSize(edge); - - if (view.nodeView && !view.edgeView) { - view.addEdgeInNodeView(edge); - } + view.edgeAdded(edge); } } } @@ -258,15 +277,15 @@ protected void addEdge(EdgeImpl edge) { protected void removeEdge(EdgeImpl edge) { if (views.length > 0) { - for (GraphViewImpl view : views) { + for (AbstractGraphView view : views) { if (view != null) { - view.removeEdge(edge); + view.edgeRemoved(edge); } } } } - protected int addView(final GraphViewImpl view) { + protected int addView(final AbstractGraphView view) { checkNonNullViewObject(view); int id; @@ -278,19 +297,18 @@ protected int addView(final GraphViewImpl view) { ensureArraySize(id); } views[id] = view; - view.storeId = id; + view.setStoreId(id); return id; } - protected void removeView(final GraphViewImpl view) { + protected void removeView(final AbstractGraphView view) { checkViewExist(view); - int id = view.storeId; + int id = view.getStoreId(); views[id] = null; garbageQueue.add(id); - view.storeId = NULL_VIEW; - view.destroyAllObservers(); + view.viewDestroyed(); // Check if not visible view if (visibleView == view) { @@ -300,7 +318,7 @@ protected void removeView(final GraphViewImpl view) { private void ensureArraySize(int index) { if (index >= views.length) { - GraphViewImpl[] newArray = new GraphViewImpl[index + 1]; + AbstractGraphView[] newArray = new AbstractGraphView[index + 1]; System.arraycopy(views, 0, newArray, 0, views.length); views = newArray; } @@ -308,7 +326,7 @@ private void ensureArraySize(int index) { public int deepHashCode() { int hash = 5; - for (GraphViewImpl view : this.views) { + for (AbstractGraphView view : this.views) { hash = 67 * hash + view.deepHashCode(); } hash = 67 * hash + this.length; @@ -327,8 +345,8 @@ public boolean deepEquals(GraphViewStore obj) { return false; } for (int i = 0; i < l; i++) { - GraphViewImpl e1 = this.views[i]; - GraphViewImpl e2 = obj.views[i]; + AbstractGraphView e1 = this.views[i]; + AbstractGraphView e2 = obj.views[i]; if (e1 == e2) { continue; @@ -349,14 +367,14 @@ protected void checkNonNullViewObject(final Object o) { throw new NullPointerException(); } if (o != graphStore.mainGraphView) { - if (!(o instanceof GraphViewImpl)) { - throw new ClassCastException("View must be a GraphViewImpl object"); + if (!(o instanceof AbstractGraphView)) { + throw new ClassCastException("View must be a AbstractGraphView object"); } } } - protected void checkViewExist(final GraphViewImpl view) { - int id = view.storeId; + protected void checkViewExist(final AbstractGraphView view) { + final int id = view.getStoreId(); if (id == NULL_VIEW || id >= length || views[id] != view) { throw new IllegalArgumentException("The view doesn't exist"); } diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java new file mode 100644 index 00000000..57cfbe66 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java @@ -0,0 +1,1574 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.Column; +import org.gephi.graph.api.ColumnIterable; +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.Subgraph; +import org.gephi.graph.api.Table; +import org.gephi.graph.api.TextProperties; +import org.gephi.graph.api.UndirectedSubgraph; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +public class HierarchicalGraphDecorator implements DirectedSubgraph, UndirectedSubgraph { + private final boolean undirected; + + private final HierarchicalGraphViewImpl view; + + private final GraphStore graphStore; + + public HierarchicalGraphDecorator(GraphStore graphStore, HierarchicalGraphViewImpl view, boolean undirected) { + this.graphStore = graphStore; + this.view = view; + this.undirected = undirected; + } + + @Override + public Edge getEdge(Node node1, Node node2) { + graphStore.autoReadLock(); + try { + for (final Node n1 : view.mapWithHidden(node1)) { + for (final Node n2 : view.mapWithHidden(node2)) { + EdgeImpl edge = graphStore.edgeStore.get(n1, n2, undirected); + if (edge != null && view.containsEdge(edge)) { + return decorateEdge(edge); + } + } + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public Edge getEdge(Node node1, Node node2, int type) { + graphStore.autoReadLock(); + try { + for (final Node n1 : view.mapWithHidden(node1)) { + for (final Node n2 : view.mapWithHidden(node2)) { + EdgeImpl edge = graphStore.edgeStore.get(n1, n2, type, undirected); + if (edge != null && view.containsEdge(edge)) { + return decorateEdge(edge); + } + } + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public EdgeIterable getEdges(final Node node1, final Node node2) { + List>> list = new ArrayList>>(); + for (final Node n1 : view.mapWithHidden(node1)) { + for (final Node n2 : view.mapWithHidden(node2)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.getAll(n1, n2, undirected); + } + }); + } + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getEdges(final Node node1, final Node node2, final int type) { + List>> list = new ArrayList>>(); + for (final Node n1 : view.mapWithHidden(node1)) { + for (final Node n2 : view.mapWithHidden(node2)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.getAll(n1, n2, type, undirected); + } + }); + } + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public Edge getMutualEdge(Edge edge) { + graphStore.autoReadLock(); + try { + final Edge unpacked = undecorateEdge(edge); + for (final Node n1 : view.mapWithHidden(unpacked.getSource())) { + for (final Node n2 : view.mapWithHidden(unpacked.getTarget())) { + Edge e = graphStore.getEdge(n1, n2); + EdgeImpl mutual = graphStore.edgeStore.getMutualEdge(e); + if (mutual != null && view.containsEdge(mutual)) { + return decorateEdge(mutual); + } + } + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public NodeIterable getPredecessors(Node node) { + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( + graphStore.edgeStore.edgeInIterator(node)))); + } + + @Override + public NodeIterable getPredecessors(Node node, int type) { + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( + graphStore.edgeStore.edgeInIterator(node, type)))); + } + + @Override + public NodeIterable getSuccessors(Node node) { + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( + graphStore.edgeStore.edgeOutIterator(node)))); + } + + @Override + public NodeIterable getSuccessors(Node node, int type) { + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( + graphStore.edgeStore.edgeOutIterator(node, type)))); + } + + @Override + public EdgeIterable getInEdges(Node node) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.edgeInIterator(n); + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getInEdges(final Node node, final int type) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.edgeInIterator(n, type); + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getOutEdges(final Node node) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.edgeOutIterator(n); + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getOutEdges(final Node node, final int type) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.edgeOutIterator(n, type); + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public boolean isAdjacent(Node source, Node target) { + checkValidInViewNodeObject(source); + checkValidInViewNodeObject(target); + graphStore.autoReadLock(); + try { + for (final Node mappedSource : view.mapWithHidden(source)) { + for (final Node mappedTarget : view.mapWithHidden(target)) { + EdgeImpl edge = graphStore.edgeStore.get(mappedSource, mappedTarget, undirected); + if (edge != null && view.containsEdge(edge)) { + return true; + } + } + } + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean isAdjacent(Node source, Node target, int type) { + checkValidInViewNodeObject(source); + checkValidInViewNodeObject(target); + graphStore.autoReadLock(); + try { + for (final Node mappedSource : view.mapWithHidden(source)) { + for (final Node mappedTarget : view.mapWithHidden(target)) { + EdgeImpl edge = graphStore.edgeStore.get(mappedSource, mappedTarget, type, undirected); + if (edge != null && view.containsEdge(edge)) { + return true; + } + } + } + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean addEdge(Edge edge) { + Edge unpacked = undecorateEdge(edge); + checkValidEdgeObject(unpacked); + graphStore.autoWriteLock(); + try { + return view.addEdge(unpacked); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean addNode(Node node) { + checkValidNodeObject(node); + graphStore.autoWriteLock(); + try { + return view.addNode(node); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean addAllEdges(Collection edges) { + graphStore.autoWriteLock(); + try { + boolean updated = false; + for (final Edge edge : edges) { + updated |= view.addEdge(edge); + } + return updated; + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean addAllNodes(Collection nodes) { + graphStore.autoWriteLock(); + try { + boolean updated = false; + for (final Node node : nodes) { + updated |= view.addNode(node); + } + return updated; + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean removeEdge(Edge edge) { + Edge unpacked = undecorateEdge(edge); + checkValidEdgeObject(unpacked); + graphStore.autoWriteLock(); + try { + return view.removeEdge(unpacked); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean removeNode(Node node) { + checkValidNodeObject(node); + graphStore.autoWriteLock(); + try { + return view.removeNode(node); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean removeAllEdges(Collection edges) { + graphStore.autoWriteLock(); + try { + return this.removeAllEdgesWithLock(edges.iterator()); + } finally { + graphStore.autoWriteUnlock(); + } + } + + private boolean removeAllEdgesWithLock(Iterator itr) { + if (null == itr) { + return false; + } + + boolean updated = false; + while (itr.hasNext()) { + final Edge edge = itr.next(); + if (edge != null) { + updated |= view.removeEdge(undecorateEdge(edge)); + } + } + return updated; + } + + @Override + public boolean removeAllNodes(Collection nodes) { + graphStore.autoWriteLock(); + try { + boolean updated = false; + for (final Node node : nodes) { + updated |= view.removeNode(node); + } + return updated; + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean contains(Node node) { + checkValidNodeObject(node); + graphStore.autoReadLock(); + try { + return view.containsNode((NodeImpl) node) && view.visibleNode((NodeImpl) node); + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean contains(Edge edge) { + Edge unpacked = undecorateEdge(edge); + checkValidEdgeObject(unpacked); + graphStore.autoReadLock(); + try { + return view.containsEdge((EdgeImpl) unpacked); + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public Node getNode(Object id) { + graphStore.autoReadLock(); + try { + NodeImpl node = graphStore.getNode(id); + if (node != null && view.containsNode(node) && view.visibleNode(node)) { + return node; + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean hasNode(final Object id) { + return getNode(id) != null; + } + + @Override + public Edge getEdge(Object id) { + graphStore.autoReadLock(); + try { + EdgeImpl edge = graphStore.getEdge(id); + if (edge != null && view.containsEdge(edge)) { + return decorateEdge(edge); + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean hasEdge(final Object id) { + return getEdge(id) != null; + } + + @Override + public NodeIterable getNodes() { + return graphStore.getNodeIterableWrapper(new NodeViewIterator(graphStore.nodeStore.iterator())); + } + + @Override + public EdgeIterable getEdges() { + if (undirected) { + return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator(graphStore.edgeStore.iterator())); + } else { + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.iterator())); + } + } + + @Override + public EdgeIterable getSelfLoops() { + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.iteratorSelfLoop())); + } + + @Override + public NodeIterable getNeighbors(Node node) { + checkValidInViewNodeObject(node); + Set set = new HashSet(); + for (final Node n : view.mapWithHidden(node)) { + final Iterator itr = new NeighborsIterator((NodeImpl) n, new UndirectedEdgeViewIterator( + graphStore.edgeStore.edgeIterator(n))); + while (itr.hasNext()) { + final Node neighbor = itr.next(); + if (neighbor != null) { + set.add(neighbor); + } + } + } + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator())); + } + + @Override + public NodeIterable getNeighbors(Node node, int type) { + checkValidInViewNodeObject(node); + Set set = new HashSet(); + for (final Node n : view.mapWithHidden(node)) { + final Iterator itr = new NeighborsIterator((NodeImpl) n, new UndirectedEdgeViewIterator( + graphStore.edgeStore.edgeIterator(n, type))); + while (itr.hasNext()) { + final Node neighbor = itr.next(); + if (neighbor != null) { + set.add(neighbor); + } + } + } + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator())); + } + + @Override + public EdgeIterable getEdges(Node node) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + if (undirected) { + return new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(n)); + } else { + return graphStore.edgeStore.edgeIterator(n); + } + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getEdges(final Node node, final int type) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + if (undirected) { + return new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(n, type)); + } else { + return graphStore.edgeStore.edgeIterator(n, type); + } + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public int getNodeCount() { + return view.getNodeCount(); + } + + @Override + public int getEdgeCount() { + if (undirected) { + return view.getUndirectedEdgeCount(); + } else { + return view.getEdgeCount(); + } + } + + @Override + public int getEdgeCount(int type) { + if (undirected) { + return view.getUndirectedEdgeCount(type); + } else { + return view.getEdgeCount(type); + } + } + + @Override + public Node getOpposite(Node node, Edge edge) { + Edge e = undecorateEdge(edge); + Node n1 = view.mapToVisible(e.getSource()); + Node n2 = view.mapToVisible(e.getTarget()); + checkValidInViewNodeObject(n1); + checkValidInViewNodeObject(n2); + checkValidInViewNodeObject(node); + checkValidInViewEdgeObject(e); + if (n1.equals(node)) { + return n2; + } else if (n2.equals(node)) { + return n1; + } else { + return null; + } + } + + @Override + public int getDegree(final Node node) { + if (!this.contains(node)) { + return 0; + } + + if (undirected) { + int count = 0; + for (final Node related : view.mapWithHidden(node)) { + EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(related); + while (itr.hasNext()) { + EdgeImpl edge = itr.next(); + if (view.containsEdge(edge) && !isUndirectedToIgnore(edge)) { + count++; + if (edge.isSelfLoop()) { + count++; + } + } + } + } + return count; + } else { + int count = 0; + for (final Node related : view.mapWithHidden(node)) { + EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(related); + while (itr.hasNext()) { + EdgeImpl edge = itr.next(); + if (view.containsEdge(edge)) { + count++; + if (edge.isSelfLoop()) { + count++; + } + } + } + } + return count; + } + } + + @Override + public int getInDegree(final Node node) { + if (!this.contains(node)) { + return 0; + } + + int count = 0; + for (final Node related : view.mapWithHidden(node)) { + EdgeStore.EdgeInIterator itr = graphStore.edgeStore.edgeInIterator(related); + while (itr.hasNext()) { + if (view.containsEdge(itr.next())) { + count++; + } + } + } + return count; + } + + @Override + public int getOutDegree(final Node node) { + if (!this.contains(node)) { + return 0; + } + + int count = 0; + for (final Node related : view.mapWithHidden(node)) { + EdgeStore.EdgeOutIterator itr = graphStore.edgeStore.edgeOutIterator(related); + while (itr.hasNext()) { + if (view.containsEdge(itr.next())) { + count++; + } + } + } + return count; + } + + @Override + public boolean isSelfLoop(Edge edge) { + return edge.isSelfLoop(); + } + + @Override + public boolean isDirected(Edge edge) { + return edge.isDirected(); + } + + @Override + public boolean isIncident(Edge edge1, Edge edge2) { + graphStore.autoReadLock(); + try { + if (edge1 instanceof MappedEdgeDecorator) { + edge1 = undecorateEdge(edge1); + } else { + edge1 = decorateEdge(edge1); + } + + if (edge2 instanceof MappedEdgeDecorator) { + edge2 = undecorateEdge(edge2); + } else { + edge2 = decorateEdge(edge2); + } + + Set n1 = new HashSet(); + for (final Node n : Arrays.asList(edge1.getSource(), edge1.getTarget())) { + n1.addAll(view.mapWithHidden(n)); + } + + Set n2 = new HashSet(); + for (final Node n : Arrays.asList(edge2.getSource(), edge2.getTarget())) { + n2.addAll(view.mapWithHidden(n)); + } + + for (Node n : n1) { + if (n2.contains(n)) { + return true; + } + } + + for (Node n : n2) { + if (n1.contains(n)) { + return true; + } + } + + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean isIncident(Node node, Edge edge) { + graphStore.autoReadLock(); + try { + if (edge instanceof MappedEdgeDecorator) { + edge = undecorateEdge(edge); + } else { + edge = decorateEdge(edge); + } + + Set n1 = new HashSet(); + for (final Node n : Arrays.asList(edge.getSource(), edge.getTarget())) { + n1.addAll(view.mapWithHidden(n)); + } + + Set n2 = new HashSet(view.mapWithHidden(node)); + + for (Node n : n1) { + if (n2.contains(n)) { + return true; + } + } + + for (Node n : n2) { + if (n1.contains(n)) { + return true; + } + } + + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public void clearEdges(final Node node) { + graphStore.autoWriteLock(); + try { + this.removeAllEdgesWithLock(graphStore.edgeStore.edgeIterator(node)); + for (final Node related : view.mapWithHidden(node)) { + this.removeAllEdgesWithLock(graphStore.edgeStore.edgeIterator(related)); + } + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void clearEdges(final Node node, final int type) { + graphStore.autoWriteLock(); + try { + this.removeAllEdgesWithLock(graphStore.edgeStore.edgeIterator(node, type)); + for (final Node related : view.mapWithHidden(node)) { + this.removeAllEdgesWithLock(graphStore.edgeStore.edgeIterator(related, type)); + } + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void clear() { + view.clear(); + } + + @Override + public void clearEdges() { + view.clearEdges(); + } + + @Override + public Object getAttribute(String key) { + return view.attributes.getValue(key); + } + + @Override + public Object getAttribute(String key, double timestamp) { + return view.attributes.getValue(key, timestamp); + } + + @Override + public Object getAttribute(String key, Interval interval) { + return view.attributes.getValue(key, interval); + } + + @Override + public Set getAttributeKeys() { + return view.attributes.getKeys(); + } + + @Override + public void setAttribute(String key, Object value) { + view.attributes.setValue(key, value); + } + + @Override + public void setAttribute(String key, Object value, double timestamp) { + view.attributes.setValue(key, value, timestamp); + } + + @Override + public void setAttribute(String key, Object value, Interval interval) { + view.attributes.setValue(key, value, interval); + } + + @Override + public void removeAttribute(String key) { + view.attributes.removeValue(key); + } + + @Override + public void removeAttribute(String key, double timestamp) { + view.attributes.removeValue(key, timestamp); + } + + @Override + public void removeAttribute(String key, Interval interval) { + view.attributes.removeValue(key, interval); + } + + @Override + public GraphModel getModel() { + return graphStore.graphModel; + } + + @Override + public boolean isDirected() { + return graphStore.isDirected(); + } + + @Override + public boolean isUndirected() { + return graphStore.isUndirected(); + } + + @Override + public boolean isMixed() { + return graphStore.isMixed(); + } + + @Override + public void readLock() { + graphStore.lock.readLock(); + } + + @Override + public void readUnlock() { + graphStore.lock.readUnlock(); + } + + @Override + public void readUnlockAll() { + graphStore.lock.readUnlockAll(); + } + + @Override + public void writeLock() { + graphStore.lock.writeLock(); + } + + @Override + public void writeUnlock() { + graphStore.lock.writeUnlock(); + } + + @Override + public GraphView getView() { + return view; + } + + @Override + public void fill() { + graphStore.autoWriteLock(); + try { + view.fill(); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void union(final Subgraph subGraph) { + checkValidViewObject(subGraph.getView()); + + graphStore.autoWriteLock(); + try { + if (subGraph instanceof GraphViewDecorator) { + view.viewDelegate.union((GraphViewImpl) subGraph.getView()); + } else if (subGraph instanceof HierarchicalGraphDecorator) { + final HierarchicalGraphViewImpl other = (HierarchicalGraphViewImpl) subGraph.getView(); + view.viewDelegate.union(other.viewDelegate); + } + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void intersection(final Subgraph subGraph) { + checkValidViewObject(subGraph.getView()); + + graphStore.autoWriteLock(); + try { + if (subGraph instanceof GraphViewDecorator) { + view.viewDelegate.intersection((GraphViewImpl) subGraph.getView()); + } else if (subGraph instanceof HierarchicalGraphDecorator) { + final HierarchicalGraphViewImpl other = (HierarchicalGraphViewImpl) subGraph.getView(); + view.viewDelegate.intersection(other.viewDelegate); + } + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void not() { + graphStore.autoWriteLock(); + try { + view.not(); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public Graph getRootGraph() { + return graphStore; + } + + void checkWriteLock() { + if (graphStore.lock != null) { + graphStore.lock.checkHoldWriteLock(); + } + } + + void checkValidNodeObject(final Node n) { + if (n == null) { + throw new NullPointerException(); + } + if (!(n instanceof NodeImpl)) { + throw new ClassCastException("Object must be a NodeImpl object"); + } + if (((NodeImpl) n).storeId == NodeStore.NULL_ID) { + throw new IllegalArgumentException("Node should belong to a store"); + } + } + + void checkValidInViewNodeObject(final Node n) { + checkValidNodeObject(n); + + if (!view.containsNode((NodeImpl) n)) { + throw new RuntimeException("Node doesn't belong to this view"); + } + } + + void checkValidEdgeObject(final Edge e) { + if (e == null) { + throw new NullPointerException(); + } + if (!(e instanceof EdgeImpl)) { + throw new ClassCastException("Object must be a EdgeImpl object"); + } + if (((EdgeImpl) e).storeId == EdgeStore.NULL_ID) { + throw new IllegalArgumentException("Edge should belong to a store"); + } + } + + void checkValidInViewEdgeObject(final Edge e) { + checkValidEdgeObject(e); + if (!view.containsEdge((EdgeImpl) e)) { + throw new RuntimeException("Edge doesn't belong to this view"); + } + } + + void checkValidViewObject(final GraphView view) { + if (view == null) { + throw new NullPointerException(); + } + if (!(view instanceof AbstractGraphView)) { + throw new ClassCastException("Object must be a AbstractGraphView object"); + } + if (((AbstractGraphView) view).graphStore != graphStore) { + throw new RuntimeException("The view doesn't belong to this store"); + } + } + + boolean isUndirectedToIgnore(final EdgeImpl edge) { + if (edge.isMutual() && edge.source.storeId < edge.target.storeId) { + if (view.containsEdge(graphStore.edgeStore.get(edge.target, edge.source, edge.type, false))) { + return true; + } + } + return false; + } + + private final class ChainedFutureIterator implements Iterator { + private final List>> delegates; + + private Iterator>> itr = null; + + private Iterator delegatePointer = null; + + private T itemPointer = null; + + private ChainedFutureIterator(final Collection>> c) { + this.delegates = new ArrayList>>(c); + } + + @Override + public boolean hasNext() { + itemPointer = null; + + if (null == this.itr) { + itr = delegates.iterator(); + } + + while (null == itemPointer) { + while (null == delegatePointer) { + if (!itr.hasNext()) { + return false; + } + try { + delegatePointer = itr.next().call(); + } catch (final Exception e) { + throw new IllegalStateException(e); + } + } + + if (delegatePointer.hasNext()) { + itemPointer = delegatePointer.next(); + } else { + delegatePointer = null; + } + } + + return true; + } + + @Override + public T next() { + return itemPointer; + } + } + + private final class NodeViewIterator implements Iterator { + private final Iterator nodeIterator; + + private NodeImpl pointer; + + public NodeViewIterator(Iterator nodeIterator) { + this.nodeIterator = nodeIterator; + } + + @Override + public boolean hasNext() { + pointer = null; + while (pointer == null) { + if (!nodeIterator.hasNext()) { + return false; + } + pointer = (NodeImpl) nodeIterator.next(); + if (pointer != null) { + if (!view.containsNode(pointer) || !view.visibleNode(pointer)) { + pointer = null; + } + } + } + return true; + } + + @Override + public Node next() { + return pointer; + } + + @Override + public void remove() { + checkWriteLock(); + removeNode(pointer); + } + } + + private final class EdgeViewIterator implements Iterator { + private final Iterator edgeIterator; + + private EdgeImpl pointer; + + public EdgeViewIterator(Iterator edgeIterator) { + this.edgeIterator = edgeIterator; + } + + @Override + public boolean hasNext() { + pointer = null; + while (pointer == null) { + if (!edgeIterator.hasNext()) { + return false; + } + pointer = (EdgeImpl) edgeIterator.next(); + if (pointer != null) { + if (!view.containsEdge(pointer)) { + pointer = null; + } + } + } + return true; + } + + @Override + public Edge next() { + return decorateEdge(pointer); + } + + @Override + public void remove() { + checkWriteLock(); + removeEdge(pointer); + } + } + + private final class UndirectedEdgeViewIterator implements Iterator { + private final Iterator itr; + + private EdgeImpl pointer; + + public UndirectedEdgeViewIterator(Iterator itr) { + this.itr = itr; + } + + @Override + public boolean hasNext() { + pointer = null; + while (pointer == null || !view.containsEdge(pointer) || isUndirectedToIgnore(pointer)) { + if (!itr.hasNext()) { + return false; + } + pointer = (EdgeImpl) itr.next(); + } + return true; + } + + @Override + public Edge next() { + return decorateEdge(pointer); + } + + @Override + public void remove() { + itr.remove(); + } + } + + private class NeighborsIterator implements Iterator { + private final NodeImpl node; + + private final Iterator itr; + + public NeighborsIterator(NodeImpl node, Iterator itr) { + this.node = node; + this.itr = itr; + } + + @Override + public boolean hasNext() { + return itr.hasNext(); + } + + @Override + public Node next() { + Edge e = itr.next(); + return e.getSource() == node ? e.getTarget() : e.getSource(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported for this iterator"); + } + } + + private Edge undecorateEdge(final Edge edge) { + if (null == edge) { + return null; + } + + Edge unpacked = edge; + while (unpacked instanceof MappedEdgeDecorator) { + unpacked = ((MappedEdgeDecorator) unpacked).edge; + } + + return unpacked; + } + + private Edge decorateEdge(final Edge edge) { + if (null == edge) { + return null; + } + + if (edge instanceof MappedEdgeDecorator) { + return edge; + } + + final Node mappedSource = this.view.mapToVisible(edge.getSource()); + final Node mappedTarget = this.view.mapToVisible(edge.getTarget()); + + if (mappedSource == edge.getSource() && mappedTarget == edge.getTarget()) { + return edge; + } + + return new MappedEdgeDecorator(edge, mappedSource, mappedTarget); + } + + protected class MappedEdgeDecorator implements Edge { + private final Edge edge; + + private final Node source; + + private final Node target; + + private MappedEdgeDecorator(final Edge edge, final Node source, final Node target) { + this.edge = edge; + this.source = source; + this.target = target; + } + + @Override + public Node getSource() { + return this.source; + } + + @Override + public Node getTarget() { + return this.target; + } + + @Override + public double getWeight() { + return edge.getWeight(); + } + + @Override + public double getWeight(double timestamp) { + return edge.getWeight(timestamp); + } + + @Override + public double getWeight(Interval interval) { + return edge.getWeight(interval); + } + + @Override + public double getWeight(GraphView view) { + return edge.getWeight(view); + } + + @Override + public Iterable getWeights() { + return edge.getWeights(); + } + + @Override + public void setWeight(double weight) { + edge.setWeight(weight); + } + + @Override + public void setWeight(double weight, double timestamp) { + edge.setWeight(weight, timestamp); + } + + @Override + public void setWeight(double weight, Interval interval) { + edge.setWeight(weight, interval); + } + + @Override + public boolean hasDynamicWeight() { + return edge.hasDynamicWeight(); + } + + @Override + public int getType() { + return edge.getType(); + } + + @Override + public Object getTypeLabel() { + return edge.getTypeLabel(); + } + + @Override + public boolean isSelfLoop() { + return edge.isSelfLoop(); + } + + @Override + public boolean isDirected() { + return edge.isSelfLoop(); + } + + @Override + public Object getId() { + return edge.getId(); + } + + @Override + public String getLabel() { + return edge.getLabel(); + } + + @Override + public Object getAttribute(String key) { + return edge.getAttribute(key); + } + + @Override + public Object getAttribute(Column column) { + return edge.getAttribute(column); + } + + @Override + public Object getAttribute(String key, double timestamp) { + return edge.getAttribute(key, timestamp); + } + + @Override + public Object getAttribute(Column column, double timestamp) { + return edge.getAttribute(column, timestamp); + } + + @Override + public Object getAttribute(String key, Interval interval) { + return edge.getAttribute(key, interval); + } + + @Override + public Object getAttribute(Column column, Interval interval) { + return edge.getAttribute(column, interval); + } + + @Override + public Object getAttribute(String key, GraphView view) { + return edge.getAttribute(key, view); + } + + @Override + public Object getAttribute(Column column, GraphView view) { + return edge.getAttribute(column, view); + } + + @Override + public Iterable getAttributes(Column column) { + return edge.getAttributes(column); + } + + @Override + public Object[] getAttributes() { + return edge.getAttributes(); + } + + @Override + public Set getAttributeKeys() { + return edge.getAttributeKeys(); + } + + @Override + public ColumnIterable getAttributeColumns() { + return edge.getAttributeColumns(); + } + + @Override + public int getStoreId() { + return edge.getStoreId(); + } + + @Override + public Object removeAttribute(String key) { + return edge.removeAttribute(key); + } + + @Override + public Object removeAttribute(Column column) { + return edge.removeAttribute(column); + } + + @Override + public Object removeAttribute(String key, double timestamp) { + return edge.removeAttribute(key, timestamp); + } + + @Override + public Object removeAttribute(Column column, double timestamp) { + return edge.removeAttribute(column, timestamp); + } + + @Override + public Object removeAttribute(String key, Interval interval) { + return edge.removeAttribute(key, interval); + } + + @Override + public Object removeAttribute(Column column, Interval interval) { + return edge.removeAttribute(column, interval); + } + + @Override + public void setLabel(String label) { + edge.setLabel(label); + } + + @Override + public void setAttribute(String key, Object value) { + edge.setAttribute(key, value); + } + + @Override + public void setAttribute(Column column, Object value) { + edge.setAttribute(column, value); + } + + @Override + public void setAttribute(String key, Object value, double timestamp) { + edge.setAttribute(key, value, timestamp); + } + + @Override + public void setAttribute(Column column, Object value, double timestamp) { + edge.setAttribute(column, value, timestamp); + } + + @Override + public void setAttribute(String key, Object value, Interval interval) { + edge.setAttribute(key, value, interval); + } + + @Override + public void setAttribute(Column column, Object value, Interval interval) { + edge.setAttribute(column, value, interval); + } + + @Override + public boolean addTimestamp(double timestamp) { + return edge.addTimestamp(timestamp); + } + + @Override + public boolean removeTimestamp(double timestamp) { + return edge.removeTimestamp(timestamp); + } + + @Override + public boolean hasTimestamp(double timestamp) { + return edge.hasTimestamp(timestamp); + } + + @Override + public double[] getTimestamps() { + return edge.getTimestamps(); + } + + @Override + public boolean addInterval(Interval interval) { + return edge.addInterval(interval); + } + + @Override + public boolean removeInterval(Interval interval) { + return edge.removeInterval(interval); + } + + @Override + public boolean hasInterval(Interval interval) { + return edge.hasInterval(interval); + } + + @Override + public Interval[] getIntervals() { + return edge.getIntervals(); + } + + @Override + public void clearAttributes() { + edge.clearAttributes(); + } + + @Override + public Table getTable() { + return edge.getTable(); + } + + @Override + public float r() { + return edge.r(); + } + + @Override + public float g() { + return edge.g(); + } + + @Override + public float b() { + return edge.b(); + } + + @Override + public int getRGBA() { + return edge.getRGBA(); + } + + @Override + public Color getColor() { + return edge.getColor(); + } + + @Override + public float alpha() { + return edge.alpha(); + } + + @Override + public TextProperties getTextProperties() { + return edge.getTextProperties(); + } + + @Override + public void setR(float r) { + edge.setR(r); + } + + @Override + public void setG(float g) { + edge.setG(g); + } + + @Override + public void setB(float b) { + edge.setB(b); + } + + @Override + public void setAlpha(float a) { + edge.setAlpha(a); + } + + @Override + public void setColor(Color color) { + edge.setColor(color); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + MappedEdgeDecorator that = (MappedEdgeDecorator) o; + + if (edge != null ? !edge.equals(that.edge) : that.edge != null) { + return false; + } + if (source != null ? !source.equals(that.source) : that.source != null) { + return false; + } + return target != null ? target.equals(that.target) : that.target == null; + } + + @Override + public int hashCode() { + int result = edge != null ? edge.hashCode() : 0; + result = 31 * result + (source != null ? source.hashCode() : 0); + result = 31 * result + (target != null ? target.hashCode() : 0); + return result; + } + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java new file mode 100644 index 00000000..8ab6ff8f --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java @@ -0,0 +1,513 @@ +package org.gephi.graph.impl; + +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphObserver; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.HierarchicalGraphView; +import org.gephi.graph.api.HierarchicalNodeGroup; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.UndirectedSubgraph; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class HierarchicalGraphViewImpl extends AbstractGraphView implements GraphView, HierarchicalGraphView { + protected final GraphViewImpl viewDelegate; + + private final GraphVersion version; + + private final HierarchicalGraphDecorator directedDecorator; + + private final HierarchicalGraphDecorator undirectedDecorator; + + private final Set observers = new HashSet(); + + private final HierarchicalNodeGroupImpl root = new HierarchicalNodeGroupImpl(null, null); + + public HierarchicalGraphViewImpl(GraphStore store, boolean nodes, boolean edges) { + super(store, nodes, edges); + this.viewDelegate = new GraphViewImpl(store, nodes, edges); + this.directedDecorator = new HierarchicalGraphDecorator(store, this, false); + this.undirectedDecorator = new HierarchicalGraphDecorator(store, this, true); + this.version = graphStore.version != null ? new GraphVersion(directedDecorator) : null; + } + + public boolean containsNode(final NodeImpl node) { + if (null == node) { + return false; + } + + if (!this.viewDelegate.containsNode(node)) { + return false; + } + + return true; + } + + public boolean visibleNode(final NodeImpl node) { + if (null == node) { + return false; + } + + final Node mapped = this.mapToVisible(node); + if (null == mapped) { + return false; + } + + return mapped == node; + } + + public boolean addNode(final Node node) { + if (!this.viewDelegate.addNode(node)) { + return false; + } + + return true; + } + + public boolean removeNode(final Node node) { + boolean updated = this.viewDelegate.removeNode(node); + updated |= this.root.removeNode(node, true); + return updated; + } + + public boolean addEdge(final Edge edge) { + if (!this.viewDelegate.addEdge(edge)) { + return false; + } + + return true; + } + + public boolean removeEdge(final Edge edge) { + if (!this.viewDelegate.removeEdge(edge)) { + return false; + } + + return true; + } + + public boolean containsEdge(final EdgeImpl edge) { + return this.viewDelegate.containsEdge(edge); + } + + public int getCollapsedNodeCount() { + final Set visibleGroups = new HashSet(); + final Set hiddenGroups = new HashSet(); + for (final HierarchicalNodeGroup group : this.getGroups()) { + final HierarchicalNodeGroupImpl impl = (HierarchicalNodeGroupImpl) group; + if (impl.hasCollapsedParent()) { + hiddenGroups.add(impl); + visibleGroups.remove(impl); + } else if (!hiddenGroups.contains(impl)) { + visibleGroups.add(impl); + } + } + return hiddenGroups.size(); + } + + public int getNodeCount() { + return this.viewDelegate.getNodeCount() - this.getCollapsedNodeCount(); + } + + public int getEdgeCount() { + return this.viewDelegate.getEdgeCount(); + } + + public int getEdgeCount(int type) { + return this.viewDelegate.getEdgeCount(type); + } + + public int getUndirectedEdgeCount() { + return this.viewDelegate.getUndirectedEdgeCount(); + } + + public int getUndirectedEdgeCount(int type) { + return this.viewDelegate.getUndirectedEdgeCount(type); + } + + public void clear() { + this.root.clear(); + this.viewDelegate.clear(); + } + + public void clearEdges() { + this.viewDelegate.clearEdges(); + } + + public void fill() { + this.viewDelegate.fill(); + } + + public void not() { + this.viewDelegate.not(); + } + + @Override + public DirectedSubgraph getDirectedGraph() { + return this.directedDecorator; + } + + @Override + public UndirectedSubgraph getUndirectedGraph() { + return this.undirectedDecorator; + } + + @Override + public boolean deepEquals(AbstractGraphView view) { + return this.viewDelegate.deepEquals(view); + } + + @Override + public int deepHashCode() { + return this.viewDelegate.deepHashCode(); + } + + @Override + protected void viewDestroyed() { + this.setStoreId(GraphViewStore.NULL_VIEW); + for (final GraphObserverImpl observer : this.observers) { + observer.destroyObserver(); + } + this.observers.clear(); + this.root.clear(); + this.viewDelegate.viewDestroyed(); + } + + @Override + protected void nodeAdded(NodeImpl node) { + this.viewDelegate.nodeAdded(node); + } + + @Override + protected void nodeRemoved(NodeImpl node) { + this.removeNode(node); + this.viewDelegate.nodeRemoved(node); + } + + @Override + protected void edgeAdded(EdgeImpl edge) { + this.viewDelegate.edgeAdded(edge); + } + + @Override + protected void edgeRemoved(EdgeImpl edge) { + this.viewDelegate.edgeRemoved(edge); + } + + @Override + protected GraphObserverImpl createGraphObserver(final Graph graph, final boolean withDiff) { + if (null == this.version) { + return null; + } + + final GraphObserverImpl observer = new GraphObserverImpl(this.graphStore, this.version, graph, withDiff); + this.observers.add(observer); + return observer; + } + + @Override + protected void destroyGraphObserver(final GraphObserver observer) { + if (this.observers.remove(observer)) { + ((GraphObserverImpl) observer).destroyObserver(); + } + } + + @Override + public Iterable getGroups() { + final List groups = new ArrayList(); + groups.add(this.root); + for (final HierarchicalNodeGroupImpl group : this.root.getGroups(true)) { + groups.add(group); + } + return Collections.unmodifiableList(groups); + } + + @Override + public HierarchicalNodeGroup getRoot() { + return this.root; + } + + @Override + public HierarchicalNodeGroup getGroup(final Node node) { + if (null == node) { + return null; + } + + return this.root.find(node, true); + } + + protected Collection mapWithHidden(final Node node) { + if (null == node) { + return Collections.emptyList(); + } + + final HierarchicalNodeGroupImpl group = this.root.find(node, true); + if (null == group) { + return Collections.singleton(node); + } + + if (group.hasCollapsedParent()) { + return Collections.emptyList(); + } + + if (group.isCollapsed()) { + final Collection children = group.getNodes(true); + final Set set = new HashSet(children.size() + 1); + set.add(node); + set.addAll(children); + return Collections.unmodifiableCollection(set); + } + + return Collections.singleton(node); + } + + protected Node mapToVisible(final Node node) { + final HierarchicalNodeGroupImpl group = this.root.find(node, true); + if (null == group) { + return node; + } else { + return group.mappedNode(); + } + } + + private class HierarchicalNodeGroupImpl implements HierarchicalNodeGroup { + private final HierarchicalNodeGroupImpl parent; + + private final Node node; + + private boolean collapsed = false; + + private final Object2ObjectMap nodeMap = new Object2ObjectOpenHashMap(); + + private HierarchicalNodeGroupImpl(final HierarchicalNodeGroupImpl parent, final Node node) { + this.parent = parent; + this.node = node; + } + + public void clear() { + if (!this.nodeMap.isEmpty()) { + return; + } + + for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) { + group.clear(); + } + this.nodeMap.clear(); + } + + @Override + public HierarchicalNodeGroup addNode(final Node childNode) { + if (null == childNode) { + return null; + } + + if (this.node == childNode) { + throw new IllegalArgumentException("Child and parent node are the same."); + } + + graphStore.autoWriteLock(); + try { + if (this.nodeMap.containsKey(childNode)) { + return null; + } + + final HierarchicalNodeGroupImpl group = new HierarchicalNodeGroupImpl(this, childNode); + this.nodeMap.put(childNode, group); + return group; + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean removeNode(final Node childNode) { + if (null == childNode) { + return false; + } + + return this.removeNode(childNode, false); + } + + public boolean removeNode(final Node childNode, final boolean recursive) { + if (null == childNode) { + return false; + } + + graphStore.autoWriteLock(); + try { + return this.removeNodeWithLock(childNode, recursive); + } finally { + graphStore.autoWriteUnlock(); + } + } + + private boolean removeNodeWithLock(final Node childNode, final boolean recursive) { + boolean updated = this.nodeMap.get(childNode) != null; + if (recursive) { + for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) { + updated |= group.removeNodeWithLock(childNode, true); + } + } + return updated; + } + + public HierarchicalNodeGroupImpl find(final Node childNode, final boolean recursive) { + graphStore.autoReadLock(); + try { + return this.findWithLock(childNode, recursive); + } finally { + graphStore.autoReadUnlock(); + } + } + + private HierarchicalNodeGroupImpl findWithLock(final Node childNode, final boolean recursive) { + final HierarchicalNodeGroupImpl existing = this.nodeMap.get(childNode); + if (existing != null) { + return existing; + } + + if (!recursive) { + return null; + } + + for (final HierarchicalNodeGroupImpl child : nodeMap.values()) { + final HierarchicalNodeGroupImpl existingChild = child.findWithLock(childNode, true); + if (existingChild != null) { + return existingChild; + } + } + + return null; + } + + public Node mappedNode() { + if (this.node != null) { + // child node + HierarchicalNodeGroupImpl childGroup = this; + HierarchicalNodeGroupImpl parentGroup = this.parent; + while (parentGroup != null) { + if (null == parentGroup.node) { + return childGroup.node; + } + if (parentGroup.isExpanded()) { + return childGroup.node; + } + final HierarchicalNodeGroupImpl tmp = parentGroup; + parentGroup = parentGroup.parent; + childGroup = tmp; + } + return null; + } else { + // first tier node, always visible + return null; + } + } + + public boolean hasCollapsedParent() { + graphStore.autoReadLock(); + try { + HierarchicalNodeGroupImpl group = this.parent; + while (group != null) { + if (group.isCollapsed()) { + return true; + } + group = group.parent; + } + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean isCollapsed() { + graphStore.autoReadLock(); + try { + return this.collapsed; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean isExpanded() { + return !this.isCollapsed(); + } + + @Override + public void expand() { + this.collapsed = false; + for (final HierarchicalNodeGroupImpl child : this.nodeMap.values()) { + child.expand(); + } + } + + @Override + public void collapse() { + this.collapsed = true; + for (final HierarchicalNodeGroupImpl child : this.nodeMap.values()) { + child.collapse(); + } + } + + @Override + public Iterable getNodes() { + graphStore.autoReadLock(); + final List list; + try { + list = new ArrayList(this.nodeMap.keySet()); + } finally { + graphStore.autoReadUnlock(); + } + return Collections.unmodifiableCollection(list); + } + + @Override + public Collection getNodes(boolean recursive) { + graphStore.autoReadLock(); + final Set set = new HashSet(this.nodeMap.size()); + try { + if (recursive) { + for (final Map.Entry entry : this.nodeMap.entrySet()) { + set.add(entry.getKey()); + if (recursive) { + set.addAll(entry.getValue().getNodes(true)); + } + } + } + } finally { + graphStore.autoReadUnlock(); + } + return Collections.unmodifiableCollection(set); + } + + public Collection getGroups(final boolean recursive) { + graphStore.autoReadLock(); + final List list; + try { + if (recursive) { + list = new ArrayList(); + for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) { + list.add(group); + list.addAll(group.getGroups(true)); + } + } else { + list = new ArrayList(this.nodeMap.values()); + } + } finally { + graphStore.autoReadUnlock(); + } + return Collections.unmodifiableCollection(list); + } + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/Serialization.java b/store/src/main/java/org/gephi/graph/impl/Serialization.java index 35a95fc8..ba5c0a38 100644 --- a/store/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/store/src/main/java/org/gephi/graph/impl/Serialization.java @@ -586,7 +586,7 @@ private GraphViewStore deserializeViewStore(final DataInput is) throws IOExcepti private void serializeGraphView(final DataOutput out, final GraphViewImpl view) throws IOException { serialize(out, view.nodeView); serialize(out, view.edgeView); - serialize(out, view.storeId); + serialize(out, view.getStoreId()); serialize(out, view.nodeCount); serialize(out, view.edgeCount); @@ -600,7 +600,7 @@ private void serializeGraphView(final DataOutput out, final GraphViewImpl view) serialize(out, view.version); serialize(out, view.attributes); - serialize(out, view.interval); + serialize(out, view.getTimeInterval()); } private GraphViewImpl deserializeGraphView(final DataInput is) throws IOException, ClassNotFoundException { @@ -624,7 +624,7 @@ private GraphViewImpl deserializeGraphView(final DataInput is) throws IOExceptio view.edgeCount = edgeCount; view.nodeBitVector = nodeCountVector; view.edgeBitVector = edgeCountVector; - view.storeId = storeId; + view.setStoreId(storeId); view.typeCounts = typeCounts; view.mutualEdgesCount = mutualEdgesCount; @@ -634,7 +634,7 @@ private GraphViewImpl deserializeGraphView(final DataInput is) throws IOExceptio view.version.edgeVersion = version.edgeVersion; view.attributes.setGraphAttributes(atts); - view.interval = interval; + view.setTimeInterval(interval); return view; } diff --git a/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java b/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java index 12f4434d..a74c2b5f 100644 --- a/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java +++ b/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java @@ -67,7 +67,7 @@ public void testDestroy() { Assert.assertFalse(store.contains(view)); Assert.assertEquals(store.size(), 0); - Assert.assertEquals(view.storeId, GraphViewStore.NULL_VIEW); + Assert.assertEquals(view.getStoreId(), GraphViewStore.NULL_VIEW); Assert.assertTrue(view.isDestroyed()); } @@ -128,7 +128,7 @@ public void testGarbage() { view = store.createView(); - Assert.assertEquals(view.storeId, 0); + Assert.assertEquals(view.getStoreId(), 0); Assert.assertEquals(store.length, 1); Assert.assertEquals(store.garbageQueue.size(), 0); } diff --git a/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java new file mode 100644 index 00000000..1e7ff6f9 --- /dev/null +++ b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java @@ -0,0 +1,115 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.HierarchicalGraphView; +import org.gephi.graph.api.HierarchicalNodeGroup; +import org.gephi.graph.api.Node; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.HashSet; +import java.util.Set; + +public class HierarchicalGraphTest { + @Test + public void testSimpleExpandCollapse() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + HierarchicalGraphView view = store.createHierarchicalView(); + DirectedSubgraph graph = store.getDirectedGraph(view); + + for (Node node : graphStore.getNodes().toArray()) { + graph.addNode(node); + } + for (Edge edge : graphStore.getEdges().toArray()) { + graph.addEdge(edge); + } + + int originalCount = store.getGraph(view).getNodeCount(); + + Node parentNode = store.getGraph(view).getNode("7"); + HierarchicalNodeGroup group = view.getRoot().addNode(parentNode); + Assert.assertEquals(originalCount, graph.getNodeCount()); + + Node childNode = store.getGraph(view).getNode("3"); + group.addNode(childNode); + Assert.assertEquals(originalCount, graph.getNodeCount()); + + Assert.assertFalse(graph.getEdges(parentNode).toCollection().isEmpty()); + Assert.assertFalse(graph.getEdges(childNode).toCollection().isEmpty()); + + int edgeCountForParent = graph.getDegree(parentNode); + int edgeCountForChild = graph.getDegree(childNode); + + group.collapse(); + + Assert.assertEquals(originalCount - 1, graph.getNodeCount()); + Assert.assertFalse(graph.hasNode("3")); + Assert.assertEquals(edgeCountForParent + edgeCountForChild, graph.getDegree(parentNode)); + Assert.assertEquals(0, graph.getDegree(childNode)); + + Assert.assertFalse(graph.getEdges(parentNode).toCollection().isEmpty()); + Assert.assertTrue(graph.getEdges(childNode).toCollection().isEmpty()); + + graph.clearEdges(); + + Assert.assertTrue(graph.getEdges(parentNode).toCollection().isEmpty()); + Assert.assertTrue(graph.getEdges(childNode).toCollection().isEmpty()); + } + + @Test + public void testManipulateEdges() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + HierarchicalGraphView view = store.createHierarchicalView(); + DirectedSubgraph graph = store.getDirectedGraph(view); + + for (Node node : graphStore.getNodes().toArray()) { + graph.addNode(node); + } + for (Edge edge : graphStore.getEdges().toArray()) { + graph.addEdge(edge); + } + + Node parentNode = store.getGraph(view).getNode("7"); + HierarchicalNodeGroup group = view.getRoot().addNode(parentNode); + Node childNode = store.getGraph(view).getNode("3"); + group.addNode(childNode); + + Set others = new HashSet(); + for (Edge edge : graph.getEdges(childNode)) { + Node other = graph.getOpposite(childNode, edge); + Assert.assertNotNull(other); + Assert.assertFalse(other.equals(childNode)); + others.add(other); + } + Assert.assertFalse(others.isEmpty()); + + group.collapse(); + + Set mapped = new HashSet(); + for (Node other : others) { + for (Edge edge : graph.getEdges(other)) { + if (edge instanceof HierarchicalGraphDecorator.MappedEdgeDecorator) { + if (edge.getSource() != edge.getTarget()) { + Assert.assertFalse(edge.getSource() == childNode || edge.getTarget() == childNode); + Assert.assertTrue(edge.getSource() == parentNode || edge.getTarget() == parentNode); + Assert.assertTrue(edge.getSource() == other || edge.getTarget() == other); + mapped.add(edge); + } + } + } + } + Assert.assertFalse(mapped.isEmpty()); + + for (Edge edge : mapped) { + Node other = graph.getOpposite(childNode, edge); + Assert.assertNull(other); + other = graph.getOpposite(parentNode, edge); + Assert.assertNotNull(other); + Assert.assertTrue(others.contains(other)); + graph.removeEdge(edge); + } + } +} From ee3f0d588d5ad7092855da6a73f23220cae6835f Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Sat, 23 Apr 2016 16:24:04 +0700 Subject: [PATCH 02/15] added better support for #getNeighbors; added #isRoot method to check for root node operations --- .../graph/api/HierarchicalNodeGroup.java | 10 ++ .../impl/HierarchicalGraphDecorator.java | 42 ++++----- .../graph/impl/HierarchicalGraphViewImpl.java | 93 +++++++++++++----- .../graph/impl/HierarchicalGraphTest.java | 94 +++++++++++++++++++ 4 files changed, 194 insertions(+), 45 deletions(-) diff --git a/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java index 1a64b085..1e6a9713 100644 --- a/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java +++ b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java @@ -26,6 +26,16 @@ public interface HierarchicalNodeGroup { */ void collapse(); + /** + * @return Returns true if this group contains on or more children nodes. + */ + boolean hasChildren(); + + /** + * @return Returns true if this group is the root node. + */ + boolean isRoot(); + /** * @return Return iterator containing children nodes (not recursive). */ diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java index 57cfbe66..2f22af66 100644 --- a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java @@ -450,38 +450,36 @@ public EdgeIterable getSelfLoops() { } @Override - public NodeIterable getNeighbors(Node node) { + public NodeIterable getNeighbors(final Node node) { checkValidInViewNodeObject(node); Set set = new HashSet(); - for (final Node n : view.mapWithHidden(node)) { - final Iterator itr = new NeighborsIterator((NodeImpl) n, new UndirectedEdgeViewIterator( - graphStore.edgeStore.edgeIterator(n))); - while (itr.hasNext()) { - final Node neighbor = itr.next(); - if (neighbor != null) { - set.add(neighbor); - } + Set mapped = new HashSet(); + mapped.add(node); + mapped.addAll(view.mapWithHidden(node)); + for (Node n : mapped) { + for (Edge edge : this.getEdges(n)) { + set.add(edge.getSource()); + set.add(edge.getTarget()); } } - checkValidInViewNodeObject(node); + set.removeAll(mapped); return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator())); } @Override - public NodeIterable getNeighbors(Node node, int type) { + public NodeIterable getNeighbors(final Node node, final int type) { checkValidInViewNodeObject(node); Set set = new HashSet(); - for (final Node n : view.mapWithHidden(node)) { - final Iterator itr = new NeighborsIterator((NodeImpl) n, new UndirectedEdgeViewIterator( - graphStore.edgeStore.edgeIterator(n, type))); - while (itr.hasNext()) { - final Node neighbor = itr.next(); - if (neighbor != null) { - set.add(neighbor); - } + Set mapped = new HashSet(); + mapped.add(node); + mapped.addAll(view.mapWithHidden(node)); + for (Node n : mapped) { + for (Edge edge : this.getEdges(n, type)) { + set.add(edge.getSource()); + set.add(edge.getTarget()); } } - checkValidInViewNodeObject(node); + set.removeAll(mapped); return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator())); } @@ -1147,11 +1145,11 @@ public void remove() { } private class NeighborsIterator implements Iterator { - private final NodeImpl node; + private final Node node; private final Iterator itr; - public NeighborsIterator(NodeImpl node, Iterator itr) { + public NeighborsIterator(Node node, Iterator itr) { this.node = node; this.itr = itr; } diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java index 8ab6ff8f..1e2ece49 100644 --- a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java @@ -16,7 +16,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -224,12 +223,12 @@ protected void destroyGraphObserver(final GraphObserver observer) { @Override public Iterable getGroups() { - final List groups = new ArrayList(); + final Set groups = new HashSet(); groups.add(this.root); for (final HierarchicalNodeGroupImpl group : this.root.getGroups(true)) { groups.add(group); } - return Collections.unmodifiableList(groups); + return Collections.unmodifiableCollection(groups); } @Override @@ -399,7 +398,7 @@ public Node mappedNode() { if (null == parentGroup.node) { return childGroup.node; } - if (parentGroup.isExpanded()) { + if (parentGroup.isExpanded() && !parentGroup.hasCollapsedParent()) { return childGroup.node; } final HierarchicalNodeGroupImpl tmp = parentGroup; @@ -446,68 +445,116 @@ public boolean isExpanded() { @Override public void expand() { - this.collapsed = false; - for (final HierarchicalNodeGroupImpl child : this.nodeMap.values()) { - child.expand(); + graphStore.autoWriteLock(); + try { + this.setCollapsedWithLock(false); + } finally { + graphStore.autoWriteUnlock(); } } @Override public void collapse() { - this.collapsed = true; + graphStore.autoWriteLock(); + try { + this.setCollapsedWithLock(true); + } finally { + graphStore.autoWriteUnlock(); + } + } + + private void setCollapsedWithLock(final boolean value) { + this.collapsed = value; for (final HierarchicalNodeGroupImpl child : this.nodeMap.values()) { - child.collapse(); + child.setCollapsedWithLock(value); + } + } + + @Override + public boolean hasChildren() { + graphStore.autoReadLock(); + try { + return !this.nodeMap.isEmpty(); + } finally { + graphStore.autoReadUnlock(); } } + @Override + public boolean isRoot() { + return root.equals(this); + } + @Override public Iterable getNodes() { graphStore.autoReadLock(); - final List list; try { - list = new ArrayList(this.nodeMap.keySet()); + return Collections.unmodifiableCollection(new ArrayList(this.nodeMap.keySet())); } finally { graphStore.autoReadUnlock(); } - return Collections.unmodifiableCollection(list); } @Override public Collection getNodes(boolean recursive) { graphStore.autoReadLock(); - final Set set = new HashSet(this.nodeMap.size()); try { if (recursive) { + final Set set = new HashSet(this.nodeMap.size()); for (final Map.Entry entry : this.nodeMap.entrySet()) { set.add(entry.getKey()); - if (recursive) { - set.addAll(entry.getValue().getNodes(true)); - } + set.addAll(entry.getValue().getNodes(true)); } + return Collections.unmodifiableCollection(set); + } else { + return Collections.unmodifiableCollection(new ArrayList(this.nodeMap.keySet())); } } finally { graphStore.autoReadUnlock(); } - return Collections.unmodifiableCollection(set); } public Collection getGroups(final boolean recursive) { graphStore.autoReadLock(); - final List list; try { if (recursive) { - list = new ArrayList(); + final Collection set = new HashSet(); for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) { - list.add(group); - list.addAll(group.getGroups(true)); + set.add(group); + set.addAll(group.getGroups(true)); } + return Collections.unmodifiableCollection(set); } else { - list = new ArrayList(this.nodeMap.values()); + return Collections.unmodifiableCollection(new ArrayList(this.nodeMap + .values())); } } finally { graphStore.autoReadUnlock(); } - return Collections.unmodifiableCollection(list); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + HierarchicalNodeGroupImpl that = (HierarchicalNodeGroupImpl) o; + + if (parent != null ? !parent.equals(that.parent) : that.parent != null) { + return false; + } + return node != null ? node.equals(that.node) : that.node == null; + } + + @Override + public int hashCode() { + int result = parent != null ? parent.hashCode() : 0; + result = 31 * result + (node != null ? node.hashCode() : 0); + return result; } } } diff --git a/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java index 1e7ff6f9..5eb2294c 100644 --- a/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java +++ b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java @@ -8,8 +8,11 @@ import org.testng.Assert; import org.testng.annotations.Test; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.TreeSet; public class HierarchicalGraphTest { @Test @@ -112,4 +115,95 @@ public void testManipulateEdges() { graph.removeEdge(edge); } } + + @Test + public void testNeighbors() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + HierarchicalGraphView view = store.createHierarchicalView(); + DirectedSubgraph graph = store.getDirectedGraph(view); + + Set types = new TreeSet(); + for (Node node : graphStore.getNodes().toArray()) { + graph.addNode(node); + } + for (Edge edge : graphStore.getEdges().toArray()) { + graph.addEdge(edge); + types.add(edge.getType()); + } + + Node parentNode = store.getGraph(view).getNode("5"); + HierarchicalNodeGroup group = view.getRoot().addNode(parentNode); + Node childNode1 = store.getGraph(view).getNode("1"); + group.addNode(childNode1); + Node childNode2 = store.getGraph(view).getNode("2"); + group.addNode(childNode2); + + int parentNeighborsCount = graph.getNeighbors(parentNode).toCollection().size(); + + Collection expandedEdges = new HashSet(); + for (int type : types) { + expandedEdges.addAll(graph.getEdges(parentNode, type).toCollection()); + } + Assert.assertFalse(expandedEdges.isEmpty()); + + group.collapse(); + + Collection collapsedEdges = new HashSet(); + for (int type : types) { + collapsedEdges.addAll(graph.getEdges(parentNode, type).toCollection()); + } + Assert.assertFalse(collapsedEdges.isEmpty()); + Assert.assertTrue(collapsedEdges.size() > expandedEdges.size()); + + group.expand(); + + Collection resetEdges = new HashSet(); + for (int type : types) { + resetEdges.addAll(graph.getEdges(parentNode, type).toCollection()); + } + Assert.assertFalse(resetEdges.isEmpty()); + Assert.assertEquals(expandedEdges.size(), resetEdges.size()); + + for (Node child : Arrays.asList(childNode1, childNode2)) { + Collection originalNeighbors = graph.getNeighbors(child).toCollection(); + Assert.assertNotNull(originalNeighbors.isEmpty()); + for (Node neighbor : originalNeighbors) { + Collection edges = new HashSet(); + for (int type : types) { + edges.addAll(graph.getEdges(child, neighbor, type).toCollection()); + edges.addAll(graph.getEdges(neighbor, child, type).toCollection()); + } + Assert.assertFalse(edges.isEmpty()); + } + + group.collapse(); + + Collection collapsedNeighbors = graph.getNeighbors(child).toCollection(); + Assert.assertTrue(collapsedNeighbors.isEmpty()); + Assert.assertTrue(graph.getNeighbors(parentNode).toCollection().size() > parentNeighborsCount); + for (Node neighbor : originalNeighbors) { + Collection edges = new HashSet(); + for (int type : types) { + edges.addAll(graph.getEdges(child, neighbor, type).toCollection()); + edges.addAll(graph.getEdges(neighbor, child, type).toCollection()); + } + Assert.assertTrue(edges.isEmpty()); + } + + group.expand(); + + Collection expandedNeighbors = graph.getNeighbors(child).toCollection(); + Assert.assertEquals(originalNeighbors.size(), expandedNeighbors.size()); + Assert.assertEquals(parentNeighborsCount, graph.getNeighbors(parentNode).toCollection().size()); + for (Node neighbor : originalNeighbors) { + Collection edges = new HashSet(); + for (int type : types) { + edges.addAll(graph.getEdges(child, neighbor, type).toCollection()); + edges.addAll(graph.getEdges(neighbor, child, type).toCollection()); + } + Assert.assertFalse(edges.isEmpty()); + } + } + } } From 63c09be8544e555f30e8e3177310c468a0e759c7 Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Fri, 13 May 2016 00:13:22 +0700 Subject: [PATCH 03/15] set blocking to false for node interators in view decorator since we are already handling that earlier in chain --- .../graph/impl/HierarchicalGraphDecorator.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java index 2f22af66..a37da0e1 100644 --- a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java @@ -89,7 +89,7 @@ public Iterator call() throws Exception { }); } } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -105,7 +105,7 @@ public Iterator call() throws Exception { }); } } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -168,7 +168,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -183,7 +183,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -198,7 +198,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -213,7 +213,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -499,7 +499,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -518,7 +518,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override From b929d0250ec4afec106f7d082ae0241df75478b0 Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Sat, 6 Aug 2016 16:28:28 -0500 Subject: [PATCH 04/15] fixed issue with cast-cast exceptions with edge iterators --- .../impl/HierarchicalGraphDecorator.java | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java index a37da0e1..6abed24d 100644 --- a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java @@ -1077,7 +1077,7 @@ public void remove() { private final class EdgeViewIterator implements Iterator { private final Iterator edgeIterator; - private EdgeImpl pointer; + private Edge pointer; public EdgeViewIterator(Iterator edgeIterator) { this.edgeIterator = edgeIterator; @@ -1090,9 +1090,13 @@ public boolean hasNext() { if (!edgeIterator.hasNext()) { return false; } - pointer = (EdgeImpl) edgeIterator.next(); + pointer = edgeIterator.next(); if (pointer != null) { - if (!view.containsEdge(pointer)) { + if (pointer instanceof EdgeImpl && !view.containsEdge((EdgeImpl) pointer)) { + pointer = null; + } + if (pointer instanceof MappedEdgeDecorator && !view + .containsEdge(((MappedEdgeDecorator) pointer).edge)) { pointer = null; } } @@ -1115,7 +1119,7 @@ public void remove() { private final class UndirectedEdgeViewIterator implements Iterator { private final Iterator itr; - private EdgeImpl pointer; + private Edge pointer; public UndirectedEdgeViewIterator(Iterator itr) { this.itr = itr; @@ -1124,11 +1128,20 @@ public UndirectedEdgeViewIterator(Iterator itr) { @Override public boolean hasNext() { pointer = null; - while (pointer == null || !view.containsEdge(pointer) || isUndirectedToIgnore(pointer)) { + while (pointer == null) { if (!itr.hasNext()) { return false; } - pointer = (EdgeImpl) itr.next(); + pointer = itr.next(); + if (pointer != null) { + if (pointer instanceof EdgeImpl && !view.containsEdge((EdgeImpl) pointer) && isUndirectedToIgnore((EdgeImpl) pointer)) { + pointer = null; + } + if (pointer instanceof MappedEdgeDecorator && !view + .containsEdge(((MappedEdgeDecorator) pointer).edge) && isUndirectedToIgnore(((MappedEdgeDecorator) pointer).edge)) { + pointer = null; + } + } } return true; } @@ -1200,17 +1213,21 @@ private Edge decorateEdge(final Edge edge) { return edge; } - return new MappedEdgeDecorator(edge, mappedSource, mappedTarget); + if (edge instanceof EdgeImpl) { + return new MappedEdgeDecorator((EdgeImpl) edge, mappedSource, mappedTarget); + } else { + return edge; + } } protected class MappedEdgeDecorator implements Edge { - private final Edge edge; + private final EdgeImpl edge; private final Node source; private final Node target; - private MappedEdgeDecorator(final Edge edge, final Node source, final Node target) { + private MappedEdgeDecorator(final EdgeImpl edge, final Node source, final Node target) { this.edge = edge; this.source = source; this.target = target; From a2506a15e80bf9e7b477cd2f70c231d74a281a19 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Wed, 8 Mar 2017 20:52:41 +0100 Subject: [PATCH 05/15] Update dependencies --- store/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/pom.xml b/store/pom.xml index 328c3f39..ba953d89 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -59,7 +59,7 @@ org.testng testng - 6.10 + 6.11 test From 57e32d978c4718d01c140f98f0370286347dbfdb Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Wed, 5 Apr 2017 17:49:20 +0200 Subject: [PATCH 06/15] Update dependencies --- store/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/store/pom.xml b/store/pom.xml index ba953d89..4a26404c 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -75,7 +75,7 @@ joda-time joda-time - 2.9.7 + 2.9.9 @@ -140,7 +140,7 @@ com.versioneye versioneye-maven-plugin - 3.11.1 + 3.11.2 public true From 3ccb0155e3c3b331d6809890c1e60858c94b06a0 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Sun, 23 Apr 2017 12:46:09 +0200 Subject: [PATCH 07/15] Update dependencies --- store/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/pom.xml b/store/pom.xml index 4a26404c..616d1277 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -90,7 +90,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.19.1 + 2.20 org.apache.maven.plugins From 052c573f60a7059f3fe608f67a9e1951a348cad2 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Wed, 3 May 2017 13:41:37 +0200 Subject: [PATCH 08/15] Update dependencies --- store/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/pom.xml b/store/pom.xml index 616d1277..7609dfd8 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -65,7 +65,7 @@ it.unimi.dsi fastutil - 7.1.0 + 7.2.0 colt From fa83a2e1c1334ed868c71b14de9df3c041ebecaa Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Sun, 10 Apr 2016 13:35:50 +0700 Subject: [PATCH 09/15] added #HierarchicalGraphView api and unit tests --- .../java/org/gephi/graph/api/GraphModel.java | 12 +- .../graph/api/HierarchicalGraphView.java | 25 + .../graph/api/HierarchicalNodeGroup.java | 54 + .../gephi/graph/impl/AbstractGraphView.java | 105 ++ .../org/gephi/graph/impl/GraphModelImpl.java | 6 + .../java/org/gephi/graph/impl/GraphStore.java | 50 +- .../gephi/graph/impl/GraphViewDecorator.java | 13 +- .../org/gephi/graph/impl/GraphViewImpl.java | 125 +- .../org/gephi/graph/impl/GraphViewStore.java | 114 +- .../impl/HierarchicalGraphDecorator.java | 1574 +++++++++++++++++ .../graph/impl/HierarchicalGraphViewImpl.java | 513 ++++++ .../org/gephi/graph/impl/Serialization.java | 8 +- .../gephi/graph/impl/GraphViewStoreTest.java | 4 +- .../graph/impl/HierarchicalGraphTest.java | 115 ++ 14 files changed, 2576 insertions(+), 142 deletions(-) create mode 100644 store/src/main/java/org/gephi/graph/api/HierarchicalGraphView.java create mode 100644 store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java create mode 100644 store/src/main/java/org/gephi/graph/impl/AbstractGraphView.java create mode 100644 store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java create mode 100644 store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java create mode 100644 store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java diff --git a/store/src/main/java/org/gephi/graph/api/GraphModel.java b/store/src/main/java/org/gephi/graph/api/GraphModel.java index ff2ea072..b0311904 100644 --- a/store/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/store/src/main/java/org/gephi/graph/api/GraphModel.java @@ -15,11 +15,12 @@ */ package org.gephi.graph.api; +import org.gephi.graph.impl.GraphModelImpl; +import org.joda.time.DateTimeZone; + import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import org.gephi.graph.impl.GraphModelImpl; -import org.joda.time.DateTimeZone; /** * Graph API's entry point. @@ -351,6 +352,13 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce */ public GraphView createView(boolean node, boolean edge); + /** + * Creates a new hierarchical graph view. + * + * @return newly created hierarchical graph view + */ + public HierarchicalGraphView createHierarchicalView(); + /** * Creates a new graph view based on an existing view. * diff --git a/store/src/main/java/org/gephi/graph/api/HierarchicalGraphView.java b/store/src/main/java/org/gephi/graph/api/HierarchicalGraphView.java new file mode 100644 index 00000000..392c3c9a --- /dev/null +++ b/store/src/main/java/org/gephi/graph/api/HierarchicalGraphView.java @@ -0,0 +1,25 @@ +package org.gephi.graph.api; + +/** + * A hierarchical graph view which allows for collapsible sub-groups. Each node, + * whether it is collapsed or extended, should be in core in view. If a node is + * node is hidden by collapsed parent, it will omitted within + * directed/undirected graph queries. + */ +public interface HierarchicalGraphView extends GraphView { + /** + * @return Return iterator for each hierarchical node group. + */ + Iterable getGroups(); + + /** + * @return Return root node which first-tier nodes should be attached. + */ + HierarchicalNodeGroup getRoot(); + + /** + * @param node node within graph graph + * @return Return existing hierarchical group if available; else null. + */ + HierarchicalNodeGroup getGroup(Node node); +} diff --git a/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java new file mode 100644 index 00000000..1a64b085 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java @@ -0,0 +1,54 @@ +package org.gephi.graph.api; + +/** + * A group of nodes within a hierarchical graph view. + */ +public interface HierarchicalNodeGroup { + /** + * @return Returns true if the group is collapsed, which means the children + * are hidden from visible graph. + */ + boolean isCollapsed(); + + /** + * @return Returns true if the group is expanded, which means the children + * are visible within visible graph. + */ + boolean isExpanded(); + + /** + * Expand node, displaying children. + */ + void expand(); + + /** + * Collapse node, hiding children. + */ + void collapse(); + + /** + * @return Return iterator containing children nodes (not recursive). + */ + Iterable getNodes(); + + /** + * @return Return iterator containing children nodes. + */ + Iterable getNodes(boolean recursive); + + /** + * Add node to this group. + * + * @param node child node + * @return Returns child hierarchical group, if created; else null. + */ + HierarchicalNodeGroup addNode(Node node); + + /** + * Remove node from group. + * + * @param node child node + * @return Returns true if child hierarchical group was remoce; else false. + */ + boolean removeNode(Node node); +} diff --git a/store/src/main/java/org/gephi/graph/impl/AbstractGraphView.java b/store/src/main/java/org/gephi/graph/impl/AbstractGraphView.java new file mode 100644 index 00000000..0e6a71d7 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/AbstractGraphView.java @@ -0,0 +1,105 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphObserver; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.UndirectedSubgraph; + +abstract public class AbstractGraphView implements GraphView { + protected final GraphStore graphStore; + + protected final GraphAttributesImpl attributes; + + protected final boolean nodeView; + + protected final boolean edgeView; + + private Interval interval; + + private int storeId = GraphViewStore.NULL_VIEW; + + public AbstractGraphView(final GraphStore store, boolean nodes, boolean edges) { + this.graphStore = store; + this.nodeView = nodes; + this.edgeView = edges; + this.interval = Interval.INFINITY_INTERVAL; + this.attributes = new GraphAttributesImpl(); + } + + public AbstractGraphView(final AbstractGraphView view, boolean nodes, boolean edges) { + this.graphStore = view.graphStore; + this.nodeView = nodes; + this.edgeView = edges; + this.interval = view.interval; + this.attributes = new GraphAttributesImpl(); + } + + public int getStoreId() { + return this.storeId; + } + + protected void setStoreId(final int id) { + this.storeId = id; + } + + @Override + public boolean isDestroyed() { + return GraphViewStore.NULL_VIEW == this.storeId; + } + + @Override + public GraphModelImpl getGraphModel() { + return this.graphStore.graphModel; + } + + @Override + public boolean isMainView() { + return false; + } + + @Override + public boolean isNodeView() { + return this.nodeView; + } + + @Override + public boolean isEdgeView() { + return this.edgeView; + } + + public void setTimeInterval(Interval interval) { + if (interval == null) { + interval = Interval.INFINITY_INTERVAL; + } + this.interval = interval; + } + + @Override + public Interval getTimeInterval() { + return this.interval; + } + + abstract public DirectedSubgraph getDirectedGraph(); + + abstract public UndirectedSubgraph getUndirectedGraph(); + + abstract public boolean deepEquals(AbstractGraphView view); + + abstract public int deepHashCode(); + + abstract protected void viewDestroyed(); + + abstract protected void nodeAdded(NodeImpl node); + + abstract protected void nodeRemoved(NodeImpl node); + + abstract protected void edgeAdded(EdgeImpl edge); + + abstract protected void edgeRemoved(EdgeImpl edge); + + abstract protected GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff); + + abstract protected void destroyGraphObserver(GraphObserver graphObserver); +} diff --git a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index f7945b0c..86a52435 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -16,6 +16,7 @@ package org.gephi.graph.impl; import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.HierarchicalGraphView; import org.gephi.graph.api.Index; import org.gephi.graph.api.Table; import org.gephi.graph.api.TimeFormat; @@ -238,6 +239,11 @@ public GraphView createView(boolean node, boolean edge) { return store.viewStore.createView(node, edge); } + @Override + public HierarchicalGraphView createHierarchicalView() { + return store.viewStore.createHierarchicalView(); + } + @Override public GraphView copyView(GraphView view) { return store.viewStore.createView(view); diff --git a/store/src/main/java/org/gephi/graph/impl/GraphStore.java b/store/src/main/java/org/gephi/graph/impl/GraphStore.java index f28915eb..3a325396 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -15,16 +15,7 @@ */ package org.gephi.graph.impl; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Set; import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Origin; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.types.TimestampSet; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; @@ -32,39 +23,66 @@ import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.Origin; import org.gephi.graph.api.Subgraph; -import org.joda.time.DateTimeZone; +import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalSet; +import org.gephi.graph.api.types.TimestampSet; +import org.joda.time.DateTimeZone; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; public class GraphStore implements DirectedGraph, DirectedSubgraph { protected final GraphModelImpl graphModel; + protected final Configuration configuration; + // Stores protected final NodeStore nodeStore; + protected final EdgeStore edgeStore; + protected final EdgeTypeStore edgeTypeStore; + protected final TableImpl nodeTable; + protected final TableImpl edgeTable; + protected final GraphViewStore viewStore; + protected final TimeStore timeStore; + protected final GraphAttributesImpl attributes; + // Factory protected final GraphFactoryImpl factory; + // Lock protected final GraphLock lock; + // Version protected final GraphVersion version; + protected final List observers; + // Undirected protected final UndirectedDecorator undirectedDecorator; + // Main Graph view protected final GraphView mainGraphView; + // TimeFormat protected TimeFormat timeFormat; + // Time zone protected DateTimeZone timeZone; @@ -822,6 +840,7 @@ public boolean deepEquals(GraphStore obj) { protected class NodeIterableWrapper implements NodeIterable { protected final Iterator iterator; + protected final boolean blocking; public NodeIterableWrapper(Iterator iterator) { @@ -842,7 +861,10 @@ public Iterator iterator() { public Node[] toArray() { List list = new ArrayList(); for (; iterator.hasNext();) { - list.add(iterator.next()); + Node node = iterator.next(); + if (node != null) { + list.add(node); + } } return list.toArray(new Node[0]); } @@ -851,7 +873,10 @@ public Node[] toArray() { public Collection toCollection() { List list = new ArrayList(); for (; iterator.hasNext();) { - list.add(iterator.next()); + Node node = iterator.next(); + if (node != null) { + list.add(node); + } } return list; } @@ -867,6 +892,7 @@ public void doBreak() { protected class EdgeIterableWrapper implements EdgeIterable { protected final Iterator iterator; + protected final boolean blocking; public EdgeIterableWrapper(Iterator iterator) { diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index e5086098..c8b590fc 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -15,9 +15,6 @@ */ package org.gephi.graph.impl; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; @@ -30,6 +27,10 @@ import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + public class GraphViewDecorator implements DirectedSubgraph, UndirectedSubgraph { protected final boolean undirected; @@ -736,10 +737,10 @@ void checkValidViewObject(final GraphView view) { if (view == null) { throw new NullPointerException(); } - if (!(view instanceof GraphViewImpl)) { - throw new ClassCastException("Object must be a GraphViewImpl object"); + if (!(view instanceof AbstractGraphView)) { + throw new ClassCastException("Object must be a AbstractGraphView object"); } - if (((GraphViewImpl) view).graphStore != graphStore) { + if (((AbstractGraphView) view).graphStore != graphStore) { throw new RuntimeException("The view doesn't belong to this store"); } } diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index a4e7ce0c..501abc77 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -17,30 +17,26 @@ import cern.colt.bitvector.BitVector; import cern.colt.bitvector.QuickBitVector; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphObserver; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Node; import org.gephi.graph.api.UndirectedSubgraph; import org.gephi.graph.impl.EdgeStore.EdgeInOutIterator; -public class GraphViewImpl implements GraphView { +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +public class GraphViewImpl extends AbstractGraphView implements GraphView { // Data - protected final GraphStore graphStore; - protected final boolean nodeView; - protected final boolean edgeView; - protected final GraphAttributesImpl attributes; protected BitVector nodeBitVector; protected BitVector edgeBitVector; - protected int storeId; // Version protected final GraphVersion version; protected final List observers; @@ -53,14 +49,9 @@ public class GraphViewImpl implements GraphView { protected int[] typeCounts; protected int[] mutualEdgeTypeCounts; protected int mutualEdgesCount; - // Dynamic - protected Interval interval; public GraphViewImpl(final GraphStore store, boolean nodes, boolean edges) { - this.graphStore = store; - this.nodeView = nodes; - this.edgeView = edges; - this.attributes = new GraphAttributesImpl(); + super(store, nodes, edges); if (nodes) { this.nodeBitVector = new BitVector(store.nodeStore.maxStoreId()); } else { @@ -74,14 +65,10 @@ public GraphViewImpl(final GraphStore store, boolean nodes, boolean edges) { this.undirectedDecorator = new GraphViewDecorator(graphStore, this, true); this.version = graphStore.version != null ? new GraphVersion(directedDecorator) : null; this.observers = graphStore.version != null ? new ArrayList() : null; - this.interval = Interval.INFINITY_INTERVAL; } public GraphViewImpl(final GraphViewImpl view, boolean nodes, boolean edges) { - this.graphStore = view.graphStore; - this.nodeView = nodes; - this.edgeView = edges; - this.attributes = new GraphAttributesImpl(); + super(view, nodes, edges); if (nodes) { this.nodeBitVector = view.nodeBitVector.copy(); this.nodeCount = view.nodeCount; @@ -98,14 +85,15 @@ public GraphViewImpl(final GraphViewImpl view, boolean nodes, boolean edges) { this.undirectedDecorator = new GraphViewDecorator(graphStore, this, true); this.version = graphStore.version != null ? new GraphVersion(directedDecorator) : null; this.observers = graphStore.version != null ? new ArrayList() : null; - this.interval = view.interval; } - protected DirectedSubgraph getDirectedGraph() { + @Override + public DirectedSubgraph getDirectedGraph() { return directedDecorator; } - protected UndirectedSubgraph getUndirectedGraph() { + @Override + public UndirectedSubgraph getUndirectedGraph() { return undirectedDecorator; } @@ -563,42 +551,6 @@ public int getUndirectedEdgeCount(int type) { } @Override - public GraphModelImpl getGraphModel() { - return graphStore.graphModel; - } - - @Override - public boolean isMainView() { - return false; - } - - @Override - public boolean isNodeView() { - return nodeView; - } - - @Override - public boolean isEdgeView() { - return edgeView; - } - - public void setTimeInterval(Interval interval) { - if (interval == null) { - interval = Interval.INFINITY_INTERVAL; - } - this.interval = interval; - } - - @Override - public Interval getTimeInterval() { - return interval; - } - - @Override - public boolean isDestroyed() { - return storeId == GraphViewStore.NULL_VIEW; - } - protected GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { if (observers != null) { GraphObserverImpl observer = new GraphObserverImpl(graphStore, version, graph, withDiff); @@ -609,10 +561,11 @@ protected GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { return null; } - protected void destroyGraphObserver(GraphObserverImpl observer) { + @Override + protected void destroyGraphObserver(GraphObserver observer) { if (observers != null) { observers.remove(observer); - observer.destroyObserver(); + ((GraphObserverImpl) observer).destroyObserver(); } } @@ -625,6 +578,35 @@ protected void destroyAllObservers() { } } + @Override + protected void viewDestroyed() { + this.setStoreId(GraphViewStore.NULL_VIEW); + this.destroyAllObservers(); + } + + @Override + protected void nodeAdded(NodeImpl node) { + this.ensureNodeVectorSize(node); + } + + @Override + protected void nodeRemoved(NodeImpl node) { + this.removeNode(node); + } + + @Override + protected void edgeAdded(EdgeImpl edge) { + this.ensureEdgeVectorSize(edge); + if (this.nodeView && !this.edgeView) { + this.addEdgeInNodeView(edge); + } + } + + @Override + protected void edgeRemoved(EdgeImpl edge) { + this.removeEdge(edge); + } + protected void ensureNodeVectorSize(NodeImpl node) { int sid = node.storeId; if (sid >= nodeBitVector.size()) { @@ -726,6 +708,7 @@ private void ensureTypeCountArrayCapacity(int type) { } } + @Override public int deepHashCode() { int hash = 5; hash = 17 * hash + (this.nodeView ? 1 : 0); @@ -737,14 +720,19 @@ public int deepHashCode() { hash = 11 * hash + Arrays.hashCode(this.typeCounts); hash = 11 * hash + Arrays.hashCode(this.mutualEdgeTypeCounts); hash = 11 * hash + this.mutualEdgesCount; - hash = 11 * hash + (this.interval != null ? this.interval.hashCode() : 0); + hash = 11 * hash + (this.getTimeInterval() != null ? this.getTimeInterval().hashCode() : 0); return hash; } - public boolean deepEquals(GraphViewImpl obj) { - if (obj == null) { + @Override + public boolean deepEquals(AbstractGraphView view) { + if (view == null) { + return false; + } + if (!(view instanceof GraphViewImpl)) { return false; } + GraphViewImpl obj = (GraphViewImpl) view; if (this.nodeBitVector != obj.nodeBitVector && (this.nodeBitVector == null || !this.nodeBitVector .equals(obj.nodeBitVector))) { return false; @@ -774,7 +762,8 @@ public boolean deepEquals(GraphViewImpl obj) { if (this.mutualEdgesCount != obj.mutualEdgesCount) { return false; } - if (this.interval != obj.interval && (this.interval == null || !this.interval.equals(obj.interval))) { + if (this.getTimeInterval() != obj.getTimeInterval() && (this.getTimeInterval() == null || !this + .getTimeInterval().equals(obj.getTimeInterval()))) { return false; } return true; diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 599b485e..8d6b9ed3 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -17,11 +17,12 @@ import it.unimi.dsi.fastutil.ints.IntRBTreeSet; import it.unimi.dsi.fastutil.ints.IntSortedSet; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.HierarchicalGraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; @@ -35,7 +36,7 @@ public class GraphViewStore { // Data protected final IntSortedSet garbageQueue; protected final GraphStore graphStore; - protected GraphViewImpl[] views; + protected AbstractGraphView[] views; protected int length; // Visible view protected GraphView visibleView; @@ -45,11 +46,26 @@ public GraphViewStore(GraphStore graphStore) { throw new NullPointerException(); } this.graphStore = graphStore; - this.views = new GraphViewImpl[DEFAULT_VIEWS]; + this.views = new AbstractGraphView[DEFAULT_VIEWS]; this.garbageQueue = new IntRBTreeSet(); this.visibleView = graphStore.mainGraphView; } + public HierarchicalGraphView createHierarchicalView() { + return createHierarchicalView(true, true); + } + + public HierarchicalGraphView createHierarchicalView(boolean nodes, boolean edges) { + graphStore.autoWriteLock(); + try { + HierarchicalGraphViewImpl graphView = new HierarchicalGraphViewImpl(graphStore, nodes, edges); + addView(graphView); + return graphView; + } finally { + graphStore.autoWriteUnlock(); + } + } + public GraphViewImpl createView() { return createView(true, true); } @@ -82,11 +98,18 @@ public GraphViewImpl createView(GraphView view, boolean nodes, boolean edges) { } } else { checkNonNullViewObject(view); - checkViewExist((GraphViewImpl) view); - + checkViewExist((AbstractGraphView) view); graphStore.autoWriteLock(); try { - GraphViewImpl graphView = new GraphViewImpl((GraphViewImpl) view, nodes, edges); + final GraphViewImpl copy; + if (view instanceof GraphViewImpl) { + copy = (GraphViewImpl) view; + } else if (view instanceof HierarchicalGraphViewImpl) { + copy = ((HierarchicalGraphViewImpl) view).viewDelegate; + } else { + throw new IllegalArgumentException(); + } + GraphViewImpl graphView = new GraphViewImpl(copy, nodes, edges); addView(graphView); return graphView; } finally { @@ -102,25 +125,25 @@ public void destroyView(GraphView view) { TimeIndexStore nodeTimeStore = graphStore.timeStore.nodeIndexStore; if (nodeTimeStore != null) { - nodeTimeStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); + nodeTimeStore.deleteViewIndex(((AbstractGraphView) view).getDirectedGraph()); } TimeIndexStore edgeTimeStore = graphStore.timeStore.edgeIndexStore; if (edgeTimeStore != null) { - edgeTimeStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); + edgeTimeStore.deleteViewIndex(((AbstractGraphView) view).getDirectedGraph()); } IndexStore nodeIndexStore = graphStore.nodeTable.store.indexStore; if (nodeIndexStore != null) { - nodeIndexStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); + nodeIndexStore.deleteViewIndex(((AbstractGraphView) view).getDirectedGraph()); } IndexStore edgeIndexStore = graphStore.edgeTable.store.indexStore; if (edgeIndexStore != null) { - edgeIndexStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); + edgeIndexStore.deleteViewIndex(((AbstractGraphView) view).getDirectedGraph()); } - removeView((GraphViewImpl) view); + removeView((AbstractGraphView) view); } finally { graphStore.autoWriteUnlock(); } @@ -128,11 +151,11 @@ public void destroyView(GraphView view) { public void setTimeInterval(GraphView view, Interval interval) { checkNonNullViewObject(view); - checkViewExist((GraphViewImpl) view); + checkViewExist((AbstractGraphView) view); graphStore.autoWriteLock(); try { - GraphViewImpl graphView = (GraphViewImpl) view; + AbstractGraphView graphView = (AbstractGraphView) view; graphView.setTimeInterval(interval); } finally { graphStore.autoWriteUnlock(); @@ -143,8 +166,8 @@ public boolean contains(GraphView view) { graphStore.autoReadLock(); try { checkNonNullViewObject(view); - GraphViewImpl viewImpl = (GraphViewImpl) view; - int id = viewImpl.storeId; + AbstractGraphView viewImpl = (AbstractGraphView) view; + int id = viewImpl.getStoreId(); if (id != NULL_VIEW && id < length && views[id] == view) { return true; } @@ -165,12 +188,12 @@ public Subgraph getGraph(GraphView view) { if (view.isMainView()) { return graphStore.undirectedDecorator; } - return ((GraphViewImpl) view).getUndirectedGraph(); + return ((AbstractGraphView) view).getUndirectedGraph(); } else { if (view.isMainView()) { return graphStore; } - return ((GraphViewImpl) view).getDirectedGraph(); + return ((AbstractGraphView) view).getDirectedGraph(); } } @@ -182,7 +205,7 @@ public DirectedSubgraph getDirectedGraph(GraphView view) { } checkDirectedAllowed(); - return ((GraphViewImpl) view).getDirectedGraph(); + return ((AbstractGraphView) view).getDirectedGraph(); } public UndirectedSubgraph getUndirectedGraph(GraphView view) { @@ -191,7 +214,7 @@ public UndirectedSubgraph getUndirectedGraph(GraphView view) { if (view.isMainView()) { return graphStore.undirectedDecorator; } - return ((GraphViewImpl) view).getUndirectedGraph(); + return ((AbstractGraphView) view).getUndirectedGraph(); } public GraphView getVisibleView() { @@ -203,20 +226,20 @@ public void setVisibleView(GraphView view) { visibleView = graphStore.mainGraphView; } else { checkNonNullViewObject(view); - checkViewExist((GraphViewImpl) view); + checkViewExist((AbstractGraphView) view); visibleView = view; } } public GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { - GraphViewImpl graphViewImpl = (GraphViewImpl) graph.getView(); + AbstractGraphView graphViewImpl = (AbstractGraphView) graph.getView(); checkViewExist(graphViewImpl); return graphViewImpl.createGraphObserver(graph, withDiff); } public void destroyGraphObserver(GraphObserverImpl graphObserver) { - GraphViewImpl graphViewImpl = (GraphViewImpl) graphObserver.graph.getView(); + AbstractGraphView graphViewImpl = (AbstractGraphView) graphObserver.graph.getView(); checkViewExist(graphViewImpl); graphViewImpl.destroyGraphObserver(graphObserver); @@ -224,9 +247,9 @@ public void destroyGraphObserver(GraphObserverImpl graphObserver) { protected void addNode(NodeImpl node) { if (views.length > 0) { - for (GraphViewImpl view : views) { + for (AbstractGraphView view : views) { if (view != null) { - view.ensureNodeVectorSize(node); + view.nodeAdded(node); } } } @@ -234,9 +257,9 @@ protected void addNode(NodeImpl node) { protected void removeNode(NodeImpl node) { if (views.length > 0) { - for (GraphViewImpl view : views) { + for (AbstractGraphView view : views) { if (view != null) { - view.removeNode(node); + view.nodeRemoved(node); } } } @@ -244,13 +267,9 @@ protected void removeNode(NodeImpl node) { protected void addEdge(EdgeImpl edge) { if (views.length > 0) { - for (GraphViewImpl view : views) { + for (AbstractGraphView view : views) { if (view != null) { - view.ensureEdgeVectorSize(edge); - - if (view.nodeView && !view.edgeView) { - view.addEdgeInNodeView(edge); - } + view.edgeAdded(edge); } } } @@ -258,15 +277,15 @@ protected void addEdge(EdgeImpl edge) { protected void removeEdge(EdgeImpl edge) { if (views.length > 0) { - for (GraphViewImpl view : views) { + for (AbstractGraphView view : views) { if (view != null) { - view.removeEdge(edge); + view.edgeRemoved(edge); } } } } - protected int addView(final GraphViewImpl view) { + protected int addView(final AbstractGraphView view) { checkNonNullViewObject(view); int id; @@ -278,19 +297,18 @@ protected int addView(final GraphViewImpl view) { ensureArraySize(id); } views[id] = view; - view.storeId = id; + view.setStoreId(id); return id; } - protected void removeView(final GraphViewImpl view) { + protected void removeView(final AbstractGraphView view) { checkViewExist(view); - int id = view.storeId; + int id = view.getStoreId(); views[id] = null; garbageQueue.add(id); - view.storeId = NULL_VIEW; - view.destroyAllObservers(); + view.viewDestroyed(); // Check if not visible view if (visibleView == view) { @@ -300,7 +318,7 @@ protected void removeView(final GraphViewImpl view) { private void ensureArraySize(int index) { if (index >= views.length) { - GraphViewImpl[] newArray = new GraphViewImpl[index + 1]; + AbstractGraphView[] newArray = new AbstractGraphView[index + 1]; System.arraycopy(views, 0, newArray, 0, views.length); views = newArray; } @@ -308,7 +326,7 @@ private void ensureArraySize(int index) { public int deepHashCode() { int hash = 5; - for (GraphViewImpl view : this.views) { + for (AbstractGraphView view : this.views) { hash = 67 * hash + view.deepHashCode(); } hash = 67 * hash + this.length; @@ -327,8 +345,8 @@ public boolean deepEquals(GraphViewStore obj) { return false; } for (int i = 0; i < l; i++) { - GraphViewImpl e1 = this.views[i]; - GraphViewImpl e2 = obj.views[i]; + AbstractGraphView e1 = this.views[i]; + AbstractGraphView e2 = obj.views[i]; if (e1 == e2) { continue; @@ -349,14 +367,14 @@ protected void checkNonNullViewObject(final Object o) { throw new NullPointerException(); } if (o != graphStore.mainGraphView) { - if (!(o instanceof GraphViewImpl)) { - throw new ClassCastException("View must be a GraphViewImpl object"); + if (!(o instanceof AbstractGraphView)) { + throw new ClassCastException("View must be a AbstractGraphView object"); } } } - protected void checkViewExist(final GraphViewImpl view) { - int id = view.storeId; + protected void checkViewExist(final AbstractGraphView view) { + final int id = view.getStoreId(); if (id == NULL_VIEW || id >= length || views[id] != view) { throw new IllegalArgumentException("The view doesn't exist"); } diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java new file mode 100644 index 00000000..57cfbe66 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java @@ -0,0 +1,1574 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.Column; +import org.gephi.graph.api.ColumnIterable; +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.Subgraph; +import org.gephi.graph.api.Table; +import org.gephi.graph.api.TextProperties; +import org.gephi.graph.api.UndirectedSubgraph; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +public class HierarchicalGraphDecorator implements DirectedSubgraph, UndirectedSubgraph { + private final boolean undirected; + + private final HierarchicalGraphViewImpl view; + + private final GraphStore graphStore; + + public HierarchicalGraphDecorator(GraphStore graphStore, HierarchicalGraphViewImpl view, boolean undirected) { + this.graphStore = graphStore; + this.view = view; + this.undirected = undirected; + } + + @Override + public Edge getEdge(Node node1, Node node2) { + graphStore.autoReadLock(); + try { + for (final Node n1 : view.mapWithHidden(node1)) { + for (final Node n2 : view.mapWithHidden(node2)) { + EdgeImpl edge = graphStore.edgeStore.get(n1, n2, undirected); + if (edge != null && view.containsEdge(edge)) { + return decorateEdge(edge); + } + } + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public Edge getEdge(Node node1, Node node2, int type) { + graphStore.autoReadLock(); + try { + for (final Node n1 : view.mapWithHidden(node1)) { + for (final Node n2 : view.mapWithHidden(node2)) { + EdgeImpl edge = graphStore.edgeStore.get(n1, n2, type, undirected); + if (edge != null && view.containsEdge(edge)) { + return decorateEdge(edge); + } + } + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public EdgeIterable getEdges(final Node node1, final Node node2) { + List>> list = new ArrayList>>(); + for (final Node n1 : view.mapWithHidden(node1)) { + for (final Node n2 : view.mapWithHidden(node2)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.getAll(n1, n2, undirected); + } + }); + } + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getEdges(final Node node1, final Node node2, final int type) { + List>> list = new ArrayList>>(); + for (final Node n1 : view.mapWithHidden(node1)) { + for (final Node n2 : view.mapWithHidden(node2)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.getAll(n1, n2, type, undirected); + } + }); + } + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public Edge getMutualEdge(Edge edge) { + graphStore.autoReadLock(); + try { + final Edge unpacked = undecorateEdge(edge); + for (final Node n1 : view.mapWithHidden(unpacked.getSource())) { + for (final Node n2 : view.mapWithHidden(unpacked.getTarget())) { + Edge e = graphStore.getEdge(n1, n2); + EdgeImpl mutual = graphStore.edgeStore.getMutualEdge(e); + if (mutual != null && view.containsEdge(mutual)) { + return decorateEdge(mutual); + } + } + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public NodeIterable getPredecessors(Node node) { + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( + graphStore.edgeStore.edgeInIterator(node)))); + } + + @Override + public NodeIterable getPredecessors(Node node, int type) { + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( + graphStore.edgeStore.edgeInIterator(node, type)))); + } + + @Override + public NodeIterable getSuccessors(Node node) { + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( + graphStore.edgeStore.edgeOutIterator(node)))); + } + + @Override + public NodeIterable getSuccessors(Node node, int type) { + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( + graphStore.edgeStore.edgeOutIterator(node, type)))); + } + + @Override + public EdgeIterable getInEdges(Node node) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.edgeInIterator(n); + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getInEdges(final Node node, final int type) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.edgeInIterator(n, type); + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getOutEdges(final Node node) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.edgeOutIterator(n); + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getOutEdges(final Node node, final int type) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + return graphStore.edgeStore.edgeOutIterator(n, type); + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public boolean isAdjacent(Node source, Node target) { + checkValidInViewNodeObject(source); + checkValidInViewNodeObject(target); + graphStore.autoReadLock(); + try { + for (final Node mappedSource : view.mapWithHidden(source)) { + for (final Node mappedTarget : view.mapWithHidden(target)) { + EdgeImpl edge = graphStore.edgeStore.get(mappedSource, mappedTarget, undirected); + if (edge != null && view.containsEdge(edge)) { + return true; + } + } + } + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean isAdjacent(Node source, Node target, int type) { + checkValidInViewNodeObject(source); + checkValidInViewNodeObject(target); + graphStore.autoReadLock(); + try { + for (final Node mappedSource : view.mapWithHidden(source)) { + for (final Node mappedTarget : view.mapWithHidden(target)) { + EdgeImpl edge = graphStore.edgeStore.get(mappedSource, mappedTarget, type, undirected); + if (edge != null && view.containsEdge(edge)) { + return true; + } + } + } + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean addEdge(Edge edge) { + Edge unpacked = undecorateEdge(edge); + checkValidEdgeObject(unpacked); + graphStore.autoWriteLock(); + try { + return view.addEdge(unpacked); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean addNode(Node node) { + checkValidNodeObject(node); + graphStore.autoWriteLock(); + try { + return view.addNode(node); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean addAllEdges(Collection edges) { + graphStore.autoWriteLock(); + try { + boolean updated = false; + for (final Edge edge : edges) { + updated |= view.addEdge(edge); + } + return updated; + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean addAllNodes(Collection nodes) { + graphStore.autoWriteLock(); + try { + boolean updated = false; + for (final Node node : nodes) { + updated |= view.addNode(node); + } + return updated; + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean removeEdge(Edge edge) { + Edge unpacked = undecorateEdge(edge); + checkValidEdgeObject(unpacked); + graphStore.autoWriteLock(); + try { + return view.removeEdge(unpacked); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean removeNode(Node node) { + checkValidNodeObject(node); + graphStore.autoWriteLock(); + try { + return view.removeNode(node); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean removeAllEdges(Collection edges) { + graphStore.autoWriteLock(); + try { + return this.removeAllEdgesWithLock(edges.iterator()); + } finally { + graphStore.autoWriteUnlock(); + } + } + + private boolean removeAllEdgesWithLock(Iterator itr) { + if (null == itr) { + return false; + } + + boolean updated = false; + while (itr.hasNext()) { + final Edge edge = itr.next(); + if (edge != null) { + updated |= view.removeEdge(undecorateEdge(edge)); + } + } + return updated; + } + + @Override + public boolean removeAllNodes(Collection nodes) { + graphStore.autoWriteLock(); + try { + boolean updated = false; + for (final Node node : nodes) { + updated |= view.removeNode(node); + } + return updated; + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean contains(Node node) { + checkValidNodeObject(node); + graphStore.autoReadLock(); + try { + return view.containsNode((NodeImpl) node) && view.visibleNode((NodeImpl) node); + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean contains(Edge edge) { + Edge unpacked = undecorateEdge(edge); + checkValidEdgeObject(unpacked); + graphStore.autoReadLock(); + try { + return view.containsEdge((EdgeImpl) unpacked); + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public Node getNode(Object id) { + graphStore.autoReadLock(); + try { + NodeImpl node = graphStore.getNode(id); + if (node != null && view.containsNode(node) && view.visibleNode(node)) { + return node; + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean hasNode(final Object id) { + return getNode(id) != null; + } + + @Override + public Edge getEdge(Object id) { + graphStore.autoReadLock(); + try { + EdgeImpl edge = graphStore.getEdge(id); + if (edge != null && view.containsEdge(edge)) { + return decorateEdge(edge); + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean hasEdge(final Object id) { + return getEdge(id) != null; + } + + @Override + public NodeIterable getNodes() { + return graphStore.getNodeIterableWrapper(new NodeViewIterator(graphStore.nodeStore.iterator())); + } + + @Override + public EdgeIterable getEdges() { + if (undirected) { + return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator(graphStore.edgeStore.iterator())); + } else { + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.iterator())); + } + } + + @Override + public EdgeIterable getSelfLoops() { + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.iteratorSelfLoop())); + } + + @Override + public NodeIterable getNeighbors(Node node) { + checkValidInViewNodeObject(node); + Set set = new HashSet(); + for (final Node n : view.mapWithHidden(node)) { + final Iterator itr = new NeighborsIterator((NodeImpl) n, new UndirectedEdgeViewIterator( + graphStore.edgeStore.edgeIterator(n))); + while (itr.hasNext()) { + final Node neighbor = itr.next(); + if (neighbor != null) { + set.add(neighbor); + } + } + } + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator())); + } + + @Override + public NodeIterable getNeighbors(Node node, int type) { + checkValidInViewNodeObject(node); + Set set = new HashSet(); + for (final Node n : view.mapWithHidden(node)) { + final Iterator itr = new NeighborsIterator((NodeImpl) n, new UndirectedEdgeViewIterator( + graphStore.edgeStore.edgeIterator(n, type))); + while (itr.hasNext()) { + final Node neighbor = itr.next(); + if (neighbor != null) { + set.add(neighbor); + } + } + } + checkValidInViewNodeObject(node); + return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator())); + } + + @Override + public EdgeIterable getEdges(Node node) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + if (undirected) { + return new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(n)); + } else { + return graphStore.edgeStore.edgeIterator(n); + } + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public EdgeIterable getEdges(final Node node, final int type) { + checkValidInViewNodeObject(node); + List>> list = new ArrayList>>(); + for (final Node n : view.mapWithHidden(node)) { + list.add(new Callable>() { + @Override + public Iterator call() throws Exception { + if (undirected) { + return new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(n, type)); + } else { + return graphStore.edgeStore.edgeIterator(n, type); + } + } + }); + } + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + } + + @Override + public int getNodeCount() { + return view.getNodeCount(); + } + + @Override + public int getEdgeCount() { + if (undirected) { + return view.getUndirectedEdgeCount(); + } else { + return view.getEdgeCount(); + } + } + + @Override + public int getEdgeCount(int type) { + if (undirected) { + return view.getUndirectedEdgeCount(type); + } else { + return view.getEdgeCount(type); + } + } + + @Override + public Node getOpposite(Node node, Edge edge) { + Edge e = undecorateEdge(edge); + Node n1 = view.mapToVisible(e.getSource()); + Node n2 = view.mapToVisible(e.getTarget()); + checkValidInViewNodeObject(n1); + checkValidInViewNodeObject(n2); + checkValidInViewNodeObject(node); + checkValidInViewEdgeObject(e); + if (n1.equals(node)) { + return n2; + } else if (n2.equals(node)) { + return n1; + } else { + return null; + } + } + + @Override + public int getDegree(final Node node) { + if (!this.contains(node)) { + return 0; + } + + if (undirected) { + int count = 0; + for (final Node related : view.mapWithHidden(node)) { + EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(related); + while (itr.hasNext()) { + EdgeImpl edge = itr.next(); + if (view.containsEdge(edge) && !isUndirectedToIgnore(edge)) { + count++; + if (edge.isSelfLoop()) { + count++; + } + } + } + } + return count; + } else { + int count = 0; + for (final Node related : view.mapWithHidden(node)) { + EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(related); + while (itr.hasNext()) { + EdgeImpl edge = itr.next(); + if (view.containsEdge(edge)) { + count++; + if (edge.isSelfLoop()) { + count++; + } + } + } + } + return count; + } + } + + @Override + public int getInDegree(final Node node) { + if (!this.contains(node)) { + return 0; + } + + int count = 0; + for (final Node related : view.mapWithHidden(node)) { + EdgeStore.EdgeInIterator itr = graphStore.edgeStore.edgeInIterator(related); + while (itr.hasNext()) { + if (view.containsEdge(itr.next())) { + count++; + } + } + } + return count; + } + + @Override + public int getOutDegree(final Node node) { + if (!this.contains(node)) { + return 0; + } + + int count = 0; + for (final Node related : view.mapWithHidden(node)) { + EdgeStore.EdgeOutIterator itr = graphStore.edgeStore.edgeOutIterator(related); + while (itr.hasNext()) { + if (view.containsEdge(itr.next())) { + count++; + } + } + } + return count; + } + + @Override + public boolean isSelfLoop(Edge edge) { + return edge.isSelfLoop(); + } + + @Override + public boolean isDirected(Edge edge) { + return edge.isDirected(); + } + + @Override + public boolean isIncident(Edge edge1, Edge edge2) { + graphStore.autoReadLock(); + try { + if (edge1 instanceof MappedEdgeDecorator) { + edge1 = undecorateEdge(edge1); + } else { + edge1 = decorateEdge(edge1); + } + + if (edge2 instanceof MappedEdgeDecorator) { + edge2 = undecorateEdge(edge2); + } else { + edge2 = decorateEdge(edge2); + } + + Set n1 = new HashSet(); + for (final Node n : Arrays.asList(edge1.getSource(), edge1.getTarget())) { + n1.addAll(view.mapWithHidden(n)); + } + + Set n2 = new HashSet(); + for (final Node n : Arrays.asList(edge2.getSource(), edge2.getTarget())) { + n2.addAll(view.mapWithHidden(n)); + } + + for (Node n : n1) { + if (n2.contains(n)) { + return true; + } + } + + for (Node n : n2) { + if (n1.contains(n)) { + return true; + } + } + + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean isIncident(Node node, Edge edge) { + graphStore.autoReadLock(); + try { + if (edge instanceof MappedEdgeDecorator) { + edge = undecorateEdge(edge); + } else { + edge = decorateEdge(edge); + } + + Set n1 = new HashSet(); + for (final Node n : Arrays.asList(edge.getSource(), edge.getTarget())) { + n1.addAll(view.mapWithHidden(n)); + } + + Set n2 = new HashSet(view.mapWithHidden(node)); + + for (Node n : n1) { + if (n2.contains(n)) { + return true; + } + } + + for (Node n : n2) { + if (n1.contains(n)) { + return true; + } + } + + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public void clearEdges(final Node node) { + graphStore.autoWriteLock(); + try { + this.removeAllEdgesWithLock(graphStore.edgeStore.edgeIterator(node)); + for (final Node related : view.mapWithHidden(node)) { + this.removeAllEdgesWithLock(graphStore.edgeStore.edgeIterator(related)); + } + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void clearEdges(final Node node, final int type) { + graphStore.autoWriteLock(); + try { + this.removeAllEdgesWithLock(graphStore.edgeStore.edgeIterator(node, type)); + for (final Node related : view.mapWithHidden(node)) { + this.removeAllEdgesWithLock(graphStore.edgeStore.edgeIterator(related, type)); + } + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void clear() { + view.clear(); + } + + @Override + public void clearEdges() { + view.clearEdges(); + } + + @Override + public Object getAttribute(String key) { + return view.attributes.getValue(key); + } + + @Override + public Object getAttribute(String key, double timestamp) { + return view.attributes.getValue(key, timestamp); + } + + @Override + public Object getAttribute(String key, Interval interval) { + return view.attributes.getValue(key, interval); + } + + @Override + public Set getAttributeKeys() { + return view.attributes.getKeys(); + } + + @Override + public void setAttribute(String key, Object value) { + view.attributes.setValue(key, value); + } + + @Override + public void setAttribute(String key, Object value, double timestamp) { + view.attributes.setValue(key, value, timestamp); + } + + @Override + public void setAttribute(String key, Object value, Interval interval) { + view.attributes.setValue(key, value, interval); + } + + @Override + public void removeAttribute(String key) { + view.attributes.removeValue(key); + } + + @Override + public void removeAttribute(String key, double timestamp) { + view.attributes.removeValue(key, timestamp); + } + + @Override + public void removeAttribute(String key, Interval interval) { + view.attributes.removeValue(key, interval); + } + + @Override + public GraphModel getModel() { + return graphStore.graphModel; + } + + @Override + public boolean isDirected() { + return graphStore.isDirected(); + } + + @Override + public boolean isUndirected() { + return graphStore.isUndirected(); + } + + @Override + public boolean isMixed() { + return graphStore.isMixed(); + } + + @Override + public void readLock() { + graphStore.lock.readLock(); + } + + @Override + public void readUnlock() { + graphStore.lock.readUnlock(); + } + + @Override + public void readUnlockAll() { + graphStore.lock.readUnlockAll(); + } + + @Override + public void writeLock() { + graphStore.lock.writeLock(); + } + + @Override + public void writeUnlock() { + graphStore.lock.writeUnlock(); + } + + @Override + public GraphView getView() { + return view; + } + + @Override + public void fill() { + graphStore.autoWriteLock(); + try { + view.fill(); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void union(final Subgraph subGraph) { + checkValidViewObject(subGraph.getView()); + + graphStore.autoWriteLock(); + try { + if (subGraph instanceof GraphViewDecorator) { + view.viewDelegate.union((GraphViewImpl) subGraph.getView()); + } else if (subGraph instanceof HierarchicalGraphDecorator) { + final HierarchicalGraphViewImpl other = (HierarchicalGraphViewImpl) subGraph.getView(); + view.viewDelegate.union(other.viewDelegate); + } + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void intersection(final Subgraph subGraph) { + checkValidViewObject(subGraph.getView()); + + graphStore.autoWriteLock(); + try { + if (subGraph instanceof GraphViewDecorator) { + view.viewDelegate.intersection((GraphViewImpl) subGraph.getView()); + } else if (subGraph instanceof HierarchicalGraphDecorator) { + final HierarchicalGraphViewImpl other = (HierarchicalGraphViewImpl) subGraph.getView(); + view.viewDelegate.intersection(other.viewDelegate); + } + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public void not() { + graphStore.autoWriteLock(); + try { + view.not(); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public Graph getRootGraph() { + return graphStore; + } + + void checkWriteLock() { + if (graphStore.lock != null) { + graphStore.lock.checkHoldWriteLock(); + } + } + + void checkValidNodeObject(final Node n) { + if (n == null) { + throw new NullPointerException(); + } + if (!(n instanceof NodeImpl)) { + throw new ClassCastException("Object must be a NodeImpl object"); + } + if (((NodeImpl) n).storeId == NodeStore.NULL_ID) { + throw new IllegalArgumentException("Node should belong to a store"); + } + } + + void checkValidInViewNodeObject(final Node n) { + checkValidNodeObject(n); + + if (!view.containsNode((NodeImpl) n)) { + throw new RuntimeException("Node doesn't belong to this view"); + } + } + + void checkValidEdgeObject(final Edge e) { + if (e == null) { + throw new NullPointerException(); + } + if (!(e instanceof EdgeImpl)) { + throw new ClassCastException("Object must be a EdgeImpl object"); + } + if (((EdgeImpl) e).storeId == EdgeStore.NULL_ID) { + throw new IllegalArgumentException("Edge should belong to a store"); + } + } + + void checkValidInViewEdgeObject(final Edge e) { + checkValidEdgeObject(e); + if (!view.containsEdge((EdgeImpl) e)) { + throw new RuntimeException("Edge doesn't belong to this view"); + } + } + + void checkValidViewObject(final GraphView view) { + if (view == null) { + throw new NullPointerException(); + } + if (!(view instanceof AbstractGraphView)) { + throw new ClassCastException("Object must be a AbstractGraphView object"); + } + if (((AbstractGraphView) view).graphStore != graphStore) { + throw new RuntimeException("The view doesn't belong to this store"); + } + } + + boolean isUndirectedToIgnore(final EdgeImpl edge) { + if (edge.isMutual() && edge.source.storeId < edge.target.storeId) { + if (view.containsEdge(graphStore.edgeStore.get(edge.target, edge.source, edge.type, false))) { + return true; + } + } + return false; + } + + private final class ChainedFutureIterator implements Iterator { + private final List>> delegates; + + private Iterator>> itr = null; + + private Iterator delegatePointer = null; + + private T itemPointer = null; + + private ChainedFutureIterator(final Collection>> c) { + this.delegates = new ArrayList>>(c); + } + + @Override + public boolean hasNext() { + itemPointer = null; + + if (null == this.itr) { + itr = delegates.iterator(); + } + + while (null == itemPointer) { + while (null == delegatePointer) { + if (!itr.hasNext()) { + return false; + } + try { + delegatePointer = itr.next().call(); + } catch (final Exception e) { + throw new IllegalStateException(e); + } + } + + if (delegatePointer.hasNext()) { + itemPointer = delegatePointer.next(); + } else { + delegatePointer = null; + } + } + + return true; + } + + @Override + public T next() { + return itemPointer; + } + } + + private final class NodeViewIterator implements Iterator { + private final Iterator nodeIterator; + + private NodeImpl pointer; + + public NodeViewIterator(Iterator nodeIterator) { + this.nodeIterator = nodeIterator; + } + + @Override + public boolean hasNext() { + pointer = null; + while (pointer == null) { + if (!nodeIterator.hasNext()) { + return false; + } + pointer = (NodeImpl) nodeIterator.next(); + if (pointer != null) { + if (!view.containsNode(pointer) || !view.visibleNode(pointer)) { + pointer = null; + } + } + } + return true; + } + + @Override + public Node next() { + return pointer; + } + + @Override + public void remove() { + checkWriteLock(); + removeNode(pointer); + } + } + + private final class EdgeViewIterator implements Iterator { + private final Iterator edgeIterator; + + private EdgeImpl pointer; + + public EdgeViewIterator(Iterator edgeIterator) { + this.edgeIterator = edgeIterator; + } + + @Override + public boolean hasNext() { + pointer = null; + while (pointer == null) { + if (!edgeIterator.hasNext()) { + return false; + } + pointer = (EdgeImpl) edgeIterator.next(); + if (pointer != null) { + if (!view.containsEdge(pointer)) { + pointer = null; + } + } + } + return true; + } + + @Override + public Edge next() { + return decorateEdge(pointer); + } + + @Override + public void remove() { + checkWriteLock(); + removeEdge(pointer); + } + } + + private final class UndirectedEdgeViewIterator implements Iterator { + private final Iterator itr; + + private EdgeImpl pointer; + + public UndirectedEdgeViewIterator(Iterator itr) { + this.itr = itr; + } + + @Override + public boolean hasNext() { + pointer = null; + while (pointer == null || !view.containsEdge(pointer) || isUndirectedToIgnore(pointer)) { + if (!itr.hasNext()) { + return false; + } + pointer = (EdgeImpl) itr.next(); + } + return true; + } + + @Override + public Edge next() { + return decorateEdge(pointer); + } + + @Override + public void remove() { + itr.remove(); + } + } + + private class NeighborsIterator implements Iterator { + private final NodeImpl node; + + private final Iterator itr; + + public NeighborsIterator(NodeImpl node, Iterator itr) { + this.node = node; + this.itr = itr; + } + + @Override + public boolean hasNext() { + return itr.hasNext(); + } + + @Override + public Node next() { + Edge e = itr.next(); + return e.getSource() == node ? e.getTarget() : e.getSource(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported for this iterator"); + } + } + + private Edge undecorateEdge(final Edge edge) { + if (null == edge) { + return null; + } + + Edge unpacked = edge; + while (unpacked instanceof MappedEdgeDecorator) { + unpacked = ((MappedEdgeDecorator) unpacked).edge; + } + + return unpacked; + } + + private Edge decorateEdge(final Edge edge) { + if (null == edge) { + return null; + } + + if (edge instanceof MappedEdgeDecorator) { + return edge; + } + + final Node mappedSource = this.view.mapToVisible(edge.getSource()); + final Node mappedTarget = this.view.mapToVisible(edge.getTarget()); + + if (mappedSource == edge.getSource() && mappedTarget == edge.getTarget()) { + return edge; + } + + return new MappedEdgeDecorator(edge, mappedSource, mappedTarget); + } + + protected class MappedEdgeDecorator implements Edge { + private final Edge edge; + + private final Node source; + + private final Node target; + + private MappedEdgeDecorator(final Edge edge, final Node source, final Node target) { + this.edge = edge; + this.source = source; + this.target = target; + } + + @Override + public Node getSource() { + return this.source; + } + + @Override + public Node getTarget() { + return this.target; + } + + @Override + public double getWeight() { + return edge.getWeight(); + } + + @Override + public double getWeight(double timestamp) { + return edge.getWeight(timestamp); + } + + @Override + public double getWeight(Interval interval) { + return edge.getWeight(interval); + } + + @Override + public double getWeight(GraphView view) { + return edge.getWeight(view); + } + + @Override + public Iterable getWeights() { + return edge.getWeights(); + } + + @Override + public void setWeight(double weight) { + edge.setWeight(weight); + } + + @Override + public void setWeight(double weight, double timestamp) { + edge.setWeight(weight, timestamp); + } + + @Override + public void setWeight(double weight, Interval interval) { + edge.setWeight(weight, interval); + } + + @Override + public boolean hasDynamicWeight() { + return edge.hasDynamicWeight(); + } + + @Override + public int getType() { + return edge.getType(); + } + + @Override + public Object getTypeLabel() { + return edge.getTypeLabel(); + } + + @Override + public boolean isSelfLoop() { + return edge.isSelfLoop(); + } + + @Override + public boolean isDirected() { + return edge.isSelfLoop(); + } + + @Override + public Object getId() { + return edge.getId(); + } + + @Override + public String getLabel() { + return edge.getLabel(); + } + + @Override + public Object getAttribute(String key) { + return edge.getAttribute(key); + } + + @Override + public Object getAttribute(Column column) { + return edge.getAttribute(column); + } + + @Override + public Object getAttribute(String key, double timestamp) { + return edge.getAttribute(key, timestamp); + } + + @Override + public Object getAttribute(Column column, double timestamp) { + return edge.getAttribute(column, timestamp); + } + + @Override + public Object getAttribute(String key, Interval interval) { + return edge.getAttribute(key, interval); + } + + @Override + public Object getAttribute(Column column, Interval interval) { + return edge.getAttribute(column, interval); + } + + @Override + public Object getAttribute(String key, GraphView view) { + return edge.getAttribute(key, view); + } + + @Override + public Object getAttribute(Column column, GraphView view) { + return edge.getAttribute(column, view); + } + + @Override + public Iterable getAttributes(Column column) { + return edge.getAttributes(column); + } + + @Override + public Object[] getAttributes() { + return edge.getAttributes(); + } + + @Override + public Set getAttributeKeys() { + return edge.getAttributeKeys(); + } + + @Override + public ColumnIterable getAttributeColumns() { + return edge.getAttributeColumns(); + } + + @Override + public int getStoreId() { + return edge.getStoreId(); + } + + @Override + public Object removeAttribute(String key) { + return edge.removeAttribute(key); + } + + @Override + public Object removeAttribute(Column column) { + return edge.removeAttribute(column); + } + + @Override + public Object removeAttribute(String key, double timestamp) { + return edge.removeAttribute(key, timestamp); + } + + @Override + public Object removeAttribute(Column column, double timestamp) { + return edge.removeAttribute(column, timestamp); + } + + @Override + public Object removeAttribute(String key, Interval interval) { + return edge.removeAttribute(key, interval); + } + + @Override + public Object removeAttribute(Column column, Interval interval) { + return edge.removeAttribute(column, interval); + } + + @Override + public void setLabel(String label) { + edge.setLabel(label); + } + + @Override + public void setAttribute(String key, Object value) { + edge.setAttribute(key, value); + } + + @Override + public void setAttribute(Column column, Object value) { + edge.setAttribute(column, value); + } + + @Override + public void setAttribute(String key, Object value, double timestamp) { + edge.setAttribute(key, value, timestamp); + } + + @Override + public void setAttribute(Column column, Object value, double timestamp) { + edge.setAttribute(column, value, timestamp); + } + + @Override + public void setAttribute(String key, Object value, Interval interval) { + edge.setAttribute(key, value, interval); + } + + @Override + public void setAttribute(Column column, Object value, Interval interval) { + edge.setAttribute(column, value, interval); + } + + @Override + public boolean addTimestamp(double timestamp) { + return edge.addTimestamp(timestamp); + } + + @Override + public boolean removeTimestamp(double timestamp) { + return edge.removeTimestamp(timestamp); + } + + @Override + public boolean hasTimestamp(double timestamp) { + return edge.hasTimestamp(timestamp); + } + + @Override + public double[] getTimestamps() { + return edge.getTimestamps(); + } + + @Override + public boolean addInterval(Interval interval) { + return edge.addInterval(interval); + } + + @Override + public boolean removeInterval(Interval interval) { + return edge.removeInterval(interval); + } + + @Override + public boolean hasInterval(Interval interval) { + return edge.hasInterval(interval); + } + + @Override + public Interval[] getIntervals() { + return edge.getIntervals(); + } + + @Override + public void clearAttributes() { + edge.clearAttributes(); + } + + @Override + public Table getTable() { + return edge.getTable(); + } + + @Override + public float r() { + return edge.r(); + } + + @Override + public float g() { + return edge.g(); + } + + @Override + public float b() { + return edge.b(); + } + + @Override + public int getRGBA() { + return edge.getRGBA(); + } + + @Override + public Color getColor() { + return edge.getColor(); + } + + @Override + public float alpha() { + return edge.alpha(); + } + + @Override + public TextProperties getTextProperties() { + return edge.getTextProperties(); + } + + @Override + public void setR(float r) { + edge.setR(r); + } + + @Override + public void setG(float g) { + edge.setG(g); + } + + @Override + public void setB(float b) { + edge.setB(b); + } + + @Override + public void setAlpha(float a) { + edge.setAlpha(a); + } + + @Override + public void setColor(Color color) { + edge.setColor(color); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + MappedEdgeDecorator that = (MappedEdgeDecorator) o; + + if (edge != null ? !edge.equals(that.edge) : that.edge != null) { + return false; + } + if (source != null ? !source.equals(that.source) : that.source != null) { + return false; + } + return target != null ? target.equals(that.target) : that.target == null; + } + + @Override + public int hashCode() { + int result = edge != null ? edge.hashCode() : 0; + result = 31 * result + (source != null ? source.hashCode() : 0); + result = 31 * result + (target != null ? target.hashCode() : 0); + return result; + } + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java new file mode 100644 index 00000000..8ab6ff8f --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java @@ -0,0 +1,513 @@ +package org.gephi.graph.impl; + +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphObserver; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.HierarchicalGraphView; +import org.gephi.graph.api.HierarchicalNodeGroup; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.UndirectedSubgraph; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class HierarchicalGraphViewImpl extends AbstractGraphView implements GraphView, HierarchicalGraphView { + protected final GraphViewImpl viewDelegate; + + private final GraphVersion version; + + private final HierarchicalGraphDecorator directedDecorator; + + private final HierarchicalGraphDecorator undirectedDecorator; + + private final Set observers = new HashSet(); + + private final HierarchicalNodeGroupImpl root = new HierarchicalNodeGroupImpl(null, null); + + public HierarchicalGraphViewImpl(GraphStore store, boolean nodes, boolean edges) { + super(store, nodes, edges); + this.viewDelegate = new GraphViewImpl(store, nodes, edges); + this.directedDecorator = new HierarchicalGraphDecorator(store, this, false); + this.undirectedDecorator = new HierarchicalGraphDecorator(store, this, true); + this.version = graphStore.version != null ? new GraphVersion(directedDecorator) : null; + } + + public boolean containsNode(final NodeImpl node) { + if (null == node) { + return false; + } + + if (!this.viewDelegate.containsNode(node)) { + return false; + } + + return true; + } + + public boolean visibleNode(final NodeImpl node) { + if (null == node) { + return false; + } + + final Node mapped = this.mapToVisible(node); + if (null == mapped) { + return false; + } + + return mapped == node; + } + + public boolean addNode(final Node node) { + if (!this.viewDelegate.addNode(node)) { + return false; + } + + return true; + } + + public boolean removeNode(final Node node) { + boolean updated = this.viewDelegate.removeNode(node); + updated |= this.root.removeNode(node, true); + return updated; + } + + public boolean addEdge(final Edge edge) { + if (!this.viewDelegate.addEdge(edge)) { + return false; + } + + return true; + } + + public boolean removeEdge(final Edge edge) { + if (!this.viewDelegate.removeEdge(edge)) { + return false; + } + + return true; + } + + public boolean containsEdge(final EdgeImpl edge) { + return this.viewDelegate.containsEdge(edge); + } + + public int getCollapsedNodeCount() { + final Set visibleGroups = new HashSet(); + final Set hiddenGroups = new HashSet(); + for (final HierarchicalNodeGroup group : this.getGroups()) { + final HierarchicalNodeGroupImpl impl = (HierarchicalNodeGroupImpl) group; + if (impl.hasCollapsedParent()) { + hiddenGroups.add(impl); + visibleGroups.remove(impl); + } else if (!hiddenGroups.contains(impl)) { + visibleGroups.add(impl); + } + } + return hiddenGroups.size(); + } + + public int getNodeCount() { + return this.viewDelegate.getNodeCount() - this.getCollapsedNodeCount(); + } + + public int getEdgeCount() { + return this.viewDelegate.getEdgeCount(); + } + + public int getEdgeCount(int type) { + return this.viewDelegate.getEdgeCount(type); + } + + public int getUndirectedEdgeCount() { + return this.viewDelegate.getUndirectedEdgeCount(); + } + + public int getUndirectedEdgeCount(int type) { + return this.viewDelegate.getUndirectedEdgeCount(type); + } + + public void clear() { + this.root.clear(); + this.viewDelegate.clear(); + } + + public void clearEdges() { + this.viewDelegate.clearEdges(); + } + + public void fill() { + this.viewDelegate.fill(); + } + + public void not() { + this.viewDelegate.not(); + } + + @Override + public DirectedSubgraph getDirectedGraph() { + return this.directedDecorator; + } + + @Override + public UndirectedSubgraph getUndirectedGraph() { + return this.undirectedDecorator; + } + + @Override + public boolean deepEquals(AbstractGraphView view) { + return this.viewDelegate.deepEquals(view); + } + + @Override + public int deepHashCode() { + return this.viewDelegate.deepHashCode(); + } + + @Override + protected void viewDestroyed() { + this.setStoreId(GraphViewStore.NULL_VIEW); + for (final GraphObserverImpl observer : this.observers) { + observer.destroyObserver(); + } + this.observers.clear(); + this.root.clear(); + this.viewDelegate.viewDestroyed(); + } + + @Override + protected void nodeAdded(NodeImpl node) { + this.viewDelegate.nodeAdded(node); + } + + @Override + protected void nodeRemoved(NodeImpl node) { + this.removeNode(node); + this.viewDelegate.nodeRemoved(node); + } + + @Override + protected void edgeAdded(EdgeImpl edge) { + this.viewDelegate.edgeAdded(edge); + } + + @Override + protected void edgeRemoved(EdgeImpl edge) { + this.viewDelegate.edgeRemoved(edge); + } + + @Override + protected GraphObserverImpl createGraphObserver(final Graph graph, final boolean withDiff) { + if (null == this.version) { + return null; + } + + final GraphObserverImpl observer = new GraphObserverImpl(this.graphStore, this.version, graph, withDiff); + this.observers.add(observer); + return observer; + } + + @Override + protected void destroyGraphObserver(final GraphObserver observer) { + if (this.observers.remove(observer)) { + ((GraphObserverImpl) observer).destroyObserver(); + } + } + + @Override + public Iterable getGroups() { + final List groups = new ArrayList(); + groups.add(this.root); + for (final HierarchicalNodeGroupImpl group : this.root.getGroups(true)) { + groups.add(group); + } + return Collections.unmodifiableList(groups); + } + + @Override + public HierarchicalNodeGroup getRoot() { + return this.root; + } + + @Override + public HierarchicalNodeGroup getGroup(final Node node) { + if (null == node) { + return null; + } + + return this.root.find(node, true); + } + + protected Collection mapWithHidden(final Node node) { + if (null == node) { + return Collections.emptyList(); + } + + final HierarchicalNodeGroupImpl group = this.root.find(node, true); + if (null == group) { + return Collections.singleton(node); + } + + if (group.hasCollapsedParent()) { + return Collections.emptyList(); + } + + if (group.isCollapsed()) { + final Collection children = group.getNodes(true); + final Set set = new HashSet(children.size() + 1); + set.add(node); + set.addAll(children); + return Collections.unmodifiableCollection(set); + } + + return Collections.singleton(node); + } + + protected Node mapToVisible(final Node node) { + final HierarchicalNodeGroupImpl group = this.root.find(node, true); + if (null == group) { + return node; + } else { + return group.mappedNode(); + } + } + + private class HierarchicalNodeGroupImpl implements HierarchicalNodeGroup { + private final HierarchicalNodeGroupImpl parent; + + private final Node node; + + private boolean collapsed = false; + + private final Object2ObjectMap nodeMap = new Object2ObjectOpenHashMap(); + + private HierarchicalNodeGroupImpl(final HierarchicalNodeGroupImpl parent, final Node node) { + this.parent = parent; + this.node = node; + } + + public void clear() { + if (!this.nodeMap.isEmpty()) { + return; + } + + for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) { + group.clear(); + } + this.nodeMap.clear(); + } + + @Override + public HierarchicalNodeGroup addNode(final Node childNode) { + if (null == childNode) { + return null; + } + + if (this.node == childNode) { + throw new IllegalArgumentException("Child and parent node are the same."); + } + + graphStore.autoWriteLock(); + try { + if (this.nodeMap.containsKey(childNode)) { + return null; + } + + final HierarchicalNodeGroupImpl group = new HierarchicalNodeGroupImpl(this, childNode); + this.nodeMap.put(childNode, group); + return group; + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean removeNode(final Node childNode) { + if (null == childNode) { + return false; + } + + return this.removeNode(childNode, false); + } + + public boolean removeNode(final Node childNode, final boolean recursive) { + if (null == childNode) { + return false; + } + + graphStore.autoWriteLock(); + try { + return this.removeNodeWithLock(childNode, recursive); + } finally { + graphStore.autoWriteUnlock(); + } + } + + private boolean removeNodeWithLock(final Node childNode, final boolean recursive) { + boolean updated = this.nodeMap.get(childNode) != null; + if (recursive) { + for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) { + updated |= group.removeNodeWithLock(childNode, true); + } + } + return updated; + } + + public HierarchicalNodeGroupImpl find(final Node childNode, final boolean recursive) { + graphStore.autoReadLock(); + try { + return this.findWithLock(childNode, recursive); + } finally { + graphStore.autoReadUnlock(); + } + } + + private HierarchicalNodeGroupImpl findWithLock(final Node childNode, final boolean recursive) { + final HierarchicalNodeGroupImpl existing = this.nodeMap.get(childNode); + if (existing != null) { + return existing; + } + + if (!recursive) { + return null; + } + + for (final HierarchicalNodeGroupImpl child : nodeMap.values()) { + final HierarchicalNodeGroupImpl existingChild = child.findWithLock(childNode, true); + if (existingChild != null) { + return existingChild; + } + } + + return null; + } + + public Node mappedNode() { + if (this.node != null) { + // child node + HierarchicalNodeGroupImpl childGroup = this; + HierarchicalNodeGroupImpl parentGroup = this.parent; + while (parentGroup != null) { + if (null == parentGroup.node) { + return childGroup.node; + } + if (parentGroup.isExpanded()) { + return childGroup.node; + } + final HierarchicalNodeGroupImpl tmp = parentGroup; + parentGroup = parentGroup.parent; + childGroup = tmp; + } + return null; + } else { + // first tier node, always visible + return null; + } + } + + public boolean hasCollapsedParent() { + graphStore.autoReadLock(); + try { + HierarchicalNodeGroupImpl group = this.parent; + while (group != null) { + if (group.isCollapsed()) { + return true; + } + group = group.parent; + } + return false; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean isCollapsed() { + graphStore.autoReadLock(); + try { + return this.collapsed; + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public boolean isExpanded() { + return !this.isCollapsed(); + } + + @Override + public void expand() { + this.collapsed = false; + for (final HierarchicalNodeGroupImpl child : this.nodeMap.values()) { + child.expand(); + } + } + + @Override + public void collapse() { + this.collapsed = true; + for (final HierarchicalNodeGroupImpl child : this.nodeMap.values()) { + child.collapse(); + } + } + + @Override + public Iterable getNodes() { + graphStore.autoReadLock(); + final List list; + try { + list = new ArrayList(this.nodeMap.keySet()); + } finally { + graphStore.autoReadUnlock(); + } + return Collections.unmodifiableCollection(list); + } + + @Override + public Collection getNodes(boolean recursive) { + graphStore.autoReadLock(); + final Set set = new HashSet(this.nodeMap.size()); + try { + if (recursive) { + for (final Map.Entry entry : this.nodeMap.entrySet()) { + set.add(entry.getKey()); + if (recursive) { + set.addAll(entry.getValue().getNodes(true)); + } + } + } + } finally { + graphStore.autoReadUnlock(); + } + return Collections.unmodifiableCollection(set); + } + + public Collection getGroups(final boolean recursive) { + graphStore.autoReadLock(); + final List list; + try { + if (recursive) { + list = new ArrayList(); + for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) { + list.add(group); + list.addAll(group.getGroups(true)); + } + } else { + list = new ArrayList(this.nodeMap.values()); + } + } finally { + graphStore.autoReadUnlock(); + } + return Collections.unmodifiableCollection(list); + } + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/Serialization.java b/store/src/main/java/org/gephi/graph/impl/Serialization.java index 35a95fc8..ba5c0a38 100644 --- a/store/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/store/src/main/java/org/gephi/graph/impl/Serialization.java @@ -586,7 +586,7 @@ private GraphViewStore deserializeViewStore(final DataInput is) throws IOExcepti private void serializeGraphView(final DataOutput out, final GraphViewImpl view) throws IOException { serialize(out, view.nodeView); serialize(out, view.edgeView); - serialize(out, view.storeId); + serialize(out, view.getStoreId()); serialize(out, view.nodeCount); serialize(out, view.edgeCount); @@ -600,7 +600,7 @@ private void serializeGraphView(final DataOutput out, final GraphViewImpl view) serialize(out, view.version); serialize(out, view.attributes); - serialize(out, view.interval); + serialize(out, view.getTimeInterval()); } private GraphViewImpl deserializeGraphView(final DataInput is) throws IOException, ClassNotFoundException { @@ -624,7 +624,7 @@ private GraphViewImpl deserializeGraphView(final DataInput is) throws IOExceptio view.edgeCount = edgeCount; view.nodeBitVector = nodeCountVector; view.edgeBitVector = edgeCountVector; - view.storeId = storeId; + view.setStoreId(storeId); view.typeCounts = typeCounts; view.mutualEdgesCount = mutualEdgesCount; @@ -634,7 +634,7 @@ private GraphViewImpl deserializeGraphView(final DataInput is) throws IOExceptio view.version.edgeVersion = version.edgeVersion; view.attributes.setGraphAttributes(atts); - view.interval = interval; + view.setTimeInterval(interval); return view; } diff --git a/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java b/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java index 12f4434d..a74c2b5f 100644 --- a/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java +++ b/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java @@ -67,7 +67,7 @@ public void testDestroy() { Assert.assertFalse(store.contains(view)); Assert.assertEquals(store.size(), 0); - Assert.assertEquals(view.storeId, GraphViewStore.NULL_VIEW); + Assert.assertEquals(view.getStoreId(), GraphViewStore.NULL_VIEW); Assert.assertTrue(view.isDestroyed()); } @@ -128,7 +128,7 @@ public void testGarbage() { view = store.createView(); - Assert.assertEquals(view.storeId, 0); + Assert.assertEquals(view.getStoreId(), 0); Assert.assertEquals(store.length, 1); Assert.assertEquals(store.garbageQueue.size(), 0); } diff --git a/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java new file mode 100644 index 00000000..1e7ff6f9 --- /dev/null +++ b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java @@ -0,0 +1,115 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.HierarchicalGraphView; +import org.gephi.graph.api.HierarchicalNodeGroup; +import org.gephi.graph.api.Node; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.HashSet; +import java.util.Set; + +public class HierarchicalGraphTest { + @Test + public void testSimpleExpandCollapse() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + HierarchicalGraphView view = store.createHierarchicalView(); + DirectedSubgraph graph = store.getDirectedGraph(view); + + for (Node node : graphStore.getNodes().toArray()) { + graph.addNode(node); + } + for (Edge edge : graphStore.getEdges().toArray()) { + graph.addEdge(edge); + } + + int originalCount = store.getGraph(view).getNodeCount(); + + Node parentNode = store.getGraph(view).getNode("7"); + HierarchicalNodeGroup group = view.getRoot().addNode(parentNode); + Assert.assertEquals(originalCount, graph.getNodeCount()); + + Node childNode = store.getGraph(view).getNode("3"); + group.addNode(childNode); + Assert.assertEquals(originalCount, graph.getNodeCount()); + + Assert.assertFalse(graph.getEdges(parentNode).toCollection().isEmpty()); + Assert.assertFalse(graph.getEdges(childNode).toCollection().isEmpty()); + + int edgeCountForParent = graph.getDegree(parentNode); + int edgeCountForChild = graph.getDegree(childNode); + + group.collapse(); + + Assert.assertEquals(originalCount - 1, graph.getNodeCount()); + Assert.assertFalse(graph.hasNode("3")); + Assert.assertEquals(edgeCountForParent + edgeCountForChild, graph.getDegree(parentNode)); + Assert.assertEquals(0, graph.getDegree(childNode)); + + Assert.assertFalse(graph.getEdges(parentNode).toCollection().isEmpty()); + Assert.assertTrue(graph.getEdges(childNode).toCollection().isEmpty()); + + graph.clearEdges(); + + Assert.assertTrue(graph.getEdges(parentNode).toCollection().isEmpty()); + Assert.assertTrue(graph.getEdges(childNode).toCollection().isEmpty()); + } + + @Test + public void testManipulateEdges() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + HierarchicalGraphView view = store.createHierarchicalView(); + DirectedSubgraph graph = store.getDirectedGraph(view); + + for (Node node : graphStore.getNodes().toArray()) { + graph.addNode(node); + } + for (Edge edge : graphStore.getEdges().toArray()) { + graph.addEdge(edge); + } + + Node parentNode = store.getGraph(view).getNode("7"); + HierarchicalNodeGroup group = view.getRoot().addNode(parentNode); + Node childNode = store.getGraph(view).getNode("3"); + group.addNode(childNode); + + Set others = new HashSet(); + for (Edge edge : graph.getEdges(childNode)) { + Node other = graph.getOpposite(childNode, edge); + Assert.assertNotNull(other); + Assert.assertFalse(other.equals(childNode)); + others.add(other); + } + Assert.assertFalse(others.isEmpty()); + + group.collapse(); + + Set mapped = new HashSet(); + for (Node other : others) { + for (Edge edge : graph.getEdges(other)) { + if (edge instanceof HierarchicalGraphDecorator.MappedEdgeDecorator) { + if (edge.getSource() != edge.getTarget()) { + Assert.assertFalse(edge.getSource() == childNode || edge.getTarget() == childNode); + Assert.assertTrue(edge.getSource() == parentNode || edge.getTarget() == parentNode); + Assert.assertTrue(edge.getSource() == other || edge.getTarget() == other); + mapped.add(edge); + } + } + } + } + Assert.assertFalse(mapped.isEmpty()); + + for (Edge edge : mapped) { + Node other = graph.getOpposite(childNode, edge); + Assert.assertNull(other); + other = graph.getOpposite(parentNode, edge); + Assert.assertNotNull(other); + Assert.assertTrue(others.contains(other)); + graph.removeEdge(edge); + } + } +} From ea8b270eded201ae2563acb9cf54a0d61b6ff28f Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Sat, 23 Apr 2016 16:24:04 +0700 Subject: [PATCH 10/15] added better support for #getNeighbors; added #isRoot method to check for root node operations --- .../graph/api/HierarchicalNodeGroup.java | 10 ++ .../impl/HierarchicalGraphDecorator.java | 42 ++++----- .../graph/impl/HierarchicalGraphViewImpl.java | 93 +++++++++++++----- .../graph/impl/HierarchicalGraphTest.java | 94 +++++++++++++++++++ 4 files changed, 194 insertions(+), 45 deletions(-) diff --git a/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java index 1a64b085..1e6a9713 100644 --- a/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java +++ b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java @@ -26,6 +26,16 @@ public interface HierarchicalNodeGroup { */ void collapse(); + /** + * @return Returns true if this group contains on or more children nodes. + */ + boolean hasChildren(); + + /** + * @return Returns true if this group is the root node. + */ + boolean isRoot(); + /** * @return Return iterator containing children nodes (not recursive). */ diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java index 57cfbe66..2f22af66 100644 --- a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java @@ -450,38 +450,36 @@ public EdgeIterable getSelfLoops() { } @Override - public NodeIterable getNeighbors(Node node) { + public NodeIterable getNeighbors(final Node node) { checkValidInViewNodeObject(node); Set set = new HashSet(); - for (final Node n : view.mapWithHidden(node)) { - final Iterator itr = new NeighborsIterator((NodeImpl) n, new UndirectedEdgeViewIterator( - graphStore.edgeStore.edgeIterator(n))); - while (itr.hasNext()) { - final Node neighbor = itr.next(); - if (neighbor != null) { - set.add(neighbor); - } + Set mapped = new HashSet(); + mapped.add(node); + mapped.addAll(view.mapWithHidden(node)); + for (Node n : mapped) { + for (Edge edge : this.getEdges(n)) { + set.add(edge.getSource()); + set.add(edge.getTarget()); } } - checkValidInViewNodeObject(node); + set.removeAll(mapped); return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator())); } @Override - public NodeIterable getNeighbors(Node node, int type) { + public NodeIterable getNeighbors(final Node node, final int type) { checkValidInViewNodeObject(node); Set set = new HashSet(); - for (final Node n : view.mapWithHidden(node)) { - final Iterator itr = new NeighborsIterator((NodeImpl) n, new UndirectedEdgeViewIterator( - graphStore.edgeStore.edgeIterator(n, type))); - while (itr.hasNext()) { - final Node neighbor = itr.next(); - if (neighbor != null) { - set.add(neighbor); - } + Set mapped = new HashSet(); + mapped.add(node); + mapped.addAll(view.mapWithHidden(node)); + for (Node n : mapped) { + for (Edge edge : this.getEdges(n, type)) { + set.add(edge.getSource()); + set.add(edge.getTarget()); } } - checkValidInViewNodeObject(node); + set.removeAll(mapped); return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator())); } @@ -1147,11 +1145,11 @@ public void remove() { } private class NeighborsIterator implements Iterator { - private final NodeImpl node; + private final Node node; private final Iterator itr; - public NeighborsIterator(NodeImpl node, Iterator itr) { + public NeighborsIterator(Node node, Iterator itr) { this.node = node; this.itr = itr; } diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java index 8ab6ff8f..1e2ece49 100644 --- a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java @@ -16,7 +16,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -224,12 +223,12 @@ protected void destroyGraphObserver(final GraphObserver observer) { @Override public Iterable getGroups() { - final List groups = new ArrayList(); + final Set groups = new HashSet(); groups.add(this.root); for (final HierarchicalNodeGroupImpl group : this.root.getGroups(true)) { groups.add(group); } - return Collections.unmodifiableList(groups); + return Collections.unmodifiableCollection(groups); } @Override @@ -399,7 +398,7 @@ public Node mappedNode() { if (null == parentGroup.node) { return childGroup.node; } - if (parentGroup.isExpanded()) { + if (parentGroup.isExpanded() && !parentGroup.hasCollapsedParent()) { return childGroup.node; } final HierarchicalNodeGroupImpl tmp = parentGroup; @@ -446,68 +445,116 @@ public boolean isExpanded() { @Override public void expand() { - this.collapsed = false; - for (final HierarchicalNodeGroupImpl child : this.nodeMap.values()) { - child.expand(); + graphStore.autoWriteLock(); + try { + this.setCollapsedWithLock(false); + } finally { + graphStore.autoWriteUnlock(); } } @Override public void collapse() { - this.collapsed = true; + graphStore.autoWriteLock(); + try { + this.setCollapsedWithLock(true); + } finally { + graphStore.autoWriteUnlock(); + } + } + + private void setCollapsedWithLock(final boolean value) { + this.collapsed = value; for (final HierarchicalNodeGroupImpl child : this.nodeMap.values()) { - child.collapse(); + child.setCollapsedWithLock(value); + } + } + + @Override + public boolean hasChildren() { + graphStore.autoReadLock(); + try { + return !this.nodeMap.isEmpty(); + } finally { + graphStore.autoReadUnlock(); } } + @Override + public boolean isRoot() { + return root.equals(this); + } + @Override public Iterable getNodes() { graphStore.autoReadLock(); - final List list; try { - list = new ArrayList(this.nodeMap.keySet()); + return Collections.unmodifiableCollection(new ArrayList(this.nodeMap.keySet())); } finally { graphStore.autoReadUnlock(); } - return Collections.unmodifiableCollection(list); } @Override public Collection getNodes(boolean recursive) { graphStore.autoReadLock(); - final Set set = new HashSet(this.nodeMap.size()); try { if (recursive) { + final Set set = new HashSet(this.nodeMap.size()); for (final Map.Entry entry : this.nodeMap.entrySet()) { set.add(entry.getKey()); - if (recursive) { - set.addAll(entry.getValue().getNodes(true)); - } + set.addAll(entry.getValue().getNodes(true)); } + return Collections.unmodifiableCollection(set); + } else { + return Collections.unmodifiableCollection(new ArrayList(this.nodeMap.keySet())); } } finally { graphStore.autoReadUnlock(); } - return Collections.unmodifiableCollection(set); } public Collection getGroups(final boolean recursive) { graphStore.autoReadLock(); - final List list; try { if (recursive) { - list = new ArrayList(); + final Collection set = new HashSet(); for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) { - list.add(group); - list.addAll(group.getGroups(true)); + set.add(group); + set.addAll(group.getGroups(true)); } + return Collections.unmodifiableCollection(set); } else { - list = new ArrayList(this.nodeMap.values()); + return Collections.unmodifiableCollection(new ArrayList(this.nodeMap + .values())); } } finally { graphStore.autoReadUnlock(); } - return Collections.unmodifiableCollection(list); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + HierarchicalNodeGroupImpl that = (HierarchicalNodeGroupImpl) o; + + if (parent != null ? !parent.equals(that.parent) : that.parent != null) { + return false; + } + return node != null ? node.equals(that.node) : that.node == null; + } + + @Override + public int hashCode() { + int result = parent != null ? parent.hashCode() : 0; + result = 31 * result + (node != null ? node.hashCode() : 0); + return result; } } } diff --git a/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java index 1e7ff6f9..5eb2294c 100644 --- a/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java +++ b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java @@ -8,8 +8,11 @@ import org.testng.Assert; import org.testng.annotations.Test; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.TreeSet; public class HierarchicalGraphTest { @Test @@ -112,4 +115,95 @@ public void testManipulateEdges() { graph.removeEdge(edge); } } + + @Test + public void testNeighbors() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + HierarchicalGraphView view = store.createHierarchicalView(); + DirectedSubgraph graph = store.getDirectedGraph(view); + + Set types = new TreeSet(); + for (Node node : graphStore.getNodes().toArray()) { + graph.addNode(node); + } + for (Edge edge : graphStore.getEdges().toArray()) { + graph.addEdge(edge); + types.add(edge.getType()); + } + + Node parentNode = store.getGraph(view).getNode("5"); + HierarchicalNodeGroup group = view.getRoot().addNode(parentNode); + Node childNode1 = store.getGraph(view).getNode("1"); + group.addNode(childNode1); + Node childNode2 = store.getGraph(view).getNode("2"); + group.addNode(childNode2); + + int parentNeighborsCount = graph.getNeighbors(parentNode).toCollection().size(); + + Collection expandedEdges = new HashSet(); + for (int type : types) { + expandedEdges.addAll(graph.getEdges(parentNode, type).toCollection()); + } + Assert.assertFalse(expandedEdges.isEmpty()); + + group.collapse(); + + Collection collapsedEdges = new HashSet(); + for (int type : types) { + collapsedEdges.addAll(graph.getEdges(parentNode, type).toCollection()); + } + Assert.assertFalse(collapsedEdges.isEmpty()); + Assert.assertTrue(collapsedEdges.size() > expandedEdges.size()); + + group.expand(); + + Collection resetEdges = new HashSet(); + for (int type : types) { + resetEdges.addAll(graph.getEdges(parentNode, type).toCollection()); + } + Assert.assertFalse(resetEdges.isEmpty()); + Assert.assertEquals(expandedEdges.size(), resetEdges.size()); + + for (Node child : Arrays.asList(childNode1, childNode2)) { + Collection originalNeighbors = graph.getNeighbors(child).toCollection(); + Assert.assertNotNull(originalNeighbors.isEmpty()); + for (Node neighbor : originalNeighbors) { + Collection edges = new HashSet(); + for (int type : types) { + edges.addAll(graph.getEdges(child, neighbor, type).toCollection()); + edges.addAll(graph.getEdges(neighbor, child, type).toCollection()); + } + Assert.assertFalse(edges.isEmpty()); + } + + group.collapse(); + + Collection collapsedNeighbors = graph.getNeighbors(child).toCollection(); + Assert.assertTrue(collapsedNeighbors.isEmpty()); + Assert.assertTrue(graph.getNeighbors(parentNode).toCollection().size() > parentNeighborsCount); + for (Node neighbor : originalNeighbors) { + Collection edges = new HashSet(); + for (int type : types) { + edges.addAll(graph.getEdges(child, neighbor, type).toCollection()); + edges.addAll(graph.getEdges(neighbor, child, type).toCollection()); + } + Assert.assertTrue(edges.isEmpty()); + } + + group.expand(); + + Collection expandedNeighbors = graph.getNeighbors(child).toCollection(); + Assert.assertEquals(originalNeighbors.size(), expandedNeighbors.size()); + Assert.assertEquals(parentNeighborsCount, graph.getNeighbors(parentNode).toCollection().size()); + for (Node neighbor : originalNeighbors) { + Collection edges = new HashSet(); + for (int type : types) { + edges.addAll(graph.getEdges(child, neighbor, type).toCollection()); + edges.addAll(graph.getEdges(neighbor, child, type).toCollection()); + } + Assert.assertFalse(edges.isEmpty()); + } + } + } } From 35663e5a5ab586e52ea155aa53e7712fa7d9c95c Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Fri, 13 May 2016 00:13:22 +0700 Subject: [PATCH 11/15] set blocking to false for node interators in view decorator since we are already handling that earlier in chain --- .../graph/impl/HierarchicalGraphDecorator.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java index 2f22af66..a37da0e1 100644 --- a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java @@ -89,7 +89,7 @@ public Iterator call() throws Exception { }); } } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -105,7 +105,7 @@ public Iterator call() throws Exception { }); } } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -168,7 +168,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -183,7 +183,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -198,7 +198,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -213,7 +213,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -499,7 +499,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override @@ -518,7 +518,7 @@ public Iterator call() throws Exception { } }); } - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(new ChainedFutureIterator(list)), false); } @Override From 7de6bfd423036d931352a178c1c6e98c06757559 Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Sat, 6 Aug 2016 16:28:28 -0500 Subject: [PATCH 12/15] fixed issue with cast-cast exceptions with edge iterators --- .../impl/HierarchicalGraphDecorator.java | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java index a37da0e1..6abed24d 100644 --- a/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java @@ -1077,7 +1077,7 @@ public void remove() { private final class EdgeViewIterator implements Iterator { private final Iterator edgeIterator; - private EdgeImpl pointer; + private Edge pointer; public EdgeViewIterator(Iterator edgeIterator) { this.edgeIterator = edgeIterator; @@ -1090,9 +1090,13 @@ public boolean hasNext() { if (!edgeIterator.hasNext()) { return false; } - pointer = (EdgeImpl) edgeIterator.next(); + pointer = edgeIterator.next(); if (pointer != null) { - if (!view.containsEdge(pointer)) { + if (pointer instanceof EdgeImpl && !view.containsEdge((EdgeImpl) pointer)) { + pointer = null; + } + if (pointer instanceof MappedEdgeDecorator && !view + .containsEdge(((MappedEdgeDecorator) pointer).edge)) { pointer = null; } } @@ -1115,7 +1119,7 @@ public void remove() { private final class UndirectedEdgeViewIterator implements Iterator { private final Iterator itr; - private EdgeImpl pointer; + private Edge pointer; public UndirectedEdgeViewIterator(Iterator itr) { this.itr = itr; @@ -1124,11 +1128,20 @@ public UndirectedEdgeViewIterator(Iterator itr) { @Override public boolean hasNext() { pointer = null; - while (pointer == null || !view.containsEdge(pointer) || isUndirectedToIgnore(pointer)) { + while (pointer == null) { if (!itr.hasNext()) { return false; } - pointer = (EdgeImpl) itr.next(); + pointer = itr.next(); + if (pointer != null) { + if (pointer instanceof EdgeImpl && !view.containsEdge((EdgeImpl) pointer) && isUndirectedToIgnore((EdgeImpl) pointer)) { + pointer = null; + } + if (pointer instanceof MappedEdgeDecorator && !view + .containsEdge(((MappedEdgeDecorator) pointer).edge) && isUndirectedToIgnore(((MappedEdgeDecorator) pointer).edge)) { + pointer = null; + } + } } return true; } @@ -1200,17 +1213,21 @@ private Edge decorateEdge(final Edge edge) { return edge; } - return new MappedEdgeDecorator(edge, mappedSource, mappedTarget); + if (edge instanceof EdgeImpl) { + return new MappedEdgeDecorator((EdgeImpl) edge, mappedSource, mappedTarget); + } else { + return edge; + } } protected class MappedEdgeDecorator implements Edge { - private final Edge edge; + private final EdgeImpl edge; private final Node source; private final Node target; - private MappedEdgeDecorator(final Edge edge, final Node source, final Node target) { + private MappedEdgeDecorator(final EdgeImpl edge, final Node source, final Node target) { this.edge = edge; this.source = source; this.target = target; From 237da0bad1aaa2c0b0508b48b355d8cceb1bfe07 Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Fri, 5 May 2017 21:23:18 +0700 Subject: [PATCH 13/15] updated benchmar tests to confirm with prior changes to #Configuration api defaults --- store-benchmark/pom.xml | 4 +-- .../benchmark/DataStructureBenchmark.java | 6 ++-- .../graph/benchmark/EdgeStoreBenchmark.java | 36 +++++++++++++------ .../org/gephi/graph/benchmark/Generator.java | 12 +++++-- .../gephi/graph/benchmark/KleinbergGraph.java | 25 +++++++------ .../graph/benchmark/NodeStoreBenchmark.java | 21 ++++++++--- .../gephi/graph/benchmark/RandomGraph.java | 18 +++++++--- 7 files changed, 85 insertions(+), 37 deletions(-) diff --git a/store-benchmark/pom.xml b/store-benchmark/pom.xml index 97e22d69..35f500ea 100644 --- a/store-benchmark/pom.xml +++ b/store-benchmark/pom.xml @@ -4,7 +4,7 @@ org.gephi graphstore-benchmark - 0.1.0-SNAPSHOT + 0.5.7-SNAPSHOT jar graphstore-benchmark @@ -59,7 +59,7 @@ ${project.groupId} graphstore - ${project.version} + 0.5.7-SNAPSHOT diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/DataStructureBenchmark.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/DataStructureBenchmark.java index 24b23c11..79a78248 100644 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/DataStructureBenchmark.java +++ b/store-benchmark/src/main/java/org/gephi/graph/benchmark/DataStructureBenchmark.java @@ -36,6 +36,8 @@ import it.unimi.dsi.fastutil.objects.ObjectBigList; import it.unimi.dsi.fastutil.objects.ObjectList; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.gephi.graph.api.types.TimestampSet; + import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; @@ -43,7 +45,6 @@ import java.util.List; import java.util.Map; import java.util.Random; -import org.gephi.attribute.time.TimestampSet; /** * @@ -654,7 +655,8 @@ public void run() { for (int i = 0; i < size; i++) { TimestampSet set = new TimestampSet(timestamps); for (int j = 0; j < timestamps; j++) { - set.add(rand.nextInt(timestamps)); + final double val = rand.nextInt(timestamps); + set.add(val); trees.add(set); } } diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/EdgeStoreBenchmark.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/EdgeStoreBenchmark.java index 16f56142..fa606cec 100644 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/EdgeStoreBenchmark.java +++ b/store-benchmark/src/main/java/org/gephi/graph/benchmark/EdgeStoreBenchmark.java @@ -2,10 +2,11 @@ import java.util.Iterator; import java.util.List; + +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Node; -import org.gephi.graph.store.EdgeImpl; -import org.gephi.graph.store.EdgeStore; +import org.gephi.graph.impl.EdgeStore; /** * @@ -16,7 +17,10 @@ public class EdgeStoreBenchmark { private Object object; public Runnable pushEdgeStore(int nodes, double prob) { - final RandomGraph graph = new RandomGraph(nodes, prob).generate(); + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); final List nodeList = graph.getNodes(); final List edgeList = graph.getEdges(); @@ -35,7 +39,10 @@ public void run() { } public Runnable iterateEdgeStore(int nodes, double prob) { - final RandomGraph graph = new RandomGraph(nodes, prob).generate().commit(); + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); Runnable runnable = new Runnable() { @@ -43,7 +50,7 @@ public Runnable iterateEdgeStore(int nodes, double prob) { public void run() { Iterator itr = edgeStore.iterator(); for (; itr.hasNext();) { - object = (EdgeImpl) itr.next(); + object = itr.next(); } } }; @@ -51,7 +58,10 @@ public void run() { } public Runnable iterateEdgeStoreNeighborsOut(int nodes, double prob) { - final RandomGraph graph = new RandomGraph(nodes, prob).generate().commit(); + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); final List nodeList = graph.getNodes(); @@ -61,7 +71,7 @@ public void run() { for (Node node : nodeList) { Iterator itr = edgeStore.edgeOutIterator(node); for (; itr.hasNext();) { - object = (EdgeImpl) itr.next(); + object = itr.next(); } } } @@ -70,7 +80,10 @@ public void run() { } public Runnable iterateEdgeStoreNeighborsInOut(int nodes, double prob) { - final RandomGraph graph = new RandomGraph(nodes, prob).generate().commit(); + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); final List nodeList = graph.getNodes(); @@ -80,7 +93,7 @@ public void run() { for (Node node : nodeList) { Iterator itr = edgeStore.edgeIterator(node); for (; itr.hasNext();) { - object = (EdgeImpl) itr.next(); + object = itr.next(); } } } @@ -89,7 +102,10 @@ public void run() { } public Runnable resetEdgeStore(int nodes, double prob) { - final RandomGraph graph = new RandomGraph(nodes, prob).generate().commit(); + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); final List edgeList = graph.getEdges(); diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/Generator.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/Generator.java index 7438ca2c..b799a365 100644 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/Generator.java +++ b/store-benchmark/src/main/java/org/gephi/graph/benchmark/Generator.java @@ -2,11 +2,13 @@ import java.util.ArrayList; import java.util.List; + +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.GraphFactory; import org.gephi.graph.api.Node; -import org.gephi.graph.store.GraphModelImpl; -import org.gephi.graph.store.GraphStore; +import org.gephi.graph.impl.GraphModelImpl; +import org.gephi.graph.impl.GraphStore; public abstract class Generator { @@ -16,7 +18,11 @@ public abstract class Generator { protected List edges; public Generator() { - GraphModelImpl model = new GraphModelImpl(); + this(new Configuration()); + } + + public Generator(final Configuration config) { + GraphModelImpl model = new GraphModelImpl(config); factory = model.factory(); graphStore = model.getStore(); nodes = new ArrayList(); diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/KleinbergGraph.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/KleinbergGraph.java index 67fd6c16..46019a24 100644 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/KleinbergGraph.java +++ b/store-benchmark/src/main/java/org/gephi/graph/benchmark/KleinbergGraph.java @@ -16,13 +16,10 @@ package org.gephi.graph.benchmark; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import java.util.Random; import org.gephi.graph.api.Edge; -import org.gephi.graph.api.GraphFactory; import org.gephi.graph.api.Node; -import org.gephi.graph.store.EdgeImpl; -import org.gephi.graph.store.GraphModelImpl; -import org.gephi.graph.store.GraphStore; + +import java.util.Random; /** * Generates a directed connected graph. @@ -79,8 +76,11 @@ public KleinbergGraph generate() { if ((isTorusBased() || !isTorusBased() && k >= 0 && k < n && l >= 0 && l < n) && d(i, j, k, l) <= p && nodes.get(i * n + j) != nodes.get(((k + n) % n) * n + ((l + n) % n))) { Edge edge = factory.newEdge(nodes.get(i * n + j), nodes.get(((k + n) % n) * n + ((l + n) % n)), 0, true); - edges.add(edge); - edgeSet.add(((EdgeImpl) edge).getLongId()); + Object id = edge.getId(); + if (id instanceof Number) { + edges.add(edge); + edgeSet.add(((Number) id).longValue()); + } } } } @@ -111,10 +111,13 @@ && d(i, j, k, l) <= p && nodes.get(i * n + j) != nodes.get(((k + n) % n) * n + ( if (!isTorusBased() && d(i, j, k, l) > p || isTorusBased() && dtb(i, j, k, l) > p) { pki += Math.pow(!isTorusBased() ? d(i, j, k, l) : dtb(i, j, k, l), -r) / sum; Edge edge = factory.newEdge(nodes.get(i * n + j), nodes.get(k * n + l), 0, true); - if (b <= pki && !edgeSet.contains(((EdgeImpl) edge).getLongId())) { - edges.add(edge); - edgeSet.add(((EdgeImpl) edge).getLongId()); - e = true; + Object id = edge.getId(); + if (id instanceof Number) { + if (b <= pki && !edgeSet.contains(((Number) id).longValue())) { + edges.add(edge); + edgeSet.add(((Number) id).longValue()); + e = true; + } } } } diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/NodeStoreBenchmark.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/NodeStoreBenchmark.java index 35d777c3..ec807843 100644 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/NodeStoreBenchmark.java +++ b/store-benchmark/src/main/java/org/gephi/graph/benchmark/NodeStoreBenchmark.java @@ -17,9 +17,11 @@ import java.util.Iterator; import java.util.List; + +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Node; -import org.gephi.graph.store.NodeImpl; -import org.gephi.graph.store.NodeStore; +import org.gephi.graph.impl.NodeImpl; +import org.gephi.graph.impl.NodeStore; /** * @@ -30,7 +32,10 @@ public class NodeStoreBenchmark { private Object object; public Runnable iterateStore(final int nodes) { - final RandomGraph graph = new RandomGraph(nodes, 0).generate().commit(); + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, 0, config).generate().commit(); final NodeStore nodeStore = graph.getStore().getNodeStore(); Runnable runnable = new Runnable() { @Override @@ -46,7 +51,10 @@ public void run() { } public Runnable resetNodeStore(final int nodes) { - final RandomGraph graph = new RandomGraph(nodes, 0).generate().commit(); + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, 0, config).generate().commit(); final NodeStore nodeStore = graph.getStore().getNodeStore(); final List nodeList = graph.getNodes(); Runnable runnable = new Runnable() { @@ -64,7 +72,10 @@ public void run() { } public Runnable pushStore(int nodes) { - final RandomGraph graph = new RandomGraph(nodes, 0).generate(); + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, 0, config).generate(); final NodeStore nodeStore = graph.getStore().getNodeStore(); final List nodeList = graph.getNodes(); diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/RandomGraph.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/RandomGraph.java index 6c0453c2..82c10447 100644 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/RandomGraph.java +++ b/store-benchmark/src/main/java/org/gephi/graph/benchmark/RandomGraph.java @@ -16,9 +16,11 @@ package org.gephi.graph.benchmark; import java.util.Random; + +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Node; -import org.gephi.graph.store.NodeImpl; +import org.gephi.graph.impl.NodeImpl; /** * Generates directed connected random graph with wiring probability p @@ -30,16 +32,24 @@ public class RandomGraph extends Generator { protected final int numberOfNodes; protected final int numberOfEdges; protected final double wiringProbability; - + public RandomGraph(int n, double p) { - super(); + this(n, p, new Configuration()); + } + + public RandomGraph(int n, double p, Configuration config) { + super(config); numberOfNodes = n; numberOfEdges = (int)(n*(n-1)*p); wiringProbability = p; } public RandomGraph(int nodes, int edges) { - this(nodes, ((double)edges)/(nodes*(nodes-1))); + this(nodes, edges, new Configuration()); + } + + public RandomGraph(int nodes, int edges, Configuration confi) { + this(nodes, ((double)edges)/(nodes*(nodes-1)), confi); } @Override From 2271d7d2031cc9325645b6e14db3f828f867c6eb Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Wed, 7 Jun 2017 21:12:59 +0700 Subject: [PATCH 14/15] Merge remote-tracking branch 'gephi/master' Merged with master. --- .idea/compiler.xml | 18 + .idea/encodings.xml | 7 + .idea/kotlinc.xml | 7 + .idea/libraries/Maven__colt_colt_1_2_0.xml | 13 + .../Maven__com_beust_jcommander_1_27.xml | 13 + .../Maven__com_beust_jcommander_1_64.xml | 13 + .../Maven__concurrent_concurrent_1_3_4.xml | 13 + .../Maven__it_unimi_dsi_fastutil_7_2_0.xml | 13 + .../Maven__joda_time_joda_time_2_9_9.xml | 13 + .idea/libraries/Maven__junit_junit_4_10.xml | 13 + .../Maven__org_beanshell_bsh_2_0b4.xml | 13 + .../Maven__org_hamcrest_hamcrest_core_1_1.xml | 13 + .../Maven__org_testng_testng_6_11.xml | 13 + .../Maven__org_testng_testng_6_8_5.xml | 13 + .../Maven__org_yaml_snakeyaml_1_17.xml | 13 + .../Maven__org_yaml_snakeyaml_1_6.xml | 13 + .idea/libraries/R_User_Library.xml | 7 + .idea/misc.xml | 30 + .idea/modules.xml | 9 + .idea/vcs.xml | 6 + .idea/workspace.xml | 1033 +++++++++++++++++ store-benchmark/graphstore-benchmark.iml | 25 + store-benchmark/pom.xml | 4 +- store/graphstore.iml | 21 + store/pom.xml | 5 +- .../java/org/gephi/graph/api/GraphModel.java | 20 + .../gephi/graph/api/types/IntervalMap.java | 4 + .../java/org/gephi/graph/impl/EdgeImpl.java | 6 + .../org/gephi/graph/impl/Serialization.java | 68 +- .../org/gephi/graph/impl/EdgeImplTest.java | 19 + .../org/gephi/graph/impl/GraphModelTest.java | 13 + .../gephi/graph/impl/SerializationTest.java | 22 + 32 files changed, 1460 insertions(+), 33 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/libraries/Maven__colt_colt_1_2_0.xml create mode 100644 .idea/libraries/Maven__com_beust_jcommander_1_27.xml create mode 100644 .idea/libraries/Maven__com_beust_jcommander_1_64.xml create mode 100644 .idea/libraries/Maven__concurrent_concurrent_1_3_4.xml create mode 100644 .idea/libraries/Maven__it_unimi_dsi_fastutil_7_2_0.xml create mode 100644 .idea/libraries/Maven__joda_time_joda_time_2_9_9.xml create mode 100644 .idea/libraries/Maven__junit_junit_4_10.xml create mode 100644 .idea/libraries/Maven__org_beanshell_bsh_2_0b4.xml create mode 100644 .idea/libraries/Maven__org_hamcrest_hamcrest_core_1_1.xml create mode 100644 .idea/libraries/Maven__org_testng_testng_6_11.xml create mode 100644 .idea/libraries/Maven__org_testng_testng_6_8_5.xml create mode 100644 .idea/libraries/Maven__org_yaml_snakeyaml_1_17.xml create mode 100644 .idea/libraries/Maven__org_yaml_snakeyaml_1_6.xml create mode 100644 .idea/libraries/R_User_Library.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 store-benchmark/graphstore-benchmark.iml create mode 100644 store/graphstore.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..8a9dc2d9 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 00000000..1a10127e --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 00000000..1c24f9a8 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__colt_colt_1_2_0.xml b/.idea/libraries/Maven__colt_colt_1_2_0.xml new file mode 100644 index 00000000..58562acd --- /dev/null +++ b/.idea/libraries/Maven__colt_colt_1_2_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_beust_jcommander_1_27.xml b/.idea/libraries/Maven__com_beust_jcommander_1_27.xml new file mode 100644 index 00000000..f0f9060d --- /dev/null +++ b/.idea/libraries/Maven__com_beust_jcommander_1_27.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_beust_jcommander_1_64.xml b/.idea/libraries/Maven__com_beust_jcommander_1_64.xml new file mode 100644 index 00000000..e72647cc --- /dev/null +++ b/.idea/libraries/Maven__com_beust_jcommander_1_64.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__concurrent_concurrent_1_3_4.xml b/.idea/libraries/Maven__concurrent_concurrent_1_3_4.xml new file mode 100644 index 00000000..4acbd2f6 --- /dev/null +++ b/.idea/libraries/Maven__concurrent_concurrent_1_3_4.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__it_unimi_dsi_fastutil_7_2_0.xml b/.idea/libraries/Maven__it_unimi_dsi_fastutil_7_2_0.xml new file mode 100644 index 00000000..0d38b062 --- /dev/null +++ b/.idea/libraries/Maven__it_unimi_dsi_fastutil_7_2_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__joda_time_joda_time_2_9_9.xml b/.idea/libraries/Maven__joda_time_joda_time_2_9_9.xml new file mode 100644 index 00000000..a468f58d --- /dev/null +++ b/.idea/libraries/Maven__joda_time_joda_time_2_9_9.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__junit_junit_4_10.xml b/.idea/libraries/Maven__junit_junit_4_10.xml new file mode 100644 index 00000000..ed8bf5fe --- /dev/null +++ b/.idea/libraries/Maven__junit_junit_4_10.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_beanshell_bsh_2_0b4.xml b/.idea/libraries/Maven__org_beanshell_bsh_2_0b4.xml new file mode 100644 index 00000000..d6f17aa7 --- /dev/null +++ b/.idea/libraries/Maven__org_beanshell_bsh_2_0b4.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_1.xml b/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_1.xml new file mode 100644 index 00000000..acdf4430 --- /dev/null +++ b/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_testng_testng_6_11.xml b/.idea/libraries/Maven__org_testng_testng_6_11.xml new file mode 100644 index 00000000..51052227 --- /dev/null +++ b/.idea/libraries/Maven__org_testng_testng_6_11.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_testng_testng_6_8_5.xml b/.idea/libraries/Maven__org_testng_testng_6_8_5.xml new file mode 100644 index 00000000..88876870 --- /dev/null +++ b/.idea/libraries/Maven__org_testng_testng_6_8_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_yaml_snakeyaml_1_17.xml b/.idea/libraries/Maven__org_yaml_snakeyaml_1_17.xml new file mode 100644 index 00000000..20e2920c --- /dev/null +++ b/.idea/libraries/Maven__org_yaml_snakeyaml_1_17.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_yaml_snakeyaml_1_6.xml b/.idea/libraries/Maven__org_yaml_snakeyaml_1_6.xml new file mode 100644 index 00000000..0f8bd198 --- /dev/null +++ b/.idea/libraries/Maven__org_yaml_snakeyaml_1_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/R_User_Library.xml b/.idea/libraries/R_User_Library.xml new file mode 100644 index 00000000..2778a36f --- /dev/null +++ b/.idea/libraries/R_User_Library.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..a2baac2c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + 1.8 + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..b745614b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..6569a33a --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,1033 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fastut + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1493983977209 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + file://$PROJECT_DIR$/store/src/main/java/org/gephi/graph/impl/NodeImpl.java + 244 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/store-benchmark/graphstore-benchmark.iml b/store-benchmark/graphstore-benchmark.iml new file mode 100644 index 00000000..8e218cee --- /dev/null +++ b/store-benchmark/graphstore-benchmark.iml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/store-benchmark/pom.xml b/store-benchmark/pom.xml index 35f500ea..c7ef55c8 100644 --- a/store-benchmark/pom.xml +++ b/store-benchmark/pom.xml @@ -4,7 +4,7 @@ org.gephi graphstore-benchmark - 0.5.7-SNAPSHOT + 0.5.8-SNAPSHOT jar graphstore-benchmark @@ -59,7 +59,7 @@ ${project.groupId} graphstore - 0.5.7-SNAPSHOT + 0.5.8-SNAPSHOT diff --git a/store/graphstore.iml b/store/graphstore.iml new file mode 100644 index 00000000..c11dda7f --- /dev/null +++ b/store/graphstore.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/store/pom.xml b/store/pom.xml index 7609dfd8..362f931f 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.5.7-SNAPSHOT + 0.5.8-SNAPSHOT jar GraphStore @@ -91,6 +91,9 @@ org.apache.maven.plugins maven-surefire-plugin 2.20 + + false + org.apache.maven.plugins diff --git a/store/src/main/java/org/gephi/graph/api/GraphModel.java b/store/src/main/java/org/gephi/graph/api/GraphModel.java index b0311904..02beaa23 100644 --- a/store/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/store/src/main/java/org/gephi/graph/api/GraphModel.java @@ -139,6 +139,26 @@ public static GraphModel read(DataInput input) throws IOException { } } + /** + * Read the input and return the read graph model without + * an explicit version header in the input. To be used with old + * graphstore serialized data prior to version 0.4 (first, that added + * the version header). + * + * @param input data input to read from + * @param graphStoreVersion Forced version to use + * @return new graph model + * @throws IOException if an io error occurs + */ + public static GraphModel readWithoutVersionHeader(DataInput input, float graphStoreVersion) throws IOException { + try { + org.gephi.graph.impl.Serialization s = new org.gephi.graph.impl.Serialization(); + return s.deserializeGraphModelWithoutVersionPrefix(input, graphStoreVersion); + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } + /** * Write graphModel to output. * diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java index e796528f..adbcdc88 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java @@ -122,6 +122,10 @@ public boolean remove(Interval interval) { @Override public Object get(Interval interval, Estimator estimator) { + if (estimator == null) { + return get(interval, (T) null); + } + if (!isSupported(estimator)) { throw new UnsupportedOperationException("Not supported estimator."); } diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java index 03768784..efa8ff7b 100644 --- a/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -147,6 +147,9 @@ public double getWeight(double timestamp) { throw new IllegalStateException("The weight is static, call getWeight() instead"); } TimestampMap dynamicValue = (TimestampMap) weightValue; + if (dynamicValue == null) { + return 0.0; + } return (Double) dynamicValue.get(timestamp, 0.0); } } @@ -160,6 +163,9 @@ public double getWeight(Interval interval) { throw new IllegalStateException("The weight is static, call getWeight() instead"); } IntervalMap dynamicValue = (IntervalMap) weightValue; + if (dynamicValue == null) { + return 0.0; + } return (Double) dynamicValue.get(interval, 0.0); } } diff --git a/store/src/main/java/org/gephi/graph/impl/Serialization.java b/store/src/main/java/org/gephi/graph/impl/Serialization.java index ba5c0a38..538f8b24 100644 --- a/store/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/store/src/main/java/org/gephi/graph/impl/Serialization.java @@ -246,6 +246,14 @@ public GraphModelImpl deserializeGraphModel(DataInput is) throws IOException, Cl return model; } + public GraphModelImpl deserializeGraphModelWithoutVersionPrefix(DataInput is, float version) throws IOException, ClassNotFoundException { + readVersion = version; + Configuration config = (Configuration) deserialize(is); + model = new GraphModelImpl(config); + deserialize(is); + return model; + } + public void serializeGraphStore(DataOutput out, GraphStore store) throws IOException { // Configuration serializeGraphStoreConfiguration(out); @@ -1269,7 +1277,7 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept out.write(NULL); } else if (clazz == Boolean.class) { - if (((Boolean) obj).booleanValue()) { + if (((Boolean) obj)) { out.write(BOOLEAN_TRUE); } else { out.write(BOOLEAN_FALSE); @@ -1771,46 +1779,46 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce ret = Boolean.FALSE; break; case INTEGER_MINUS_1: - ret = Integer.valueOf(-1); + ret = -1; break; case INTEGER_0: - ret = Integer.valueOf(0); + ret = 0; break; case INTEGER_1: - ret = Integer.valueOf(1); + ret = 1; break; case INTEGER_2: - ret = Integer.valueOf(2); + ret = 2; break; case INTEGER_3: - ret = Integer.valueOf(3); + ret = 3; break; case INTEGER_4: - ret = Integer.valueOf(4); + ret = 4; break; case INTEGER_5: - ret = Integer.valueOf(5); + ret = 5; break; case INTEGER_6: - ret = Integer.valueOf(6); + ret = 6; break; case INTEGER_7: - ret = Integer.valueOf(7); + ret = 7; break; case INTEGER_8: - ret = Integer.valueOf(8); + ret = 8; break; case INTEGER_MINUS_MAX: - ret = Integer.valueOf(Integer.MIN_VALUE); + ret = Integer.MIN_VALUE; break; case INTEGER_255: - ret = Integer.valueOf(is.readUnsignedByte()); + ret = is.readUnsignedByte(); break; case INTEGER_PACK_NEG: - ret = Integer.valueOf(-LongPacker.unpackInt(is)); + ret = -LongPacker.unpackInt(is); break; case INTEGER_PACK: - ret = Integer.valueOf(LongPacker.unpackInt(is)); + ret = LongPacker.unpackInt(is); break; case LONG_MINUS_1: ret = Long.valueOf(-1); @@ -1846,40 +1854,40 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce ret = Long.valueOf(is.readUnsignedByte()); break; case LONG_PACK_NEG: - ret = Long.valueOf(-LongPacker.unpackLong(is)); + ret = -LongPacker.unpackLong(is); break; case LONG_PACK: - ret = Long.valueOf(LongPacker.unpackLong(is)); + ret = LongPacker.unpackLong(is); break; case LONG_MINUS_MAX: - ret = Long.valueOf(Long.MIN_VALUE); + ret = Long.MIN_VALUE; break; case SHORT_MINUS_1: - ret = Short.valueOf((short) -1); + ret = ((short) -1); break; case SHORT_0: - ret = Short.valueOf((short) 0); + ret = ((short) 0); break; case SHORT_1: - ret = Short.valueOf((short) 1); + ret = ((short) 1); break; case SHORT_255: - ret = Short.valueOf((short) is.readUnsignedByte()); + ret = ((short) is.readUnsignedByte()); break; case SHORT_FULL: - ret = Short.valueOf(is.readShort()); + ret = is.readShort(); break; case BYTE_MINUS_1: - ret = Byte.valueOf((byte) -1); + ret = ((byte) -1); break; case BYTE_0: - ret = Byte.valueOf((byte) 0); + ret = ((byte) 0); break; case BYTE_1: - ret = Byte.valueOf((byte) 1); + ret = ((byte) 1); break; case BYTE_FULL: - ret = Byte.valueOf(is.readByte()); + ret = (is.readByte()); break; case SHORT_ARRAY: int size = LongPacker.unpackInt(is); @@ -1917,7 +1925,7 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce } break; case CHAR: - ret = Character.valueOf(is.readChar()); + ret = is.readChar(); break; case FLOAT_MINUS_1: ret = Float.valueOf(-1); @@ -1935,7 +1943,7 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce ret = Float.valueOf(is.readShort()); break; case FLOAT_FULL: - ret = Float.valueOf(is.readFloat()); + ret = is.readFloat(); break; case DOUBLE_MINUS_1: ret = Double.valueOf(-1); @@ -1953,7 +1961,7 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce ret = Double.valueOf(is.readShort()); break; case DOUBLE_FULL: - ret = Double.valueOf(is.readDouble()); + ret = is.readDouble(); break; case BIGINTEGER: ret = new BigInteger(deserializeArrayByteInt(is)); diff --git a/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java b/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java index 65947f32..6f1c93fb 100644 --- a/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java +++ b/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java @@ -54,6 +54,15 @@ public void testGetDefaultTimestampWeight() { Assert.assertEquals(e.getWeight(2.0), 0.0); } + @Test + public void testGetDefaultTimestampWeightWhenNotSet() { + Configuration config = new Configuration(); + config.setEdgeWeightType(TimestampDoubleMap.class); + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); + Edge e = graphStore.getEdge("0"); + Assert.assertEquals(e.getWeight(2.0), 0.0); + } + @Test public void testGetDefaultIntervalWeight() { Configuration config = new Configuration(); @@ -65,6 +74,16 @@ public void testGetDefaultIntervalWeight() { Assert.assertEquals(e.getWeight(new Interval(2.0, 4.0)), 0.0); } + @Test + public void testGetDefaultIntervalWeightWhenNotSet() { + Configuration config = new Configuration(); + config.setTimeRepresentation(TimeRepresentation.INTERVAL); + config.setEdgeWeightType(IntervalDoubleMap.class); + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); + Edge e = graphStore.getEdge("0"); + Assert.assertEquals(e.getWeight(new Interval(2.0, 4.0)), 0.0); + } + @Test public void testSetTimestampWeight() { Configuration config = new Configuration(); diff --git a/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 1e419ae5..8f2cc54a 100644 --- a/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -435,6 +435,19 @@ public void testSerialization() throws IOException { Assert.assertTrue(readModelImpl.deepEquals(readModelImpl)); } + @Test + public void testSerializationReadWithoutVersionHeader() throws IOException { + DataInputOutput dio = new DataInputOutput(); + GraphModelImpl graphModelImpl = GraphGenerator.generateSmallGraphStore().graphModel; + GraphModel.Serialization.write(dio, graphModelImpl); + byte[] bytes = dio.toByteArray(); + dio.reset(bytes); + dio.skip(5);// id byte + FLOAT_255 (int) + GraphModelImpl readModelImpl = (GraphModelImpl) GraphModel.Serialization + .readWithoutVersionHeader(dio, Serialization.VERSION); + Assert.assertTrue(readModelImpl.deepEquals(readModelImpl)); + } + @Test public void testGetConfiguration() { Configuration config = new Configuration(); diff --git a/store/src/test/java/org/gephi/graph/impl/SerializationTest.java b/store/src/test/java/org/gephi/graph/impl/SerializationTest.java index eeebf09a..5d6bc39f 100644 --- a/store/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/store/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -41,6 +41,7 @@ import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import it.unimi.dsi.fastutil.shorts.ShortArrayList; import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import java.io.DataOutput; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; @@ -1198,4 +1199,25 @@ public void testSmallUndirectedGraphModel() throws Exception { GraphModelImpl read = ser.deserializeGraphModel(dio.reset(bytes)); Assert.assertTrue(read.deepEquals(gm)); } + + @Test + public void testDeserializeWithoutVersion() throws Exception { + GraphModelImpl gm = GraphGenerator.generateSmallGraphStore().graphModel; + Serialization ser = new Serialization(gm) { + @Override + public void serializeGraphModel(DataOutput out, GraphModelImpl model) throws IOException { + this.model = model; + serialize(out, model.configuration); + serialize(out, model.store); + } + }; + + DataInputOutput dio = new DataInputOutput(); + ser.serializeGraphModel(dio, gm); + byte[] bytes = dio.toByteArray(); + + ser = new Serialization(); + GraphModelImpl read = ser.deserializeGraphModelWithoutVersionPrefix(dio.reset(bytes), Serialization.VERSION); + Assert.assertTrue(read.deepEquals(gm)); + } } From 0930b8e2fd9e3abd18d021c898f68bda4e50998c Mon Sep 17 00:00:00 2001 From: Alex Bowen Date: Wed, 21 Feb 2018 21:09:28 -0500 Subject: [PATCH 15/15] fixed unit tests compile issues --- .../Maven__it_unimi_dsi_fastutil_7_2_0.xml | 13 - .idea/workspace.xml | 441 ++++++++++-------- store-benchmark/graphstore-benchmark.iml | 4 +- store/graphstore.iml | 4 +- .../org/gephi/graph/impl/EdgeImplTest.java | 20 - 5 files changed, 260 insertions(+), 222 deletions(-) delete mode 100644 .idea/libraries/Maven__it_unimi_dsi_fastutil_7_2_0.xml diff --git a/.idea/libraries/Maven__it_unimi_dsi_fastutil_7_2_0.xml b/.idea/libraries/Maven__it_unimi_dsi_fastutil_7_2_0.xml deleted file mode 100644 index 0d38b062..00000000 --- a/.idea/libraries/Maven__it_unimi_dsi_fastutil_7_2_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 6569a33a..581cdcd0 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,13 +2,10 @@ - - - - - - - + + + + @@ -19,14 +16,13 @@ - - + - - + + @@ -34,7 +30,7 @@ - + @@ -46,11 +42,21 @@ + + + + + + + + + + - - + + @@ -59,8 +65,8 @@ - - + + @@ -95,6 +101,7 @@ @@ -133,30 +140,53 @@ - + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +