This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new b6965b302f Refactor and move `SetOfUnknownSize` and
`ListOfUnknownSize`. This is not that much because they are useful to users
(while they may be), but rather for reducing the number of classes that are
closely related but still spread in many packages. Those two classes have been
stable for a reasonable amount of time and their API is relatively well defined.
b6965b302f is described below
commit b6965b302f315a6020c9f953c81907f142c3a53e
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Nov 1 12:34:28 2025 +0100
Refactor and move `SetOfUnknownSize` and `ListOfUnknownSize`.
This is not that much because they are useful to users (while they may be),
but rather for reducing the number of classes that are closely related but
still spread in many packages.
Those two classes have been stable for a reasonable amount of time and
their API is relatively well defined.
---
.../sis/feature/builder/AttributeTypeBuilder.java | 4 +-
.../main/org/apache/sis/filter/Capabilities.java | 6 +
.../apache/sis/xml/bind/IdentifierMapAdapter.java | 8 +-
.../apache/sis/referencing/AuthorityFactories.java | 9 +
.../factory/IdentifiedObjectFinder.java | 6 +
.../factory/MultiAuthoritiesFactory.java | 17 +-
.../sis/referencing/internal/shared/LazySet.java | 2 +-
.../operation/transform/OperationMethodSet.java | 18 +-
.../apache/sis/storage/geotiff/GeoTiffStore.java | 19 +-
.../apache/sis/storage/image/WorldFileStore.java | 17 +-
.../apache/sis/storage/image/WritableStore.java | 2 +-
.../org/apache/sis/util/collection/DerivedSet.java | 10 +-
.../shared => collection}/ListOfUnknownSize.java | 152 ++++++++----
.../sis/util/collection/SetOfUnknownSize.java | 267 +++++++++++++++++++++
.../sis/util/internal/shared/AbstractMap.java | 13 +
.../sis/util/internal/shared/CheckedHashSet.java | 2 +-
.../sis/util/internal/shared/SetOfUnknownSize.java | 199 ---------------
.../ListOfUnknownSizeTest.java | 5 +-
18 files changed, 481 insertions(+), 275 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/AttributeTypeBuilder.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/AttributeTypeBuilder.java
index fabd4d95a0..d852e17452 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/AttributeTypeBuilder.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/AttributeTypeBuilder.java
@@ -23,6 +23,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Objects;
+import java.util.Spliterator;
import java.lang.reflect.Array;
import javax.measure.Unit;
import org.opengis.util.GenericName;
@@ -32,8 +33,8 @@ import org.apache.sis.feature.FeatureOperations;
import org.apache.sis.util.Classes;
import org.apache.sis.util.ObjectConverters;
import org.apache.sis.util.UnconvertibleObjectException;
+import org.apache.sis.util.collection.SetOfUnknownSize;
import org.apache.sis.util.internal.shared.CollectionsExt;
-import org.apache.sis.util.internal.shared.SetOfUnknownSize;
import org.apache.sis.util.internal.shared.AbstractIterator;
import org.apache.sis.feature.internal.Resources;
import org.apache.sis.feature.internal.shared.AttributeConvention;
@@ -563,6 +564,7 @@ public final class AttributeTypeBuilder<V> extends
PropertyTypeBuilder {
return new SetOfUnknownSize<AttributeRole>() {
@Override public Iterator<AttributeRole> iterator() {return new
RoleIter();}
@Override public boolean add(AttributeRole role) {return
addRole(role);}
+ @Override protected int characteristics() {return
Spliterator.DISTINCT | Spliterator.NONNULL;}
};
}
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Capabilities.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Capabilities.java
index fa59d42a3e..f226faa3a1 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Capabilities.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Capabilities.java
@@ -21,6 +21,7 @@ import java.util.Set;
import java.util.Collection;
import java.util.Arrays;
import java.util.Iterator;
+import java.util.Spliterator;
import java.util.ServiceLoader;
import java.util.Optional;
import org.opengis.util.LocalName;
@@ -106,6 +107,11 @@ final class Capabilities extends AbstractMap<String,
AvailableFunction>
Capabilities(final Geometries<?> geometries) {
functionNames = CharSequences.EMPTY_ARRAY;
providers = new LazySet<FunctionRegister>() {
+ /** Declares that this set excludes null. */
+ @Override protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL;
+ }
+
/** Returns the SQLMM functions to check first. */
@Override protected FunctionRegister[] initialValues() {
return new FunctionRegister[] {new Registry(geometries)};
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/IdentifierMapAdapter.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/IdentifierMapAdapter.java
index d2d73d25ab..6355f546e3 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/IdentifierMapAdapter.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/IdentifierMapAdapter.java
@@ -26,6 +26,7 @@ import java.util.Collection;
import java.util.AbstractMap;
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.Spliterator;
import java.io.Serializable;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
@@ -33,7 +34,7 @@ import org.apache.sis.xml.XLink;
import org.apache.sis.xml.IdentifierMap;
import org.apache.sis.xml.IdentifierSpace;
import org.apache.sis.util.internal.shared.Strings;
-import org.apache.sis.util.internal.shared.SetOfUnknownSize;
+import org.apache.sis.util.collection.SetOfUnknownSize;
import org.apache.sis.util.collection.Containers;
import org.apache.sis.pending.jdk.JDK19;
@@ -395,6 +396,11 @@ public class IdentifierMapAdapter extends
AbstractMap<Citation,String> implement
return IdentifierMapAdapter.this.size();
}
+ /** Declares that this set excludes null. */
+ @Override protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL;
+ }
+
/** Returns an iterator over the (<var>citation</var>,
<var>code</var>) entries. */
@Override public Iterator<Entry<Citation, String>> iterator() {
return new Iter(identifiers, isModifiable());
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
index 769433723a..d1ab0d7272 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
@@ -18,6 +18,7 @@ package org.apache.sis.referencing;
import java.util.Set;
import java.util.Iterator;
+import java.util.Spliterator;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.LogRecord;
@@ -110,6 +111,14 @@ final class AuthorityFactories<T extends AuthorityFactory>
extends LazySet<T> {
service = type;
}
+ /**
+ * Declares that this set excludes the null element.
+ */
+ @Override
+ protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL;
+ }
+
/**
* Creates the iterator which will provide the elements of this set before
filtering.
*/
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
index 2eabbcdb41..0bc929e9f7 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
@@ -20,6 +20,7 @@ import java.util.Iterator;
import java.util.Set;
import java.util.HashSet;
import java.util.Objects;
+import java.util.Spliterator;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.LogRecord;
@@ -477,6 +478,11 @@ public class IdentifiedObjectFinder {
this.object = object;
}
+ /** Declares that this set excludes null. */
+ @Override protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL;
+ }
+
/**
* Creates the iterator which will create objects from authority codes.
* This method will be invoked only when first needed and at most
once, unless {@link #reload()} is invoked.
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
index b6dd73c42d..fcdfa5282c 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
@@ -31,6 +31,8 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.IdentityHashMap;
import java.util.ConcurrentModificationException;
+import java.util.OptionalInt;
+import java.util.Spliterator;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -55,7 +57,7 @@ import org.apache.sis.util.internal.shared.Constants;
import org.apache.sis.util.internal.shared.AbstractIterator;
import org.apache.sis.util.internal.shared.DefinitionURI;
import org.apache.sis.util.internal.shared.CollectionsExt;
-import org.apache.sis.util.internal.shared.SetOfUnknownSize;
+import org.apache.sis.util.collection.SetOfUnknownSize;
import org.apache.sis.metadata.internal.shared.NameMeaning;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.IdentifiedObjects;
@@ -407,6 +409,11 @@ public class MultiAuthoritiesFactory extends
GeodeticAuthorityFactory implements
return codes;
}
+ /** Declares that this set excludes null. */
+ @Override protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL;
+ }
+
/**
* The collection size, or a negative value if we have not yet
computed the size.
* A negative value different than -1 means that we have not
counted all elements,
@@ -415,11 +422,11 @@ public class MultiAuthoritiesFactory extends
GeodeticAuthorityFactory implements
private int size = -1;
/**
- * Returns {@code true} if the {@link #size()} method is cheap.
+ * Returns the {@link #size()} value if cheap.
*/
@Override
- protected boolean isSizeKnown() {
- return size >= 0;
+ protected OptionalInt sizeIfKnown() {
+ return size >= 0 ? OptionalInt.of(size) : OptionalInt.empty();
}
/**
@@ -493,7 +500,7 @@ public class MultiAuthoritiesFactory extends
GeodeticAuthorityFactory implements
return false;
}
- /** Declared soon as unsupported operation for preventing a call
to {@link #size()}. */
+ /** Declared as unsupported operation for preventing a call to
{@link #size()}. */
@Override public boolean removeAll(Collection<?> c) {throw new
UnsupportedOperationException();}
@Override public boolean retainAll(Collection<?> c) {throw new
UnsupportedOperationException();}
@Override public boolean remove (Object o) {throw new
UnsupportedOperationException();}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/LazySet.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/LazySet.java
index bbe3bdafe7..51e338b81a 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/LazySet.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/LazySet.java
@@ -19,7 +19,7 @@ package org.apache.sis.referencing.internal.shared;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
-import org.apache.sis.util.internal.shared.SetOfUnknownSize;
+import org.apache.sis.util.collection.SetOfUnknownSize;
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/OperationMethodSet.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/OperationMethodSet.java
index e786ca5844..c60943d79b 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/OperationMethodSet.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/OperationMethodSet.java
@@ -21,11 +21,13 @@ import java.util.ArrayList;
import java.util.Set;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.OptionalInt;
import java.util.ServiceLoader;
+import java.util.Spliterator;
import org.opengis.referencing.operation.SingleOperation;
import org.opengis.referencing.operation.OperationMethod;
-import org.apache.sis.util.internal.shared.SetOfUnknownSize;
import org.apache.sis.referencing.operation.DefaultOperationMethod;
+import org.apache.sis.util.collection.SetOfUnknownSize;
/**
@@ -160,11 +162,11 @@ final class OperationMethodSet extends
SetOfUnknownSize<OperationMethod> {
}
/**
- * Returns {@code true} if the {@link #size()} method is cheap.
+ * Returns number of codes if this information is already known.
*/
@Override
- protected synchronized boolean isSizeKnown() {
- return methodIterator == null;
+ protected synchronized OptionalInt sizeIfKnown() {
+ return (methodIterator == null) ? OptionalInt.of(cachedMethods.size())
: OptionalInt.empty();
}
/**
@@ -257,4 +259,12 @@ final class OperationMethodSet extends
SetOfUnknownSize<OperationMethod> {
*/
return super.contains(object);
}
+
+ /**
+ * Declares that this set is immutable and excludes the null element.
+ */
+ @Override
+ protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL |
Spliterator.IMMUTABLE;
+ }
}
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java
index 1152138fe2..84004a13d4 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java
@@ -20,6 +20,8 @@ import java.util.Set;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.Spliterator;
import java.net.URI;
import java.io.IOException;
import java.nio.charset.Charset;
@@ -65,7 +67,7 @@ import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.SimpleInternationalString;
import org.apache.sis.util.internal.shared.Constants;
-import org.apache.sis.util.internal.shared.ListOfUnknownSize;
+import org.apache.sis.util.collection.ListOfUnknownSize;
import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.iso.DefaultNameFactory;
@@ -568,10 +570,15 @@ public class GeoTiffStore extends DataStore implements
Aggregate {
Components() {
}
- /** Returns the size or -1 if not yet known. */
- @Override protected int sizeIfKnown() {
+ /** Declares that this list has no duplicated elements and excludes
null. */
+ @Override protected int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
Spliterator.DISTINCT;
+ }
+
+ /** Returns the size or empty if not yet known. */
+ @Override protected OptionalInt sizeIfKnown() {
synchronized (GeoTiffStore.this) {
- return size;
+ return (size >= 0) ? OptionalInt.of(size) :
OptionalInt.empty();
}
}
@@ -595,8 +602,8 @@ public class GeoTiffStore extends DataStore implements
Aggregate {
}
/** Returns whether the given index is valid. */
- @Override protected boolean exists(final int index) {
- return (index >= 0) && getImageFileDirectory(index) != null;
+ @Override protected boolean isValidIndex(final int index) {
+ return (index >= 0) && (size >= 0 ? index < size :
getImageFileDirectory(index) != null);
}
/** Returns element at the given index or throw {@link
IndexOutOfBoundsException}. */
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java
index 6aebb95fd8..fd8a720a8d 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java
@@ -21,6 +21,8 @@ import java.util.Collection;
import java.util.Map;
import java.util.HashMap;
import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.Spliterator;
import java.io.Closeable;
import java.io.IOException;
import java.io.EOFException;
@@ -56,7 +58,7 @@ import org.apache.sis.storage.base.AuxiliaryContent;
import org.apache.sis.referencing.internal.shared.AffineTransform2D;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.ArraysExt;
-import org.apache.sis.util.internal.shared.ListOfUnknownSize;
+import org.apache.sis.util.collection.ListOfUnknownSize;
import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.setup.OptionKey;
@@ -624,22 +626,27 @@ loop: for (int convention=0;; convention++) {
}
/**
- * Returns the number of images if this information is known, or any
negative value otherwise.
+ * Returns the number of images if this information is known, or empty
otherwise.
* This is used by {@link ListOfUnknownSize} for optimizing some
operations.
*/
@Override
- protected int sizeIfKnown() {
+ protected OptionalInt sizeIfKnown() {
synchronized (WorldFileStore.this) {
- return size;
+ return (size >= 0) ? OptionalInt.of(size) :
OptionalInt.empty();
}
}
+ /** Declares that this list has no duplicated elements and excludes
null. */
+ @Override protected int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
Spliterator.DISTINCT;
+ }
+
/**
* Returns {@code true} if an element exists at the given index.
* Current implementations is not more efficient than {@link
#get(int)}.
*/
@Override
- protected boolean exists(final int index) {
+ protected boolean isValidIndex(final int index) {
synchronized (WorldFileStore.this) {
if (size >= 0) {
return index >= 0 && index < size;
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WritableStore.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WritableStore.java
index 593564b149..43555d3137 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WritableStore.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WritableStore.java
@@ -176,7 +176,7 @@ class WritableStore extends WorldFileStore {
final Components components = components(true, numImages);
if (components.isEmpty()) {
numImages = 0;
- } else if (components.exists(1)) {
+ } else if (components.isValidIndex(1)) {
return 2;
} else {
numImages = 1;
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/DerivedSet.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/DerivedSet.java
index 33613c9bde..802f660aee 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/DerivedSet.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/DerivedSet.java
@@ -18,12 +18,12 @@ package org.apache.sis.util.collection;
import java.util.Set;
import java.util.Iterator;
+import java.util.Spliterator;
import java.io.Serializable;
import org.apache.sis.math.FunctionProperty;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.util.resources.Errors;
-import org.apache.sis.util.internal.shared.SetOfUnknownSize;
/**
@@ -112,6 +112,14 @@ class DerivedSet<S,E> extends SetOfUnknownSize<E>
implements CheckedContainer<E>
return converter.getTargetClass();
}
+ /**
+ * Declares that this set excludes the null element.
+ */
+ @Override
+ protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL;
+ }
+
/**
* Returns an iterator over the elements contained in this set.
* The iterator will invoke the {@link ObjectConverter#apply(Object)}
method for each element.
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/ListOfUnknownSize.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/ListOfUnknownSize.java
similarity index 56%
rename from
endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/ListOfUnknownSize.java
rename to
endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/ListOfUnknownSize.java
index 61880294e7..2f33f70a7b 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/ListOfUnknownSize.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/ListOfUnknownSize.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sis.util.internal.shared;
+package org.apache.sis.util.collection;
import java.util.List;
import java.util.AbstractSequentialList;
@@ -24,83 +24,103 @@ import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.Objects;
+import java.util.OptionalInt;
+import java.util.function.IntFunction;
+import static
org.apache.sis.util.collection.SetOfUnknownSize.DEFAULT_INITIAL_SIZE;
/**
- * An alternative to {@code AbstractList} for implementations having a costly
{@link #size()} method.
- * This class overrides some methods in a way that avoid or reduce calls to
{@link #size()}.
+ * An alternative to {@code AbstractList} for implementations having a costly
{@code size()} method.
+ * The size of the set is not known in advance, but may become known after one
complete iteration.
+ * This class overrides methods in a way that reduces or eliminates the need
to invoke {@link #size()}.
*
- * <p>Despite extending {@link AbstractSequentialList}, this class expects
implementations to override
- * the random access method {@link #get(int)} instead of {@link
#listIterator(int)}.</p>
+ * <h4>Note for implementers</h4>
+ * Despite extending {@link AbstractSequentialList}, subclasses are not
expected to overwrite the
+ * {@link #listIterator(int)} method. Instead, subclasses should implement the
following methods:
+ *
+ * <ul>
+ * <li>{@link #get(int)}</li>
+ * <li>{@link #isValidIndex(int)}</li>
+ * <li>{@link #sizeIfKnown()} — optional but recommended for efficiency</li>
+ * <li>{@link #characteristics()} — if the implementation disallows null
elements.</li>
+ * </ul>
*
* @author Martin Desruisseaux (Geomatys)
+ * @version 1.6
*
* @param <E> the type of elements in the list.
+ *
+ * @since 1.6
*/
+@SuppressWarnings("EqualsAndHashcode")
public abstract class ListOfUnknownSize<E> extends AbstractSequentialList<E> {
/**
- * For subclass constructors.
+ * Creates a new list of initially unknown size.
*/
protected ListOfUnknownSize() {
}
/**
- * Returns {@link #size()} if its value is already known, or a negative
value if the size is still unknown.
- * The size may become known for example if it has been cached by the
subclass. In such case,
- * some {@code ListOfUnknownSize} methods will take a more efficient path.
+ * Returns {@code true} if this list is empty.
+ * The implementation of this method avoids to invoke {@link #size()}.
*
- * @return {@link #size()} if its value is already known, or any negative
value if it still costly to compute.
+ * @return {@code true} if this list is empty.
*/
- protected int sizeIfKnown() {
- return -1;
+ @Override
+ public boolean isEmpty() {
+ return sizeIfKnown().orElseGet(() -> isValidIndex(0) ? 1 : 0) == 0;
}
/**
- * Returns the number of elements in this list. The default implementation
counts the number of elements
- * for which {@link #exists(int)} returns {@code true}. Subclasses are
encouraged to cache this value if
- * they know that the underlying storage is immutable.
+ * Returns the number of elements in this list. The default implementation
returns the
+ * {@linkplain #sizeIfKnown() size if known}, or otherwise searches the
smallest value
+ * for which {@link #isValidIndex(int)} returns {@code false}.
*
* @return the number of elements in this list.
*/
@Override
public int size() {
- int size = sizeIfKnown();
- if (size < 0) {
- size = 0;
- while (exists(size)) {
- if (++size == Integer.MAX_VALUE) {
- break;
- }
+ OptionalInt size;
+ int i = 1;
+ do {
+ size = sizeIfKnown();
+ if (size.isPresent()) {
+ return size.getAsInt();
}
+ } while (isValidIndex(i) && (i <<= 1) >= 0);
+ // Following is inefficient, but this fallback should not happen with
Apache SIS subclasses.
+ i >>>= 1;
+ while (isValidIndex(++i)) {
+ if (i == Integer.MAX_VALUE) break;
}
- return size;
+ return i;
}
/**
- * Returns {@code true} if this list is empty.
- * This method avoids to invoke {@link #size()} unless it is cheap.
+ * Returns the {@link #size()} value if it is already known, or empty if
the size is still unknown.
+ * The default implementation always returns an empty value. Subclasses
can overwrite this method
+ * if they have already computed and cached the number of elements. When
this information is known,
+ * some {@code ListOfUnknownSize} methods will take a more efficient path.
*
- * @return {@code true} if this list is empty.
+ * @return {@link #size()} if its value is already known, or empty if this
value is still costly to compute.
*/
- @Override
- public boolean isEmpty() {
- final int size = sizeIfKnown();
- return (size == 0) || (size < 0 && !exists(0));
+ protected OptionalInt sizeIfKnown() {
+ return OptionalInt.empty();
}
/**
- * Returns {@code true} if an element exists at the given index. If an
element at index <var>i</var> exists,
- * then all elements at index 0 to <var>i</var> - 1 also exist. Those
elements do not need to be computed
- * immediately if their computation is deferred.
+ * Returns {@code true} if the given index is valid for this list. If the
index <var>i</var> is valid,
+ * then all indices from 0 to <var>i</var> - 1 inclusive are presumed also
valid. Invoking this method
+ * is usually less expansive than testing {@code index < size()} when the
size is costly to compute.
*
* @param index the index where to verify if an element exists.
* @return {@code true} if an element exists at the given index.
*/
- protected abstract boolean exists(int index);
+ protected abstract boolean isValidIndex(int index);
/**
* Returns the element at the specified index.
- * Invoking this method may trig computation of the element if their
computation is deferred.
+ * Invoking this method may trig computation of the element if these
computations are deferred.
*
* @param index position of the element to get in this list.
* @return the element at the given index.
@@ -111,12 +131,13 @@ public abstract class ListOfUnknownSize<E> extends
AbstractSequentialList<E> {
/**
* Removes elements of the given collection from this list.
- * This method avoids to invoke {@link #size()}.
+ * The implementation of this method avoids to invoke {@link #size()}.
*
* @param c the collection containing elements to remove.
* @return {@code true} if at least one element has been removed.
*/
@Override
+ @SuppressWarnings("element-type-mismatch")
public boolean removeAll(final Collection<?> c) {
// See comment in SetOfUnknownSize.removeAll(…).
boolean modified = false;
@@ -128,7 +149,7 @@ public abstract class ListOfUnknownSize<E> extends
AbstractSequentialList<E> {
/**
* Returns a list iterator over the elements in this list.
- * The default implementation invokes {@link #exists(int)} and {@link
#get(int)}.
+ * The default implementation delegates to {@link #isValidIndex(int)} and
{@link #get(int)}.
* Write operations are not supported.
*
* @param index index of first element to be returned from the list.
@@ -142,7 +163,7 @@ public abstract class ListOfUnknownSize<E> extends
AbstractSequentialList<E> {
/**
* The iterator returned by {@link #listIterator()}. Provided as a named
class instead
- * than anonymous class for more readable stack traces. This is especially
useful since
+ * of an anonymous class for more readable stack traces. This is
especially useful since
* elements may be loaded or computed when first needed, and those
operations may fail.
*/
private final class Iterator implements ListIterator<E> {
@@ -161,8 +182,7 @@ public abstract class ListOfUnknownSize<E> extends
AbstractSequentialList<E> {
/** Whether {@link #next()} can succeed. */
@Override public boolean hasNext() {
- final int size = sizeIfKnown();
- return (size >= 0) ? cursor < size : exists(cursor);
+ return isValidIndex(cursor);
}
/** Move forward by one element. */
@@ -171,6 +191,7 @@ public abstract class ListOfUnknownSize<E> extends
AbstractSequentialList<E> {
try {
element = get(cursor);
} catch (IndexOutOfBoundsException e) {
+ // TODO: simplify when we are allowed to compile for JDK15.
throw (NoSuchElementException) new
NoSuchElementException().initCause(e);
}
cursor++; // Set only on success.
@@ -199,37 +220,71 @@ public abstract class ListOfUnknownSize<E> extends
AbstractSequentialList<E> {
}
/**
- * Creates a {@code Spliterator} without knowledge of collection size.
+ * Returns characteristics of this collection as a combination of {@code
Spliterator} bits.
+ * The default implementation returns {@link Spliterator#ORDERED}.
Subclasses should add the
+ * {@link Spliterator#NONNULL} bit if they guarantee that this list does
not contain any {@code null} element.
+ *
+ * @return the characteristics of this collection.
+ *
+ * @see Spliterator#ORDERED
+ * @see Spliterator#NONNULL
+ * @see Spliterator#IMMUTABLE
+ * @see Spliterator#characteristics()
+ */
+ protected int characteristics() {
+ return Spliterator.ORDERED;
+ }
+
+ /**
+ * Creates a {@code Spliterator} which may be of unknown size.
*
* @return a {@code Spliterator} over the elements in this collection.
*/
@Override
public Spliterator<E> spliterator() {
- return sizeIfKnown() >= 0 ? super.spliterator() :
Spliterators.spliteratorUnknownSize(iterator(), Spliterator.ORDERED);
+ final int characteristics = characteristics();
+ final OptionalInt size = sizeIfKnown();
+ return size.isPresent()
+ ? Spliterators.spliterator(iterator(), size.getAsInt(),
characteristics)
+ : Spliterators.spliteratorUnknownSize(iterator(),
characteristics);
}
/**
- * Returns the elements in an array.
+ * Returns all elements in an array.
+ * The implementation of this method avoids to invoke {@link #size()}.
*
* @return an array containing all list elements.
*/
@Override
public Object[] toArray() {
- return sizeIfKnown() >= 0 ? super.toArray() :
SetOfUnknownSize.toArray(iterator(), new Object[32], true);
+ return SetOfUnknownSize.toArray(iterator(), new
Object[sizeIfKnown().orElse(DEFAULT_INITIAL_SIZE)], true);
}
/**
- * Returns the elements in the given array, or in a new array of the same
type
- * if it was necessary to allocate more space.
+ * Returns the elements in the given array, or in a new array if it was
necessary to allocate more space.
+ * If the given array is larger than necessary, the remaining array
elements are set to {@code null}.
+ * The implementation of this method avoids to invoke {@link #size()}.
*
* @param <T> the type array elements.
* @param array where to store the elements.
* @return an array containing all list elements.
*/
@Override
- @SuppressWarnings("SuspiciousToArrayCall")
public <T> T[] toArray(final T[] array) {
- return sizeIfKnown() >= 0 ? super.toArray(array) :
SetOfUnknownSize.toArray(iterator(), array, false);
+ return SetOfUnknownSize.toArray(iterator(), array, false);
+ }
+
+ /**
+ * Returns the elements in an array generated by the given function.
+ * The implementation of this method avoids to invoke {@link #size()}.
+ *
+ * @param <T> the type array elements.
+ * @param generator the function for allocating a new array.
+ * @return an array containing all list elements.
+ */
+ @Override
+ public <T> T[] toArray(final IntFunction<T[]> generator) {
+ return SetOfUnknownSize.toArray(iterator(),
generator.apply(sizeIfKnown().orElse(DEFAULT_INITIAL_SIZE)), true);
}
/**
@@ -238,6 +293,7 @@ public abstract class ListOfUnknownSize<E> extends
AbstractSequentialList<E> {
*
* @param object the object to compare with this list.
* @return {@code true} if the two list have the same content.
+ * @hidden because nothing new to said compared to general contract.
*/
@Override
public boolean equals(final Object object) {
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/SetOfUnknownSize.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/SetOfUnknownSize.java
new file mode 100644
index 0000000000..d1f47905ba
--- /dev/null
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/SetOfUnknownSize.java
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.util.collection;
+
+import java.util.Set;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Arrays;
+import java.util.OptionalInt;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.IntFunction;
+import org.apache.sis.util.ArraysExt;
+
+
+/**
+ * An alternative to {@code AbstractSet} for implementations having a costly
{@code size()} method.
+ * The size of the set is not known in advance, but may become known after one
complete iteration.
+ * This class overrides methods in a way that reduces or eliminates the need
to invoke {@link #size()}.
+ *
+ * <h4>Note for implementers</h4>
+ * Subclasses should implement the following methods:
+ *
+ * <ul>
+ * <li>{@link #iterator()}</li>
+ * <li>{@link #sizeIfKnown()} — optional but recommended for efficiency</li>
+ * <li>{@link #characteristics()} — if the implementation disallows null
elements.</li>
+ * </ul>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.6
+ *
+ * @param <E> the type of elements in the set.
+ *
+ * @since 1.6
+ */
+@SuppressWarnings("EqualsAndHashcode")
+public abstract class SetOfUnknownSize<E> extends AbstractSet<E> {
+ /**
+ * Default initial size for {@code toArray(…)} methods.
+ */
+ static final int DEFAULT_INITIAL_SIZE = 16;
+
+ /**
+ * Creates a new set of initially unknown size.
+ */
+ protected SetOfUnknownSize() {
+ }
+
+ /**
+ * Returns {@code true} if this set is empty.
+ * The implementation of this method avoids to invoke {@link #size()}.
+ *
+ * @return {@code true} if this set is empty.
+ */
+ @Override
+ public boolean isEmpty() {
+ return sizeIfKnown().orElseGet(() -> iterator().hasNext() ? 1 : 0) ==
0;
+ }
+
+ /**
+ * Returns the number of elements in this set. The default implementation
returns the
+ * {@linkplain #sizeIfKnown() size if known}, or otherwise counts the
number of elements
+ * returned by the {@linkplain #iterator() iterator}.
+ *
+ * @return the number of elements in this set.
+ */
+ @Override
+ public int size() {
+ return sizeIfKnown().orElseGet(() -> {
+ int count = 0;
+ for (final Iterator<E> it = iterator(); it.hasNext();) {
+ it.next();
+ if (++count == Integer.MAX_VALUE) {
+ break;
+ }
+ }
+ return count;
+ });
+ }
+
+ /**
+ * Returns the {@link #size()} value if it is already known, or empty if
the size is still unknown.
+ * The default implementation always returns an empty value. Subclasses
can overwrite this method
+ * if they have already computed and cached the number of elements. When
this information is known,
+ * some {@code SetOfUnknownSize} methods will take a more efficient path.
+ *
+ * @return {@link #size()} if its value is already known, or empty if this
value is still costly to compute.
+ */
+ protected OptionalInt sizeIfKnown() {
+ return OptionalInt.empty();
+ }
+
+ /**
+ * Removes elements of the given collection from this set.
+ * The implementation of this method avoids to invoke {@link #size()}.
+ *
+ * @param c the collection containing elements to remove.
+ * @return {@code true} if at least one element has been removed.
+ */
+ @Override
+ @SuppressWarnings("element-type-mismatch")
+ public boolean removeAll(final Collection<?> c) {
+ /*
+ * Do not invoke `super.removeAll(c)` even if `sizeIfKnown()` is
present because we want to unconditionally
+ * iterate over the elements of the given collection. The reason is
that this Set may compute the values in
+ * a dynamic way and it is sometimes difficult to ensure that the
values returned by this Set's iterator are
+ * fully consistent with the values recognized by contains(Object) and
remove(Object) methods. Furthermore,
+ * we want the operation to fail fast in the common case where the
remove(Object) method is unsupported.
+ */
+ boolean modified = false;
+ for (final Iterator<?> it = c.iterator(); it.hasNext();) {
+ modified |= remove(it.next());
+ }
+ return modified;
+ }
+
+ /**
+ * Returns characteristics of this collection as a combination of {@code
Spliterator} bits.
+ * The default implementation returns {@link Spliterator#DISTINCT}.
Subclasses should add the
+ * {@link Spliterator#NONNULL} bit if they guarantee that this set does
not contain the {@code null} element.
+ *
+ * @return the characteristics of this collection.
+ *
+ * @see Spliterator#DISTINCT
+ * @see Spliterator#NONNULL
+ * @see Spliterator#IMMUTABLE
+ * @see Spliterator#characteristics()
+ */
+ protected int characteristics() {
+ return Spliterator.DISTINCT;
+ }
+
+ /**
+ * Creates a {@code Spliterator} which may be of unknown size.
+ *
+ * @return a {@code Spliterator} over the elements in this collection.
+ */
+ @Override
+ public Spliterator<E> spliterator() {
+ final int characteristics = characteristics();
+ final OptionalInt size = sizeIfKnown();
+ return size.isPresent()
+ ? Spliterators.spliterator(iterator(), size.getAsInt(),
characteristics)
+ : Spliterators.spliteratorUnknownSize(iterator(),
characteristics);
+ }
+
+ /**
+ * Returns all elements in an array.
+ * The implementation of this method avoids to invoke {@link #size()}.
+ *
+ * @return an array containing all set elements.
+ */
+ @Override
+ public Object[] toArray() {
+ return toArray(iterator(), new
Object[sizeIfKnown().orElse(DEFAULT_INITIAL_SIZE)], true);
+ }
+
+ /**
+ * Returns the elements in the given array, or in a new array if it was
necessary to allocate more space.
+ * If the given array is larger than necessary, the remaining array
elements are set to {@code null}.
+ * The implementation of this method avoids to invoke {@link #size()}.
+ *
+ * @param <T> the type array elements.
+ * @param array where to store the elements.
+ * @return an array containing all set elements.
+ */
+ @Override
+ public <T> T[] toArray(final T[] array) {
+ return toArray(iterator(), array, false);
+ }
+
+ /**
+ * Returns the elements in an array generated by the given function.
+ * The implementation of this method avoids to invoke {@link #size()}.
+ *
+ * @param <T> the type array elements.
+ * @param generator the function for allocating a new array.
+ * @return an array containing all set elements.
+ */
+ @Override
+ public <T> T[] toArray(final IntFunction<T[]> generator) {
+ return toArray(iterator(),
generator.apply(sizeIfKnown().orElse(DEFAULT_INITIAL_SIZE)), true);
+ }
+
+ /**
+ * Implementation of the public {@code toArray()} methods without call to
{@link #size()}.
+ *
+ * @param <E> type of elements in the collection.
+ * @param it iterator providing the elements.
+ * @param array where to store the elements.
+ * @param trimToSize whether to trim the array to the actual number of
elements.
+ * @return the elements.
+ */
+ @SuppressWarnings("unchecked")
+ static <E> E[] toArray(final Iterator<?> it, E[] array, boolean
trimToSize) {
+ int i = 0;
+ while (it.hasNext()) {
+ if (i >= array.length) {
+ int length = array.length << 1;
+ if (length < 0) { // Overflow.
+ length = Integer.MAX_VALUE;
+ if (length == array.length) break; // Stop after we
reached the maximal capacity of an array.
+ }
+ array = Arrays.copyOf(array, Math.max(DEFAULT_INITIAL_SIZE,
length));
+ trimToSize = true;
+ }
+ array[i++] = (E) it.next(); // Will throw an
ArrayStoreException if the type is incorrect.
+ }
+ if (trimToSize) {
+ array = ArraysExt.resize(array, i);
+ } else {
+ Arrays.fill(array, i, array.length, null);
+ }
+ return array;
+ }
+
+ /**
+ * Returns {@code true} if the given object is also a set and the two sets
have the same content.
+ * The implementation of this method avoids to invoke {@link #size()} on
this instance.
+ * However, it still call {@code other.size()}.
+ *
+ * @param other the object to compare with this set.
+ * @return {@code true} if the two set have the same content.
+ */
+ @Override
+ public boolean equals(final Object other) {
+ /*
+ * Do not invoke `super.equals(object)` even if `sizeIfKnown()` is
present because we want to unconditionally
+ * iterate over the elements of this Set. The reason is that this Set
may compute the values dynamically and
+ * it is sometimes difficult to ensure that this Set's iterator is
fully consistent with the values recognized
+ * by the contains(Object) method. For example, the iterator may
return "EPSG:4326" while the contains(Object)
+ * method may accept both "EPSG:4326" and "EPSG:4326". For this
equal(Object) method, we consider the
+ * contains(Object) method of the other Set as more reliable.
+ */
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof Set<?>)) {
+ return false;
+ }
+ final var that = (Set<?>) other;
+ int size = 0;
+ for (final Iterator<E> it = iterator(); it.hasNext();) {
+ if (!that.contains(it.next())) {
+ return false;
+ }
+ size++;
+ }
+ return size == that.size();
+ }
+}
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractMap.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractMap.java
index b427821499..bf82e5bbbf 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractMap.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractMap.java
@@ -22,8 +22,10 @@ import java.util.Objects;
import java.util.Iterator;
import java.util.Collection;
import java.util.Collections;
+import java.util.Spliterator;
import java.util.NoSuchElementException;
import org.apache.sis.io.TableAppender;
+import org.apache.sis.util.collection.SetOfUnknownSize;
import org.apache.sis.util.resources.Errors;
@@ -418,6 +420,7 @@ public abstract class AbstractMap<K,V> implements Map<K,V> {
* @return a view of the keys in this map.
*/
@Override
+ @SuppressWarnings("element-type-mismatch")
public Set<K> keySet() {
return new SetOfUnknownSize<K>() {
@Override public void clear() {
AbstractMap.this.clear();}
@@ -431,6 +434,11 @@ public abstract class AbstractMap<K,V> implements Map<K,V>
{
return (it != null) ? new Keys<>(it) :
Collections.emptyIterator();
}
+ /** Declares that this set excludes null. */
+ @Override protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL;
+ }
+
/** Overridden for the same reason as {@link
AbstractMap#equals(Object). */
@Override public boolean equals(final Object object) {
if (object == this) {
@@ -495,6 +503,11 @@ public abstract class AbstractMap<K,V> implements Map<K,V>
{
@Override public boolean isEmpty() {return
AbstractMap.this.isEmpty();}
@Override public int size() {return
AbstractMap.this.size();}
+ /** Declares that this set excludes null. */
+ @Override protected int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.NONNULL;
+ }
+
/** Returns {@code true} if the map contains the given (key,
value) pair. */
@Override public boolean contains(final Object e) {
if (e instanceof Entry<?,?>) {
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/CheckedHashSet.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/CheckedHashSet.java
index 37b6f04aa2..219045667a 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/CheckedHashSet.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/CheckedHashSet.java
@@ -110,6 +110,6 @@ public final class CheckedHashSet<E> extends
LinkedHashSet<E> implements Checked
}
/*
- * No need to override 'addAll', since it is implemented on top of 'add'.
+ * No need to override `addAll(…)` since it is implemented on top of
`add(…)`.
*/
}
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/SetOfUnknownSize.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/SetOfUnknownSize.java
deleted file mode 100644
index 9b6f876acf..0000000000
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/SetOfUnknownSize.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.sis.util.internal.shared;
-
-import java.util.Set;
-import java.util.AbstractSet;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Arrays;
-import java.util.Spliterator;
-import java.util.Spliterators;
-import org.apache.sis.util.ArraysExt;
-
-
-/**
- * An alternative to {@code AbstractSet} for implementations having a costly
{@link #size()} method.
- * This class overrides some methods in a way that avoid or reduce calls to
{@link #size()}.
- *
- * @author Martin Desruisseaux (Geomatys)
- *
- * @param <E> the type of elements in the set.
- */
-public abstract class SetOfUnknownSize<E> extends AbstractSet<E> {
- /**
- * For subclass constructors.
- */
- protected SetOfUnknownSize() {
- }
-
- /**
- * Returns {@code true} if the {@link #size()} method is cheap. This is
sometimes the case
- * when {@code size()} has already been invoked and the subclasses cached
the result.
- *
- * @return {@code true} if the {@link #size()} method is cheap.
- */
- protected boolean isSizeKnown() {
- return false;
- }
-
- /**
- * Returns {@code true} if this set is empty.
- * This method avoids to invoke {@link #size()} unless it is cheap.
- *
- * @return {@code true} if this set is empty.
- */
- @Override
- public boolean isEmpty() {
- return isSizeKnown() ? super.isEmpty() : !iterator().hasNext();
- }
-
- /**
- * Returns the number of elements in this set. The default implementation
counts the number of elements
- * returned by the {@linkplain #iterator() iterator}. Subclasses are
encouraged to cache this value
- * if they know that the underlying storage is immutable.
- *
- * @return the number of elements in this set.
- */
- @Override
- public int size() {
- int count = 0;
- for (final Iterator<E> it=iterator(); it.hasNext();) {
- it.next();
- if (++count == Integer.MAX_VALUE) {
- break;
- }
- }
- return count;
- }
-
- /**
- * Removes elements of the given collection from this set.
- * This method avoids to invoke {@link #size()}.
- *
- * @param c the collection containing elements to remove.
- * @return {@code true} if at least one element has been removed.
- */
- @Override
- public boolean removeAll(final Collection<?> c) {
- /*
- * Do not invoke super.removeAll(c) even if isSizeKnown() returns
`true` because we want to unconditionally
- * iterate over the elements of the given collection. The reason is
that this Set may compute the values in
- * a dynamic way and it is sometimes difficult to ensure that the
values returned by this Set's iterator are
- * fully consistent with the values recognized by contains(Object) and
remove(Object) methods. Furthermore,
- * we want the operation to fail fast in the common case where the
remove(Object) method is unsupported.
- */
- boolean modified = false;
- for (final Iterator<?> it = c.iterator(); it.hasNext();) {
- modified |= remove(it.next());
- }
- return modified;
- }
-
- /**
- * Creates a {@code Spliterator} without knowledge of collection size.
- *
- * @return a {@code Spliterator} over the elements in this collection.
- */
- @Override
- public Spliterator<E> spliterator() {
- return isSizeKnown() ? super.spliterator() :
Spliterators.spliteratorUnknownSize(iterator(), 0);
- }
-
- /**
- * Returns the elements in an array.
- *
- * @return an array containing all set elements.
- */
- @Override
- public Object[] toArray() {
- return isSizeKnown() ? super.toArray() : toArray(iterator(), new
Object[32], true);
- }
-
- /**
- * Returns the elements in the given array, or in a new array of the same
type
- * if it was necessary to allocate more space.
- *
- * @param <T> the type array elements.
- * @param array where to store the elements.
- * @return an array containing all set elements.
- */
- @Override
- @SuppressWarnings("SuspiciousToArrayCall")
- public <T> T[] toArray(final T[] array) {
- return isSizeKnown() ? super.toArray(array) : toArray(iterator(),
array, false);
- }
-
- /**
- * Implementation of the public {@code toArray()} methods without call to
{@link #size()}.
- */
- @SuppressWarnings("unchecked")
- static <T> T[] toArray(final Iterator<?> it, T[] array, boolean
trimToSize) {
- int i = 0;
- while (it.hasNext()) {
- if (i >= array.length) {
- if (i >= Integer.MAX_VALUE >>> 1) {
- throw new OutOfMemoryError("Required array size too
large");
- }
- array = Arrays.copyOf(array, Math.max(16, array.length) << 1);
- trimToSize = true;
- }
- array[i++] = (T) it.next(); // Will throw an
ArrayStoreException if the type is incorrect.
- }
- if (trimToSize) {
- array = ArraysExt.resize(array, i);
- } else {
- Arrays.fill(array, i, array.length, null);
- }
- return array;
- }
-
- /**
- * Returns {@code true} if the given object is also a set and the two sets
have the same content.
- * This method avoids to invoke {@link #size()} on this instance (but it
still call that method
- * on the other instance).
- *
- * @param object the object to compare with this set.
- * @return {@code true} if the two set have the same content.
- */
- @Override
- public boolean equals(final Object object) {
- /*
- * Do not invoke super.equals(object) even if isSizeKnown() returns
`true` because we want to unconditionally
- * iterate over the elements of this Set. The reason is that this Set
may compute the values dynamically and
- * it is sometimes difficult to ensure that this Set's iterator is
fully consistent with the values recognized
- * by the contains(Object) method. For example, the iterator may
return "EPSG:4326" while the contains(Object)
- * method may accept both "EPSG:4326" and "EPSG:4326". For this
equal(Object) method, we consider the
- * contains(Object) method of the other Set as more reliable.
- */
- if (object == this) {
- return true;
- }
- if (!(object instanceof Set<?>)) {
- return false;
- }
- final var that = (Set<?>) object;
- int size = 0;
- for (final Iterator<E> it = iterator(); it.hasNext();) {
- if (!that.contains(it.next())) {
- return false;
- }
- size++;
- }
- return size == that.size();
- }
-}
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/internal/shared/ListOfUnknownSizeTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/ListOfUnknownSizeTest.java
similarity index 93%
rename from
endorsed/src/org.apache.sis.util/test/org/apache/sis/util/internal/shared/ListOfUnknownSizeTest.java
rename to
endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/ListOfUnknownSizeTest.java
index 54fb38c90a..92303fb5d4 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/internal/shared/ListOfUnknownSizeTest.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/ListOfUnknownSizeTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sis.util.internal.shared;
+package org.apache.sis.util.collection;
import java.util.ListIterator;
@@ -29,6 +29,7 @@ import org.apache.sis.test.TestCase;
*
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class ListOfUnknownSizeTest extends TestCase {
/**
* Creates a new test case.
@@ -44,7 +45,7 @@ public final class ListOfUnknownSizeTest extends TestCase {
final Integer[] data = new Integer[] {4, 7, 1, 3, 0, -4, -3};
final ListIterator<Integer> it = new ListOfUnknownSize<Integer>() {
@Override
- protected boolean exists(int index) {
+ protected boolean isValidIndex(int index) {
return index >= 0 && index < data.length;
}