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__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..581cdcd0
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,1100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fastut
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ DEFINITION_ORDER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ project
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1493983977209
+
+
+ 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..8ccefdd2
--- /dev/null
+++ b/store-benchmark/graphstore-benchmark.iml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/store/graphstore.iml b/store/graphstore.iml
new file mode 100644
index 00000000..933a512d
--- /dev/null
+++ b/store/graphstore.iml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
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 493f064d..02beaa23 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.
@@ -371,6 +372,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..1e6a9713
--- /dev/null
+++ b/store/src/main/java/org/gephi/graph/api/HierarchicalNodeGroup.java
@@ -0,0 +1,64 @@
+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 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).
+ */
+ 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 b39903db..bbc370bc 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 caf28b93..c98e943c 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..6abed24d
--- /dev/null
+++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphDecorator.java
@@ -0,0 +1,1589 @@
+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)), false);
+ }
+
+ @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)), false);
+ }
+
+ @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)), false);
+ }
+
+ @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)), false);
+ }
+
+ @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)), false);
+ }
+
+ @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)), false);
+ }
+
+ @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 extends Edge> 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 extends Node> 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 extends Edge> edges) {
+ graphStore.autoWriteLock();
+ try {
+ return this.removeAllEdgesWithLock(edges.iterator());
+ } finally {
+ graphStore.autoWriteUnlock();
+ }
+ }
+
+ private boolean removeAllEdgesWithLock(Iterator extends Edge> 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 extends Node> 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(final Node node) {
+ checkValidInViewNodeObject(node);
+ Set set = new HashSet();
+ 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());
+ }
+ }
+ set.removeAll(mapped);
+ return graphStore.getNodeIterableWrapper(new NodeViewIterator(set.iterator()));
+ }
+
+ @Override
+ public NodeIterable getNeighbors(final Node node, final int type) {
+ checkValidInViewNodeObject(node);
+ Set set = new HashSet();
+ 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());
+ }
+ }
+ set.removeAll(mapped);
+ 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)), false);
+ }
+
+ @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)), false);
+ }
+
+ @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 extends Callable>> 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 Edge pointer;
+
+ public EdgeViewIterator(Iterator edgeIterator) {
+ this.edgeIterator = edgeIterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ pointer = null;
+ while (pointer == null) {
+ if (!edgeIterator.hasNext()) {
+ return false;
+ }
+ pointer = edgeIterator.next();
+ if (pointer != null) {
+ if (pointer instanceof EdgeImpl && !view.containsEdge((EdgeImpl) pointer)) {
+ pointer = null;
+ }
+ if (pointer instanceof MappedEdgeDecorator && !view
+ .containsEdge(((MappedEdgeDecorator) pointer).edge)) {
+ 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 Edge pointer;
+
+ public UndirectedEdgeViewIterator(Iterator itr) {
+ this.itr = itr;
+ }
+
+ @Override
+ public boolean hasNext() {
+ pointer = null;
+ while (pointer == null) {
+ if (!itr.hasNext()) {
+ return false;
+ }
+ 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;
+ }
+
+ @Override
+ public Edge next() {
+ return decorateEdge(pointer);
+ }
+
+ @Override
+ public void remove() {
+ itr.remove();
+ }
+ }
+
+ private class NeighborsIterator implements Iterator {
+ private final Node node;
+
+ private final Iterator itr;
+
+ public NeighborsIterator(Node 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;
+ }
+
+ if (edge instanceof EdgeImpl) {
+ return new MappedEdgeDecorator((EdgeImpl) edge, mappedSource, mappedTarget);
+ } else {
+ return edge;
+ }
+ }
+
+ protected class MappedEdgeDecorator implements Edge {
+ private final EdgeImpl edge;
+
+ private final Node source;
+
+ private final Node target;
+
+ private MappedEdgeDecorator(final EdgeImpl 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..1e2ece49
--- /dev/null
+++ b/store/src/main/java/org/gephi/graph/impl/HierarchicalGraphViewImpl.java
@@ -0,0 +1,560 @@
+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.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 Set groups = new HashSet();
+ groups.add(this.root);
+ for (final HierarchicalNodeGroupImpl group : this.root.getGroups(true)) {
+ groups.add(group);
+ }
+ return Collections.unmodifiableCollection(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() && !parentGroup.hasCollapsedParent()) {
+ 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() {
+ graphStore.autoWriteLock();
+ try {
+ this.setCollapsedWithLock(false);
+ } finally {
+ graphStore.autoWriteUnlock();
+ }
+ }
+
+ @Override
+ public void collapse() {
+ 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.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();
+ try {
+ return Collections.unmodifiableCollection(new ArrayList(this.nodeMap.keySet()));
+ } finally {
+ graphStore.autoReadUnlock();
+ }
+ }
+
+ @Override
+ public Collection getNodes(boolean recursive) {
+ graphStore.autoReadLock();
+ try {
+ if (recursive) {
+ final Set set = new HashSet(this.nodeMap.size());
+ for (final Map.Entry entry : this.nodeMap.entrySet()) {
+ set.add(entry.getKey());
+ set.addAll(entry.getValue().getNodes(true));
+ }
+ return Collections.unmodifiableCollection(set);
+ } else {
+ return Collections.unmodifiableCollection(new ArrayList(this.nodeMap.keySet()));
+ }
+ } finally {
+ graphStore.autoReadUnlock();
+ }
+ }
+
+ public Collection getGroups(final boolean recursive) {
+ graphStore.autoReadLock();
+ try {
+ if (recursive) {
+ final Collection set = new HashSet();
+ for (final HierarchicalNodeGroupImpl group : this.nodeMap.values()) {
+ set.add(group);
+ set.addAll(group.getGroups(true));
+ }
+ return Collections.unmodifiableCollection(set);
+ } else {
+ return Collections.unmodifiableCollection(new ArrayList(this.nodeMap
+ .values()));
+ }
+ } finally {
+ graphStore.autoReadUnlock();
+ }
+ }
+
+ @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/main/java/org/gephi/graph/impl/Serialization.java b/store/src/main/java/org/gephi/graph/impl/Serialization.java
index 69487f88..0e84998b 100644
--- a/store/src/main/java/org/gephi/graph/impl/Serialization.java
+++ b/store/src/main/java/org/gephi/graph/impl/Serialization.java
@@ -608,7 +608,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);
@@ -622,7 +622,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 {
@@ -646,7 +646,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;
@@ -656,7 +656,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/EdgeImplTest.java b/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java
index 5be79887..4ccd7927 100644
--- a/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java
+++ b/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java
@@ -62,7 +62,6 @@ public void testGetDefaultTimestampWeightWhenNotSet() {
Edge e = graphStore.getEdge("0");
Assert.assertEquals(e.getWeight(2.0), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING);
}
-
@Test
public void testGetDefaultIntervalWeight() {
Configuration config = new Configuration();
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..5eb2294c
--- /dev/null
+++ b/store/src/test/java/org/gephi/graph/impl/HierarchicalGraphTest.java
@@ -0,0 +1,209 @@
+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.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+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);
+ }
+ }
+
+ @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());
+ }
+ }
+ }
+}