Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/main/java/org/gephi/graph/api/ColumnIndex.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,28 @@ public interface ColumnIndex<K, T extends Element> extends Iterable<Map.Entry<K,

/**
* Gets an Iterable of all elements in the column index with <em>value</em>.
* <p>
* The index lock, or the graph read lock if the column isn't indexed, is held from {@code iterator()} until the
* iterator is exhausted or {@link ElementIterable#doBreak()} is called. To stop early, or when the loop body
* throws, call <code>doBreak()</code>, which also releases the nested iterations over the same result. A result is
* iterated and broken from a single thread and isn't shared between threads. {@code stream()} iterates a copy and
* holds no lock, parallel streams aren't supported.
* <p>
* While the lock is held, other threads wait for it: for an indexed column, the threads that set its value or add
* or remove elements of its table, otherwise the threads that write to the graph. A loop body that calls the graph,
* model or index APIs (reading or writing the graph, setting attributes, getting a view index) can deadlock with
* them. In that case, copy the result with {@link ElementIterable#toArray()}, which releases the lock before
* returning, and iterate the array. The loop body must not set the values of the column or add or remove elements.
* <p>
* A value of the wrong type for an indexed column may throw a <code>ClassCastException</code> when iterating, not
* in this method.
*
* @param value the value
* @return an iterable with element with <em>value</em>
* @return an iterable with element with <em>value</em>, empty if value not found
* @throws IllegalArgumentException if this is the edge type column index and <em>value</em> isn't an edge type
* label
*/
Iterable<T> get(K value);
ElementIterable<T> get(K value);

/**
* Returns all unique values.
Expand Down
21 changes: 19 additions & 2 deletions src/main/java/org/gephi/graph/api/Index.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,29 @@ public interface Index<T extends Element> {

/**
* Gets an Iterable of all elements in the index with <em>value</em> in the given <em>column</em>.
* <p>
* The index lock, or the graph read lock if the column isn't indexed, is held from {@code iterator()} until the
* iterator is exhausted or {@link ElementIterable#doBreak()} is called. To stop early, or when the loop body
* throws, call <code>doBreak()</code>, which also releases the nested iterations over the same result. A result is
* iterated and broken from a single thread and isn't shared between threads. {@code stream()} iterates a copy and
* holds no lock, parallel streams aren't supported.
* <p>
* While the lock is held, other threads wait for it: for an indexed column, the threads that set its value or add
* or remove elements of its table, otherwise the threads that write to the graph. A loop body that calls the graph,
* model or index APIs (reading or writing the graph, setting attributes, getting a view index) can deadlock with
* them. In that case, copy the result with {@link ElementIterable#toArray()}, which releases the lock before
* returning, and iterate the array. The loop body must not set the values of the column or add or remove elements.
* <p>
* A value of the wrong type for an indexed column may throw a <code>ClassCastException</code> when iterating, not
* in this method.
*
* @param column the column to get values
* @param value the value
* @return an iterable with element with <em>value</em> in <em>column</em>, or null if value not found
* @return an iterable with element with <em>value</em> in <em>column</em>, empty if value not found
* @throws IllegalArgumentException if <em>column</em> is the edge type column and <em>value</em> isn't an edge type
* label
*/
public Iterable<T> get(Column column, Object value);
public ElementIterable<T> get(Column column, Object value);

/**
* Returns all unique values in the given column.
Expand Down
119 changes: 47 additions & 72 deletions src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.gephi.graph.api.Column;
import org.gephi.graph.api.Edge;
import org.gephi.graph.api.Element;
import org.gephi.graph.api.ElementIterable;
import org.gephi.graph.api.Estimator;
import org.gephi.graph.api.Graph;
import org.gephi.graph.api.GraphLock;
Expand All @@ -50,22 +51,25 @@ protected ColumnNoIndexImpl(ColumnImpl column, Graph graph, Class<T> elementClas
this.graphLock = graph != null ? graph.getLock() : null;
}

private Iterator<T> getElementIterator() {
// The iterator of the elements holds a graph read lock until it is exhausted
private ElementIterable<T> getElements() {
if (elementClass.equals(Node.class)) {
return (Iterator<T>) graph.getNodes().iterator();
return (ElementIterable<T>) graph.getNodes();
} else if (elementClass.equals(Edge.class)) {
return (Iterator<T>) graph.getEdges().iterator();
return (ElementIterable<T>) graph.getEdges();
}
return null;
return ElementIterable.EMPTY;
}

@Override
public int count(K value) {
lock();
try {
Iterator<T> elementIterator = getElementIterator();
ElementIterable<T> elements = getElements();
Iterator<T> elementIterator = elements.iterator();
int count = 0;
if (elementIterator != null) {
boolean exhausted = false;
try {
while (elementIterator.hasNext()) {
ElementImpl element = (ElementImpl) elementIterator.next();
K obj = (K) element.getAttribute(column, graph.getView());
Expand All @@ -75,6 +79,11 @@ public int count(K value) {
count++;
}
}
exhausted = true;
} finally {
if (!exhausted) {
elements.doBreak();
}
}
return count;
} finally {
Expand All @@ -83,22 +92,32 @@ public int count(K value) {
}

@Override
public Iterable<T> get(K value) {
return new ElementWithValueIterable(getElementIterator(), value);
public ElementIterable<T> get(K value) {
return new FilteredElementIterable<>(getElements(), element -> {
Object val = element.getAttribute(column, graph.getView());
return val == null ? value == null : val.equals(value);
}, elementClass);
}

@Override
public Collection<K> values() {
lock();
try {
Iterator<T> elementIterator = getElementIterator();
ElementIterable<T> elements = getElements();
Iterator<T> elementIterator = elements.iterator();
Set<K> set = new ObjectOpenHashSet<>();
if (elementIterator != null) {
boolean exhausted = false;
try {
while (elementIterator.hasNext()) {
ElementImpl element = (ElementImpl) elementIterator.next();
K obj = (K) element.getAttribute(column, graph.getView());
set.add(obj);
}
exhausted = true;
} finally {
if (!exhausted) {
elements.doBreak();
}
}
return set;
} finally {
Expand Down Expand Up @@ -134,8 +153,10 @@ public Number getMinValue() {
lock();
try {
Number min = null;
Iterator<T> elementIterator = getElementIterator();
if (elementIterator != null) {
ElementIterable<T> elements = getElements();
Iterator<T> elementIterator = elements.iterator();
boolean exhausted = false;
try {
double minN = Double.POSITIVE_INFINITY;
while (elementIterator.hasNext()) {
ElementImpl element = (ElementImpl) elementIterator.next();
Expand All @@ -147,6 +168,11 @@ public Number getMinValue() {
min = num;
}
}
exhausted = true;
} finally {
if (!exhausted) {
elements.doBreak();
}
}
return min;
} finally {
Expand All @@ -162,10 +188,11 @@ public Number getMaxValue() {
lock();
try {
Number max = null;
Iterator<T> elementIterator = getElementIterator();
if (elementIterator != null) {
ElementIterable<T> elements = getElements();
Iterator<T> elementIterator = elements.iterator();
boolean exhausted = false;
try {
double maxN = Double.NEGATIVE_INFINITY;

while (elementIterator.hasNext()) {
ElementImpl element = (ElementImpl) elementIterator.next();
Number num = (Number) element.getAttribute(column, graph.getView(), Estimator.MAX);
Expand All @@ -176,6 +203,11 @@ public Number getMaxValue() {
max = num;
}
}
exhausted = true;
} finally {
if (!exhausted) {
elements.doBreak();
}
}
return max;
} finally {
Expand Down Expand Up @@ -229,63 +261,6 @@ public void removeValue(T element, K value) {
version.incrementAndGet();
}

private class ElementWithValueIterable implements Iterable<T> {

private final Iterator<T> ite;
private final K value;

public ElementWithValueIterable(Iterator<T> ite, K value) {
this.ite = ite;
this.value = value;
}

@Override
public Iterator<T> iterator() {
return new ElementWithValueIterator(ite, value);
}
}

private class ElementWithValueIterator implements Iterator<T> {

private final Iterator<T> itr;
private final K value;
private T pointer;

public ElementWithValueIterator(Iterator<T> itr, K value) {
this.itr = itr;
this.value = value;
lock();
}

@Override
public boolean hasNext() {
while (pointer == null && itr.hasNext()) {
T element = itr.next();
K val = (K) element.getAttribute(column, graph.getView());
if ((value == null && val == null) || (val != null && val.equals(value))) {
pointer = element;
}
}
if (pointer != null) {
return true;
}
unlock();
return false;
}

@Override
public T next() {
T res = pointer;
pointer = null;
return res;
}

@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}

private void lock() {
if (graphLock != null) {
graphLock.readLock();
Expand Down
Loading
Loading