Enumerator now extends AutoCloseable. This will improve resource management when an Enumerable or Enumerator wraps a resource such as a file or database connection.
diff --git a/src/main/java/net/hydromatic/lambda/streams/MapStream.java b/src/main/java/net/hydromatic/lambda/streams/MapStream.java
index 690c8e3..867baa1 100644
--- a/src/main/java/net/hydromatic/lambda/streams/MapStream.java
+++ b/src/main/java/net/hydromatic/lambda/streams/MapStream.java
@@ -146,6 +146,10 @@
public void reset() {
enumerator.reset();
}
+
+ public void close() {
+ enumerator.close();
+ }
};
return new Iterable<BiValue<K, V>>() {
public Iterator<BiValue<K, V>> iterator() {
diff --git a/src/main/java/net/hydromatic/linq4j/CartesianProductEnumerator.java b/src/main/java/net/hydromatic/linq4j/CartesianProductEnumerator.java
index 74056fd..3734b5d 100644
--- a/src/main/java/net/hydromatic/linq4j/CartesianProductEnumerator.java
+++ b/src/main/java/net/hydromatic/linq4j/CartesianProductEnumerator.java
@@ -80,6 +80,26 @@
public void reset() {
first = true;
}
+
+ public void close() {
+ // If there is one or more exceptions, carry on and close all enumerators,
+ // then throw the first.
+ Throwable rte = null;
+ for (Enumerator<T> enumerator : enumerators) {
+ try {
+ enumerator.close();
+ } catch (Throwable e) {
+ rte = e;
+ }
+ }
+ if (rte != null) {
+ if (rte instanceof Error) {
+ throw (Error) rte;
+ } else {
+ throw (RuntimeException) rte;
+ }
+ }
+ }
}
// End CartesianProductEnumerator.java
diff --git a/src/main/java/net/hydromatic/linq4j/EnumerableDefaults.java b/src/main/java/net/hydromatic/linq4j/EnumerableDefaults.java
index 976d99d..41bf134 100644
--- a/src/main/java/net/hydromatic/linq4j/EnumerableDefaults.java
+++ b/src/main/java/net/hydromatic/linq4j/EnumerableDefaults.java
@@ -25,7 +25,7 @@
import static net.hydromatic.linq4j.function.Functions.adapt;
/**
- * @author jhyde
+ * Default implementations of methods in the {@link Enumerable} interface.
*/
public abstract class EnumerableDefaults {
@@ -648,6 +648,9 @@
public void reset() {
entries.reset();
}
+
+ public void close() {
+ }
};
}
};
@@ -785,6 +788,7 @@
|| !outerEnumerable.any()) {
productEnumerator = Linq4j.emptyEnumerator();
} else {
+ //noinspection unchecked
productEnumerator = Linq4j.product(
Arrays.asList(
(Enumerator<Object>) (Enumerator)
@@ -798,6 +802,9 @@
public void reset() {
entries.reset();
}
+
+ public void close() {
+ }
};
}
};
@@ -1227,6 +1234,10 @@
public void reset() {
enumerator.reset();
}
+
+ public void close() {
+ enumerator.close();
+ }
};
}
};
@@ -1261,6 +1272,10 @@
public void reset() {
enumerator.reset();
}
+
+ public void close() {
+ enumerator.close();
+ }
};
}
};
@@ -1301,6 +1316,11 @@
sourceEnumerator.reset();
resultEnumerator = Linq4j.emptyEnumerator();
}
+
+ public void close() {
+ sourceEnumerator.close();
+ resultEnumerator.close();
+ }
};
}
};
@@ -1853,6 +1873,10 @@
public void reset() {
enumerator.reset();
}
+
+ public void close() {
+ enumerator.close();
+ }
};
}
};
@@ -1890,6 +1914,10 @@
enumerator.reset();
n = -1;
}
+
+ public void close() {
+ enumerator.close();
+ }
};
}
};
@@ -1958,6 +1986,10 @@
done = false;
n = -1;
}
+
+ public void close() {
+ enumerator.close();
+ }
}
static class SkipWhileEnumerator<TSource> implements Enumerator<TSource> {
@@ -1997,6 +2029,10 @@
started = false;
n = -1;
}
+
+ public void close() {
+ enumerator.close();
+ }
}
static class CastingEnumerator<T> implements Enumerator<T> {
@@ -2019,6 +2055,10 @@
public void reset() {
enumerator.reset();
}
+
+ public void close() {
+ enumerator.close();
+ }
}
private static class Wrapped<T> {
diff --git a/src/main/java/net/hydromatic/linq4j/Enumerator.java b/src/main/java/net/hydromatic/linq4j/Enumerator.java
index f8b82c1..c9b8ce5 100644
--- a/src/main/java/net/hydromatic/linq4j/Enumerator.java
+++ b/src/main/java/net/hydromatic/linq4j/Enumerator.java
@@ -27,7 +27,7 @@
*
* @param <T> element type
*/
-public interface Enumerator<T> {
+public interface Enumerator<T> extends AutoCloseable {
/**
* Gets the current element in the collection.
*
@@ -117,6 +117,14 @@
* {@link #current()}.</p>
*/
void reset();
+
+ /**
+ * Closes this enumerable and releases resources.
+ *
+ * <p>This method is idempotent. Calling it multiple times has the same effect
+ * as calling it once.</p>
+ */
+ void close();
}
// End Enumerator.java
diff --git a/src/main/java/net/hydromatic/linq4j/Linq4j.java b/src/main/java/net/hydromatic/linq4j/Linq4j.java
index efc4de4..965d5e8 100644
--- a/src/main/java/net/hydromatic/linq4j/Linq4j.java
+++ b/src/main/java/net/hydromatic/linq4j/Linq4j.java
@@ -47,6 +47,9 @@
public void reset() {
}
+
+ public void close() {
+ }
};
public static final Enumerable<?> EMPTY_ENUMERABLE =
@@ -372,6 +375,20 @@
iterator = iterable.iterator();
current = (T) DUMMY;
}
+
+ public void close() {
+ final Iterator<T> iterator = this.iterator;
+ this.iterator = null;
+ if (iterator instanceof AutoCloseable) {
+ try {
+ ((AutoCloseable) iterator).close();
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
}
static class CompositeEnumerable<E> extends AbstractEnumerable<E> {
@@ -383,6 +400,7 @@
public Enumerator<E> enumerator() {
return new Enumerator<E>() {
+ // Never null.
Enumerator<E> current = emptyEnumerator();
public E current() {
@@ -394,7 +412,9 @@
if (current.moveNext()) {
return true;
}
+ current.close();
if (!enumerableEnumerator.moveNext()) {
+ current = emptyEnumerator();
return false;
}
current = enumerableEnumerator.current().enumerator();
@@ -405,6 +425,11 @@
enumerableEnumerator.reset();
current = emptyEnumerator();
}
+
+ public void close() {
+ current.close();
+ current = emptyEnumerator();
+ }
};
}
}
diff --git a/src/main/java/net/hydromatic/linq4j/LookupImpl.java b/src/main/java/net/hydromatic/linq4j/LookupImpl.java
index f83b054..9be21d6 100644
--- a/src/main/java/net/hydromatic/linq4j/LookupImpl.java
+++ b/src/main/java/net/hydromatic/linq4j/LookupImpl.java
@@ -55,6 +55,10 @@
public void reset() {
enumerator.reset();
}
+
+ public void close() {
+ enumerator.close();
+ }
};
}
@@ -182,6 +186,10 @@
public void reset() {
groupingEnumerator.reset();
}
+
+ public void close() {
+ groupingEnumerator.close();
+ }
};
}
};
@@ -208,7 +216,9 @@
if (enumerator.moveNext()) {
return true;
}
+ enumerator.close();
if (!listEnumerator.moveNext()) {
+ enumerator = Linq4j.emptyEnumerator();
return false;
}
enumerator = listEnumerator.current().enumerator();
@@ -219,6 +229,10 @@
listEnumerator.reset();
enumerator = Linq4j.emptyEnumerator();
}
+
+ public void close() {
+ enumerator.close();
+ }
};
}
};