Skip to content

Commit 6980a94

Browse files
authored
Add createView method to GraphModel based on predicates (#259)
* Add create view based on parallel streams * Complete adding view creation based on predicates
1 parent e107570 commit 6980a94

File tree

8 files changed

+337
-46
lines changed

8 files changed

+337
-46
lines changed

src/main/java/org/gephi/graph/api/GraphModel.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.DataOutput;
2020
import java.io.IOException;
2121
import java.time.ZoneId;
22+
import java.util.function.Predicate;
2223
import org.gephi.graph.impl.GraphModelImpl;
2324

2425
/**
@@ -488,16 +489,38 @@ public static interface DefaultColumns {
488489

489490
/**
490491
* Creates a new graph view.
492+
* <p>
493+
* By default, the view applies to both nodes and edges, so this is equivalent
494+
* to {@link #createView(boolean, boolean) createView(true, true)}.
495+
* <p>
496+
* New views are by default empty, i.e. no nodes and no edges are visible in the
497+
* view.
491498
*
492499
* @return newly created graph view
493500
*/
494501
public GraphView createView();
495502

503+
/**
504+
* Creates a new graph view.
505+
* <p>
506+
* The node and edge filters allows to restrict the view filtering to only nodes
507+
* or only edges. If node only, all edges connected to included nodes will be
508+
* included too. If edge only, all nodes are included but only the edges
509+
* matching the view are included.
510+
*
511+
* @param nodeFilter predicate to filter nodes, or null to include all nodes
512+
* @param edgeFilter predicate to filter edges, or null to include all edges
513+
* @return newly created graph view
514+
*/
515+
public GraphView createView(Predicate<Node> nodeFilter, Predicate<Edge> edgeFilter);
516+
496517
/**
497518
* Creates a new graph view.
498519
* <p>
499520
* The node and edge parameters allows to restrict the view filtering to only
500-
* nodes or only edges. By default, the view applies to both nodes and edges.
521+
* nodes or only edges. If node only, all edges connected to included nodes will
522+
* be included too. If edge only, all nodes are included but only the edges
523+
* matching the view are included.
501524
*
502525
* @param node true to enable node view, false otherwise
503526
* @param edge true to enable edge view, false otherwise

src/main/java/org/gephi/graph/impl/GraphModelImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.time.ZoneId;
1919
import java.util.Arrays;
20+
import java.util.function.Predicate;
2021
import org.gephi.graph.api.Configuration;
2122
import org.gephi.graph.api.DirectedGraph;
2223
import org.gephi.graph.api.DirectedSubgraph;
@@ -251,6 +252,11 @@ public GraphView createView() {
251252
return store.viewStore.createView();
252253
}
253254

255+
@Override
256+
public GraphView createView(Predicate<Node> nodeFilter, Predicate<Edge> edgeFilter) {
257+
return store.viewStore.createView(nodeFilter, edgeFilter);
258+
}
259+
254260
@Override
255261
public GraphView createView(boolean node, boolean edge) {
256262
return store.viewStore.createView(node, edge);

src/main/java/org/gephi/graph/impl/GraphViewDecorator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ public boolean hasEdge(final Object id) {
385385

386386
@Override
387387
public NodeIterable getNodes() {
388+
if (!view.isNodeView()) {
389+
return graphStore.getNodes();
390+
}
388391
return new NodeIterableWrapper(() -> new NodeViewIterator(graphStore.nodeStore.iterator()),
389392
NodeViewSpliterator::new, graphStore.getAutoLock());
390393
}

src/main/java/org/gephi/graph/impl/GraphViewImpl.java

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Iterator;
2323
import java.util.List;
2424
import java.util.Objects;
25+
import java.util.function.Predicate;
2526
import org.gephi.graph.api.DirectedSubgraph;
2627
import org.gephi.graph.api.Edge;
2728
import org.gephi.graph.api.Graph;
@@ -77,6 +78,53 @@ public GraphViewImpl(final GraphStore store, boolean nodes, boolean edges) {
7778
this.interval = Interval.INFINITY_INTERVAL;
7879
}
7980

81+
public GraphViewImpl(final GraphStore store, Predicate<Node> nodePredicate, Predicate<Edge> edgePredicate) {
82+
this(store, nodePredicate != null, edgePredicate != null);
83+
84+
// Fill - optimized with iterators and manual counting
85+
if (nodePredicate != null) {
86+
int count = 0;
87+
for (Node node : graphStore.nodeStore) {
88+
if (nodePredicate.test(node)) {
89+
nodeBitVector.set(node.getStoreId());
90+
count++;
91+
}
92+
}
93+
nodeCount = count;
94+
incrementNodeVersion();
95+
}
96+
97+
// Process edges with iterator
98+
int count = 0;
99+
for (Edge edge : graphStore.edgeStore) {
100+
// Cache store IDs
101+
int sourceId = edge.getSource().getStoreId();
102+
int targetId = edge.getTarget().getStoreId();
103+
104+
// Filter by node predicate if needed
105+
if (nodePredicate != null && (!nodeBitVector.get(sourceId) || !nodeBitVector.get(targetId))) {
106+
continue;
107+
}
108+
109+
// Filter by edge predicate if needed
110+
if (edgePredicate != null && !edgePredicate.test(edge)) {
111+
continue;
112+
}
113+
114+
edgeBitVector.set(edge.getStoreId());
115+
int type = edge.getType();
116+
typeCounts[type]++;
117+
count++;
118+
119+
if (((EdgeImpl) edge).isMutual() && !edge.isSelfLoop() && containsEdge(graphStore.edgeStore
120+
.get(edge.getTarget(), edge.getSource(), type, false))) {
121+
mutualEdgeTypeCounts[type]++;
122+
mutualEdgesCount++;
123+
}
124+
}
125+
edgeCount = count;
126+
}
127+
80128
public GraphViewImpl(final GraphViewImpl view, boolean nodes, boolean edges) {
81129
this.graphStore = view.graphStore;
82130
this.nodeView = nodes;
@@ -962,14 +1010,6 @@ protected void destroyAllObservers() {
9621010
}
9631011
}
9641012

965-
protected void ensureNodeVectorSize(NodeImpl node) {
966-
// BitSet automatically grows as needed, no manual resizing required
967-
}
968-
969-
protected void ensureEdgeVectorSize(EdgeImpl edge) {
970-
// BitSet automatically grows as needed, no manual resizing required
971-
}
972-
9731013
protected void setEdgeType(EdgeImpl edgeImpl, int oldType, boolean wasMutual) {
9741014
ensureTypeCountArrayCapacity(edgeImpl.type);
9751015
typeCounts[oldType]--;

src/main/java/org/gephi/graph/impl/GraphViewStore.java

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
1919
import it.unimi.dsi.fastutil.ints.IntSortedSet;
20+
import java.util.function.Predicate;
2021
import org.gephi.graph.api.DirectedSubgraph;
2122
import org.gephi.graph.api.Edge;
2223
import org.gephi.graph.api.Graph;
@@ -54,6 +55,17 @@ public GraphViewImpl createView() {
5455
return createView(true, true);
5556
}
5657

58+
public GraphViewImpl createView(Predicate<Node> nodeFilter, Predicate<Edge> edgeFilter) {
59+
graphStore.autoWriteLock();
60+
try {
61+
GraphViewImpl graphView = new GraphViewImpl(graphStore, nodeFilter, edgeFilter);
62+
addView(graphView);
63+
return graphView;
64+
} finally {
65+
graphStore.autoWriteUnlock();
66+
}
67+
}
68+
5769
public GraphViewImpl createView(boolean nodes, boolean edges) {
5870
graphStore.autoWriteLock();
5971
try {
@@ -232,58 +244,38 @@ public void destroyGraphObserver(GraphObserverImpl graphObserver) {
232244
graphViewImpl.destroyGraphObserver(graphObserver);
233245
}
234246

235-
protected void addNode(NodeImpl node) {
236-
if (views.length > 0) {
237-
for (GraphViewImpl view : views) {
238-
if (view != null) {
239-
view.ensureNodeVectorSize(node);
240-
}
241-
}
242-
}
243-
}
244-
245247
protected void removeNode(NodeImpl node) {
246-
if (views.length > 0) {
247-
for (GraphViewImpl view : views) {
248-
if (view != null) {
249-
view.removeNode(node);
250-
}
248+
for (GraphViewImpl view : views) {
249+
if (view != null) {
250+
view.removeNode(node);
251251
}
252252
}
253253
}
254254

255255
protected void addEdge(EdgeImpl edge) {
256-
if (views.length > 0) {
257-
for (GraphViewImpl view : views) {
258-
if (view != null) {
259-
view.ensureEdgeVectorSize(edge);
260-
261-
if (view.nodeView && !view.edgeView) {
262-
view.addEdgeInNodeView(edge);
263-
}
256+
for (GraphViewImpl view : views) {
257+
if (view != null) {
258+
if (view.nodeView && !view.edgeView) {
259+
view.addEdgeInNodeView(edge);
264260
}
265261
}
266262
}
267263
}
268264

269265
protected void setEdgeType(EdgeImpl edge, int oldType, boolean wasMutual) {
270-
if (views.length > 0) {
271-
for (GraphViewImpl view : views) {
272-
if (view != null) {
273-
if ((view.nodeView && !view.edgeView) || (view.edgeView && view.containsEdge(edge))) {
274-
view.setEdgeType(edge, oldType, wasMutual);
275-
}
266+
for (GraphViewImpl view : views) {
267+
if (view != null) {
268+
if ((view.nodeView && !view.edgeView) || (view.edgeView && view.containsEdge(edge))) {
269+
view.setEdgeType(edge, oldType, wasMutual);
276270
}
277271
}
278272
}
279273
}
280274

281275
protected void removeEdge(EdgeImpl edge) {
282-
if (views.length > 0) {
283-
for (GraphViewImpl view : views) {
284-
if (view != null) {
285-
view.removeEdge(edge);
286-
}
276+
for (GraphViewImpl view : views) {
277+
if (view != null) {
278+
view.removeEdge(edge);
287279
}
288280
}
289281
}

src/main/java/org/gephi/graph/impl/NodeStore.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,6 @@ public boolean add(final Node n) {
305305
currentBlock.add(node);
306306
dictionary.put(node.getId(), node.storeId);
307307
}
308-
if (viewStore != null) {
309-
viewStore.addNode(node);
310-
}
311308
node.indexAttributes();
312309

313310
if (spatialIndex != null) {

0 commit comments

Comments
 (0)