sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1726634 - in /sis/branches/JDK8/core: sis-metadata/src/main/java/org/apache/sis/internal/metadata/ sis-referencing/src/main/java/org/apache/sis/referencing/factory/ sis-referencing/src/main/java/org/apache/sis/referencing/operation/transfo...
Date Mon, 25 Jan 2016 14:53:15 GMT
Author: desruisseaux
Date: Mon Jan 25 14:53:15 2016
New Revision: 1726634

URL: http://svn.apache.org/viewvc?rev=1726634&view=rev
Log:
Implement the MultiAuthorityFactories.getAuthorityCodes(Class) method.

Added:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractIterator.java
      - copied, changed from r1726601, sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedIterator.java
      - copied, changed from r1726633, sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/SetOfUnknownSize.java   (with props)
Removed:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java
Modified:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/OperationMethodSet.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySet.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -20,9 +20,9 @@ import java.util.AbstractCollection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.NoSuchElementException;
 import org.apache.sis.metadata.AbstractMetadata;
 import org.apache.sis.internal.jaxb.Context;
+import org.apache.sis.internal.util.AbstractIterator;
 import org.apache.sis.util.resources.Messages;
 import org.apache.sis.util.ArgumentChecks;
 
@@ -257,18 +257,13 @@ public abstract class LegacyPropertyAdap
     @Override
     public final Iterator<L> iterator() {
         final Iterator<N> it = elements.iterator();
-        return new Iterator<L>() {
+        return new AbstractIterator<L>() {
             /**
              * The container of the next value to return.
              */
             private N container;
 
             /**
-             * The next value to return, or {@code null} if not yet verified.
-             */
-            private L next;
-
-            /**
              * Returns {@code true} if there is more elements to iterate.
              * This method prefetches and stores the next value.
              */
@@ -289,22 +284,6 @@ public abstract class LegacyPropertyAdap
             }
 
             /**
-             * Returns the next element.
-             */
-            @Override
-            public final L next() {
-                L value = next;
-                if (value == null) {
-                    if (!hasNext()) {
-                        throw new NoSuchElementException();
-                    }
-                    value = next;
-                }
-                next = null;
-                return value;
-            }
-
-            /**
              * Removes the last element returned by {@link #next()}.
              */
             @Override

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -159,7 +159,9 @@ abstract class AuthorityFactoryProxy<T>
      * @return The object created from the given code.
      * @throws FactoryException If an error occurred while creating the object.
      */
-    abstract T create(GeodeticAuthorityFactory factory, String code) throws FactoryException;
+    T create(GeodeticAuthorityFactory factory, String code) throws FactoryException {
+        return createFromAPI(factory, code);
+    }
 
     /**
      * Creates the object for the given code using only GeoAPI interfaces.
@@ -177,9 +179,6 @@ abstract class AuthorityFactoryProxy<T>
      */
     static final AuthorityFactoryProxy<InternationalString> DESCRIPTION =
         new AuthorityFactoryProxy<InternationalString>(InternationalString.class, AuthorityFactoryIdentifier.ANY) {
-            @Override InternationalString create(GeodeticAuthorityFactory factory, String code) throws FactoryException {
-                return factory.getDescriptionText(code);
-            }
             @Override InternationalString createFromAPI(AuthorityFactory factory, String code) throws FactoryException {
                 return factory.getDescriptionText(code);
             }
@@ -190,9 +189,6 @@ abstract class AuthorityFactoryProxy<T>
      */
     static final AuthorityFactoryProxy<IdentifiedObject> OBJECT =
         new AuthorityFactoryProxy<IdentifiedObject>(IdentifiedObject.class, AuthorityFactoryIdentifier.ANY) {
-            @Override IdentifiedObject create(GeodeticAuthorityFactory factory, String code) throws FactoryException {
-                return factory.createObject(code);
-            }
             @Override IdentifiedObject createFromAPI(AuthorityFactory factory, String code) throws FactoryException {
                 return factory.createObject(code);
             }
@@ -615,7 +611,7 @@ abstract class AuthorityFactoryProxy<T>
      * @return The proxy for the given type, or {@code null} if the given type is illegal.
      */
     @SuppressWarnings("unchecked")
-    final AuthorityFactoryProxy<? extends T> specialize(final String typeName) {
+    AuthorityFactoryProxy<? extends T> specialize(final String typeName) {
         final AuthorityFactoryProxy<?> c = BY_URN_TYPE.get(typeName.toLowerCase(Locale.US));
         if (c != null) {
             if (c.type.isAssignableFrom(type)) {

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -18,12 +18,18 @@ package org.apache.sis.referencing.facto
 
 import java.util.ServiceLoader;
 import java.util.Collections;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.Map;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
+import java.util.IdentityHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.ConcurrentModificationException;
+import javax.measure.unit.Unit;
 import org.opengis.referencing.*;
 import org.opengis.referencing.cs.*;
 import org.opengis.referencing.crs.*;
@@ -34,16 +40,18 @@ import org.opengis.metadata.extent.Exten
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.util.FactoryException;
 import org.opengis.util.InternationalString;
-import javax.measure.unit.Unit;
+import org.apache.sis.internal.util.AbstractIterator;
 import org.apache.sis.internal.util.Citations;
 import org.apache.sis.internal.util.DefinitionURI;
 import org.apache.sis.internal.util.CollectionsExt;
-import org.apache.sis.internal.util.LazySynchronizedSetIterator;
+import org.apache.sis.internal.util.LazySynchronizedIterator;
+import org.apache.sis.internal.util.SetOfUnknownSize;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.iso.DefaultNameSpace;
+import org.apache.sis.util.collection.BackingStoreException;
 
 
 /**
@@ -212,7 +220,13 @@ public class MultiAuthoritiesFactory ext
      * Returns the set of authority codes for objects of the given type.
      * This method returns the union of codes returned by all factories specified at construction time.
      *
-     * <p><b>Warnings:</b>
+     * <p>The {@link Set#contains(Object)} method of the returned set is lenient:
+     * it accepts various ways to format a code even if the iterator returns only one form.
+     * For example the {@code contains(Object)} method may return {@code true} for {@code "EPSG:4326"},
+     * {code "EPSG::4326"}, {@code "urn:ogc:def:crs:EPSG::4326"}, <i>etc.</i> even if
+     * the iterator returns only {@code "EPSG:4326"}.</p>
+     *
+     * <p><b>Warnings:</b></p>
      * <ul>
      *   <li>Callers should not retain a reference to the returned collection for a long time,
      *       since it may be backed by database connections (depending on the factory implementations).</li>
@@ -227,8 +241,168 @@ public class MultiAuthoritiesFactory ext
      * @throws FactoryException if access to an underlying factory failed.
      */
     @Override
-    public Set<String> getAuthorityCodes(Class<? extends IdentifiedObject> type) throws FactoryException {
-        throw new UnsupportedOperationException("Not supported yet.");      // TODO
+    public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type) throws FactoryException {
+        return new SetOfUnknownSize<String>() {
+            /**
+             * Returns an iterator over all authority codes.
+             * Codes are fetched on-the-fly.
+             */
+            @Override
+            public Iterator<String> iterator() {
+                return new AbstractIterator<String>() {
+                    /** An iterator over the factories for which to return codes. */
+                    private final Iterator<AuthorityFactory> factories = getAllFactories();
+
+                    /** An iterator over the codes of the current factory. */
+                    private Iterator<String> codes = Collections.emptyIterator();
+
+                    /** The prefix to prepend before codes, or {@code null} if none. */
+                    private String prefix;
+
+                    /** For filtering duplicated codes when there is many versions of the same authority. */
+                    private final Set<String> done = new HashSet<>();
+
+                    /** Tests if there is more codes to return. */
+                    @Override public boolean hasNext() {
+                        while (next == null) {
+                            while (!codes.hasNext()) {
+                                do {
+                                    if (!factories.hasNext()) {
+                                        return false;
+                                    }
+                                    final AuthorityFactory factory = factories.next();
+                                    codes = getAuthorityCodes(factory).iterator();
+                                    prefix = getCodeSpace(factory);
+                                } while (!done.add(prefix));
+                            }
+                            next = codes.next();
+                        }
+                        return true;
+                    }
+
+                    /** Returns the next element, with namespace inserted before the code if needed. */
+                    @Override public String next() {
+                        String code = super.next();
+                        if (prefix != null && code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR) < 0) {
+                            code = prefix + DefaultNameSpace.DEFAULT_SEPARATOR + code;
+                        }
+                        return code;
+                    }
+                };
+            }
+
+            /**
+             * The cache of values returned by {@link #getAuthorityCodes(AuthorityFactory)}.
+             */
+            private final Map<AuthorityFactory, Set<String>> cache = new IdentityHashMap<>();
+
+            /**
+             * Returns the authority codes for the given factory.
+             * This method invokes {@link AuthorityFactory#getAuthorityCodes(Class)}
+             * only once per factory and caches the returned {@code Set<String>}.
+             */
+            final Set<String> getAuthorityCodes(final AuthorityFactory factory) {
+                Set<String> codes = cache.get(factory);
+                if (codes == null) {
+                    try {
+                        codes = factory.getAuthorityCodes(type);
+                    } catch (FactoryException e) {
+                        throw new BackingStoreException(e);
+                    }
+                    if (cache.put(factory, codes) != null) {
+                        throw new ConcurrentModificationException();
+                    }
+                }
+                return codes;
+            }
+
+            /**
+             * 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,
+             * but we have determined that the set is not empty.
+             */
+            private int size = -1;
+
+            /**
+             * Returns {@code true} if the {@link #size()} method is cheap.
+             */
+            @Override
+            protected boolean isSizeKnown() {
+                return size >= 0;
+            }
+
+            /**
+             * Returns the number of elements in this set (costly operation).
+             */
+            @Override
+            public int size() {
+                if (size < 0) {
+                    int n = 0;
+                    final Set<String> done = new HashSet<>();
+                    for (final Iterator<AuthorityFactory> it = getAllFactories(); it.hasNext();) {
+                        final AuthorityFactory factory = it.next();
+                        if (done.add(getCodeSpace(factory))) {
+                            n += getAuthorityCodes(factory).size();
+                        }
+                    }
+                    size = n;
+                }
+                return size;
+            }
+
+            /**
+             * Returns {@code true} if the set does not contain any element.
+             * This method is much more efficient than testing {@code size() != 0}
+             * since it will stop iteration as soon as an element is found.
+             */
+            @Override
+            public boolean isEmpty() {
+                if (size == -1) {
+                    for (final Iterator<AuthorityFactory> it = getAllFactories(); it.hasNext();) {
+                        if (!getAuthorityCodes(it.next()).isEmpty()) {
+                            size = -2;      // Size still unknown, but we know that the set is not empty.
+                            return false;
+                        }
+                    }
+                    size = 0;
+                }
+                return size == 0;
+            }
+
+            /**
+             * The proxy for the {@code GeodeticAuthorityFactory.getAuthorityCodes(type).contains(String)}.
+             * Used by {@link #contains(Object)} for delegating its work to the most appropriate factory.
+             */
+            private final AuthorityFactoryProxy<Boolean> contains =
+                new AuthorityFactoryProxy<Boolean>(Boolean.class, AuthorityFactoryIdentifier.ANY) {
+                    @Override Boolean createFromAPI(AuthorityFactory factory, String code) throws FactoryException {
+                        return getAuthorityCodes(factory).contains(code);
+                    }
+                    @Override AuthorityFactoryProxy<Boolean> specialize(String typeName) {
+                        return this;
+                    }
+                };
+
+            /**
+             * Returns {@code true} if the factory contains the given code.
+             */
+            @Override
+            public boolean contains(final Object code) {
+                if (code instanceof String) try {
+                    return create(contains, (String) code);
+                } catch (NoSuchAuthorityCodeException e) {
+                    // Ignore - will return false.
+                } catch (FactoryException e) {
+                    throw new BackingStoreException(e);
+                }
+                return false;
+            }
+
+            /** Declared soon 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();}
+        };
     }
 
     /**
@@ -269,6 +443,16 @@ public class MultiAuthoritiesFactory ext
     }
 
     /**
+     * Returns the "main" namespace of the given factory, or {@code null} if none.
+     * Current implementation returns the first namespace, but this may be changed in any future SIS version.
+     *
+     * <p>The purpose of this method is to get a unique identifier of a factory, ignoring version number.</p>
+     */
+    static String getCodeSpace(final AuthorityFactory factory) {
+        return CollectionsExt.first(getCodeSpaces(factory));
+    }
+
+    /**
      * Caches the given factory, but without replacing existing instance if any.
      * This method returns the factory that we should use, either the given instance of the cached one.
      *
@@ -283,15 +467,17 @@ public class MultiAuthoritiesFactory ext
 
     /**
      * Returns an iterator over all factories in this {@link MultiAuthoritiesFactory}.
-     * This iterator takes care of synchronization on the {@code Iterable<AuthorityFactory>} instances
-     * and ensures that the same factory is not returned twice.
+     * Note that the same factory instance may be returned more than once if it implements more than one
+     * of the {@link CRSAuthorityFactory}, {@link CSAuthorityFactory}, {@link DatumAuthorityFactory} or
+     * {@link CoordinateOperationAuthorityFactory} interfaces.
      *
-     * <p>Note that despite the above-cited synchronization, the returned iterator is <strong>not</strong>
+     * <p>This iterator takes care of synchronization on the {@code Iterable<AuthorityFactory>} instances.
+     * Note that despite the above-cited synchronization, the returned iterator is <strong>not</strong>
      * thread-safe: each thread needs to use its own iterator instance. However provided that the above
      * condition is meet, threads can safely use their iterators concurrently.</p>
      */
     final Iterator<AuthorityFactory> getAllFactories() {
-        return new LazySynchronizedSetIterator<>(providers);
+        return new LazySynchronizedIterator<>(providers);
     }
 
     /**
@@ -474,7 +660,7 @@ public class MultiAuthoritiesFactory ext
      * @return The object from one of the authority factory specified at construction time.
      * @throws FactoryException If an error occurred while creating the object.
      */
-    private <T> T create(AuthorityFactoryProxy<? extends T> proxy, String code) throws FactoryException {
+    final <T> T create(AuthorityFactoryProxy<? extends T> proxy, String code) throws FactoryException {
         ArgumentChecks.ensureNonNull("code", code);
         final String authority, version;
         final String[] parameters;

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/OperationMethodSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/OperationMethodSet.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/OperationMethodSet.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/OperationMethodSet.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -19,12 +19,12 @@ package org.apache.sis.referencing.opera
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Set;
-import java.util.AbstractSet;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.ServiceLoader;
 import org.opengis.referencing.operation.SingleOperation;
 import org.opengis.referencing.operation.OperationMethod;
+import org.apache.sis.internal.util.SetOfUnknownSize;
 import org.apache.sis.referencing.operation.DefaultOperationMethod;
 
 
@@ -43,10 +43,10 @@ import org.apache.sis.referencing.operat
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  */
-final class OperationMethodSet extends AbstractSet<OperationMethod> {
+final class OperationMethodSet extends SetOfUnknownSize<OperationMethod> {
     /**
      * The operation type we are looking for.
      */
@@ -163,6 +163,14 @@ final class OperationMethodSet extends A
     }
 
     /**
+     * Returns {@code true} if the {@link #size()} method is cheap.
+     */
+    @Override
+    protected synchronized boolean isSizeKnown() {
+        return methodIterator == null;
+    }
+
+    /**
      * Returns {@code true} if {@link #next(int)} can return an operation method at the given index.
      */
     final synchronized boolean hasNext(final int index) {

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -18,6 +18,7 @@ package org.apache.sis.referencing.facto
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 import javax.measure.unit.SI;
 import org.opengis.referencing.IdentifiedObject;
@@ -283,4 +284,27 @@ public final strictfp class MultiAuthori
             assertTrue(message, message.contains("Datum"));
         }
     }
+
+    /**
+     * Tests {@link MultiAuthoritiesFactory#getAuthorityCodes(Class)}.
+     *
+     * @throws FactoryException if an error occurred while fetching the set of codes.
+     */
+    @Test
+    public void testGetAuthorityCodes() throws FactoryException {
+        final List<AuthorityFactoryMock> mock = Arrays.asList(
+                new AuthorityFactoryMock("MOCK", null),
+                new AuthorityFactoryMock("MOCK", "2.3"));
+        final MultiAuthoritiesFactory factory = new MultiAuthoritiesFactory(mock, mock, mock, null);
+        final Set<String> codes = factory.getAuthorityCodes(CoordinateReferenceSystem.class);
+
+        assertTrue("MOCK:4979",   codes.contains("MOCK:4979"));     // A geocentric CRS.
+        assertTrue(" mock :: 84", codes.contains(" mock :: 84"));   // A geographic CRS.
+        assertTrue("http://www.opengis.net/gml/srs/mock.xml#4326",
+                codes.contains("http://www.opengis.net/gml/srs/mock.xml#4326"));
+
+        assertFalse("MOCK:6326", codes.contains("MOCK:6326"));      // A geodetic datum.
+        assertFalse("isEmpty()", codes.isEmpty());
+        assertArrayEquals(new String[] {"MOCK:4979", "MOCK:84", "MOCK:4326", "MOCK:5714", "MOCK:9905"}, codes.toArray());
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -23,7 +23,6 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.AbstractSet;
 import java.util.AbstractMap;
 import java.util.NoSuchElementException;
 import java.io.Serializable;
@@ -33,6 +32,7 @@ import org.apache.sis.util.Debug;
 import org.apache.sis.xml.XLink;
 import org.apache.sis.xml.IdentifierMap;
 import org.apache.sis.xml.IdentifierSpace;
+import org.apache.sis.internal.util.SetOfUnknownSize;
 
 import static org.apache.sis.util.collection.Containers.hashMapCapacity;
 
@@ -358,7 +358,7 @@ public class IdentifierMapAdapter extend
          * fields if the underlying list is thread-safe. Furthermore, IdentifierMapAdapter are temporary
          * objects anyway in the current ISOMetadata implementation.
          */
-        return new AbstractSet<Entry<Citation,String>>() {
+        return new SetOfUnknownSize<Entry<Citation,String>>() {
             /** Delegates to the enclosing class. */
             @Override public void clear() throws UnsupportedOperationException {
                 IdentifierMapAdapter.this.clear();

Copied: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractIterator.java (from r1726601, sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractIterator.java?p2=sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractIterator.java&p1=sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java&r1=1726601&r2=1726634&rev=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractIterator.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -17,21 +17,14 @@
 package org.apache.sis.internal.util;
 
 import java.util.Iterator;
-import java.util.IdentityHashMap;
 import java.util.NoSuchElementException;
 
 
 /**
- * An iterator over all elements given by an array of {@code Iterable<E>}, skipping null elements.
- * All uses of an {@code Iterable<E>} (including its iterator) is synchronized on that {@code Iterable} instance.
- *
- * <p>Note that despite the above-cited synchronization, this iterator is <strong>not</strong> thread-safe:
- * each thread needs to use its own iterator instance. However provided that the above condition is meet,
- * different threads can safely use their iterators concurrently even if the underlying {@code Iterable}s
- * were not thread-safe, because of the synchronization on {@code Iterable<E>} instances.</p>
- *
- * <p>This class extends {@code IdentityHashMap} for making sure that we do not return the same instance twice
- * This particular hierarchy is an implementation details that users of this iterator should ignore.</p>
+ * Base class for iterators that prepare the next element in advance.
+ * The {@link #next} field is initially {@code null} and is reset to {@code null} after each call to {@link #next()}.
+ * The {@link #hasNext()} method shall set the {@code #next} field to a non-null value if there is more elements to
+ * return.
  *
  * @param <E> The type of elements to be returned by the iterator.
  *
@@ -40,72 +33,17 @@ import java.util.NoSuchElementException;
  * @version 0.7
  * @module
  */
-@SuppressWarnings("serial")
-public final class LazySynchronizedSetIterator<E> extends IdentityHashMap<E,Boolean> implements Iterator<E> {
-    /**
-     * The providers of iterators. This array shall not be modified by {@code LazySynchronizedSetIterator},
-     * since this is a direct reference to the array given to the constructor (not a copy).
-     */
-    private final Iterable<? extends E>[] providers;
-
-    /**
-     * Index of the {@code Iterable<E>} instance that provides the {@link #it} value.
-     * This is also the instance to use as a synchronization lock.
-     */
-    private int providerIndex;
-
-    /**
-     * The iterator on which to delegate calls to {@link #hasNext()} and {@link #next()}.
-     * This iterator is provided by {@code providers[providerIndex].iterator()}.
-     */
-    private Iterator<? extends E> it;
-
+public abstract class AbstractIterator<E> implements Iterator<E> {
     /**
      * The next value to be returned by {@link #next()}, or {@code null} if not yet determined.
+     * This field should be set by a non-null value by {@link #hasNext()}, unless there is no more elements.
      */
-    private E next;
-
-    /**
-     * Creates a new iterator over all elements returned by the given providers.
-     * Null elements in the given array will be ignored.
-     *
-     * @param providers The providers of iterators. This array is <strong>not</strong> cloned.
-     */
-    public LazySynchronizedSetIterator(final Iterable<? extends E>[] providers) {
-        this.providers = providers;
-    }
+    protected E next;
 
     /**
-     * Returns {@code true} if {@link #next()} can return a non-null element.
-     * This method delegates to the iterators of all {@linkplain #providers}
-     * until one is found that return a non-null element.
-     *
-     * @return {@code true} if there is more elements to return.
+     * For subclass constructors.
      */
-    @Override
-    public boolean hasNext() {
-        if (next != null) {
-            return true;
-        }
-        while (providerIndex < providers.length) {
-            final Iterable<? extends E> provider = providers[providerIndex];
-            if (provider != null) {
-                synchronized (provider) {
-                    if (it == null) {
-                        it = provider.iterator();
-                    }
-                    while (it.hasNext()) {
-                        next = it.next();
-                        if (next != null && put(next, Boolean.TRUE) == null) {
-                            return true;
-                        }
-                    }
-                    it = null;
-                }
-            }
-            providerIndex++;
-        }
-        return false;
+    protected AbstractIterator() {
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -21,7 +21,6 @@ import java.util.Set;
 import java.util.Iterator;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.AbstractSet;
 import java.util.AbstractCollection;
 import java.util.NoSuchElementException;
 import org.apache.sis.io.TableAppender;
@@ -69,7 +68,7 @@ import java.util.Objects;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.7
  * @module
  */
 public abstract class AbstractMap<K,V> implements Map<K,V> {
@@ -377,7 +376,7 @@ public abstract class AbstractMap<K,V> i
      */
     @Override
     public Set<K> keySet() {
-        return new AbstractSet<K>() {
+        return new SetOfUnknownSize<K>() {
             @Override public void        clear()            {       AbstractMap.this.clear();}
             @Override public boolean     isEmpty()          {return AbstractMap.this.isEmpty();}
             @Override public int         size()             {return AbstractMap.this.size();}
@@ -448,7 +447,7 @@ public abstract class AbstractMap<K,V> i
      */
     @Override
     public Set<Entry<K,V>> entrySet() {
-        return new AbstractSet<Entry<K,V>>() {
+        return new SetOfUnknownSize<Entry<K,V>>() {
             @Override public void    clear()   {       AbstractMap.this.clear();}
             @Override public boolean isEmpty() {return AbstractMap.this.isEmpty();}
             @Override public int     size()    {return AbstractMap.this.size();}

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySet.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySet.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySet.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -18,7 +18,6 @@ package org.apache.sis.internal.util;
 
 import java.util.Arrays;
 import java.util.Iterator;
-import java.util.AbstractSet;
 import java.util.NoSuchElementException;
 import org.apache.sis.util.Workaround;
 
@@ -39,11 +38,11 @@ import org.apache.sis.util.Workaround;
  *
  * @author  Martin Desruisseaux (IRD)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  */
 @Workaround(library="JDK", version="1.8.0_31-b13")
-public final class LazySet<E> extends AbstractSet<E> {
+public final class LazySet<E> extends SetOfUnknownSize<E> {
     /**
      * The original source of elements, or {@code null} if unknown.
      */

Copied: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedIterator.java (from r1726633, sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedIterator.java?p2=sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedIterator.java&p1=sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java&r1=1726633&r2=1726634&rev=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedSetIterator.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/LazySynchronizedIterator.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -17,8 +17,6 @@
 package org.apache.sis.internal.util;
 
 import java.util.Iterator;
-import java.util.IdentityHashMap;
-import java.util.NoSuchElementException;
 
 
 /**
@@ -30,9 +28,6 @@ import java.util.NoSuchElementException;
  * different threads can safely use their iterators concurrently even if the underlying {@code Iterable}s
  * were not thread-safe, because of the synchronization on {@code Iterable<E>} instances.</p>
  *
- * <p>This class extends {@code IdentityHashMap} for making sure that we do not return the same instance twice
- * This particular hierarchy is an implementation details that users of this iterator should ignore.</p>
- *
  * @param <E> The type of elements to be returned by the iterator.
  *
  * @author  Martin Desruisseaux (Geomatys)
@@ -40,8 +35,7 @@ import java.util.NoSuchElementException;
  * @version 0.7
  * @module
  */
-@SuppressWarnings("serial")
-public final class LazySynchronizedSetIterator<E> extends IdentityHashMap<E,Boolean> implements Iterator<E> {
+public final class LazySynchronizedIterator<E> extends AbstractIterator<E> {
     /**
      * The providers of iterators. This array shall not be modified by {@code LazySynchronizedSetIterator},
      * since this is a direct reference to the array given to the constructor (not a copy).
@@ -61,17 +55,12 @@ public final class LazySynchronizedSetIt
     private Iterator<? extends E> it;
 
     /**
-     * The next value to be returned by {@link #next()}, or {@code null} if not yet determined.
-     */
-    private E next;
-
-    /**
      * Creates a new iterator over all elements returned by the given providers.
      * Null elements in the given array will be ignored.
      *
      * @param providers The providers of iterators. This array is <strong>not</strong> cloned.
      */
-    public LazySynchronizedSetIterator(final Iterable<? extends E>[] providers) {
+    public LazySynchronizedIterator(final Iterable<? extends E>[] providers) {
         this.providers = providers;
     }
 
@@ -96,7 +85,7 @@ public final class LazySynchronizedSetIt
                     }
                     while (it.hasNext()) {
                         next = it.next();
-                        if (next != null && put(next, Boolean.TRUE) == null) {
+                        if (next != null) {
                             return true;
                         }
                     }
@@ -107,22 +96,4 @@ public final class LazySynchronizedSetIt
         }
         return false;
     }
-
-    /**
-     * Returns the next element in this iteration.
-     *
-     * @return The next element.
-     */
-    @Override
-    public E next() {
-        E value = next;
-        if (value == null) {
-            if (!hasNext()) {
-                throw new NoSuchElementException();
-            }
-            value = next;
-        }
-        next = null;
-        return value;
-    }
 }

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/SetOfUnknownSize.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/SetOfUnknownSize.java?rev=1726634&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/SetOfUnknownSize.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/SetOfUnknownSize.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -0,0 +1,171 @@
+/*
+ * 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.internal.util;
+
+import java.util.Set;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Arrays;
+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()}.
+ *
+ * @param <E> The type of elements in the set.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+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 sometime 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();
+    }
+
+    /**
+     * 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 sometime 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 the elements in an array.
+     *
+     * @return An array containing all set elements.
+     */
+    @Override
+    public Object[] toArray() {
+        return isSizeKnown() ? super.toArray() : toArray(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(array, false);
+    }
+
+    /**
+     * Implementation of the public {@code toArray()} methods.
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T[] toArray(T[] array, boolean trimToSize) {
+        int i = 0;
+        for (final Iterator<E> it = iterator(); 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 sometime 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 equals(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 Set<?> 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();
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/SetOfUnknownSize.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/SetOfUnknownSize.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java?rev=1726634&r1=1726633&r2=1726634&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java [UTF-8] Mon Jan 25 14:53:15 2016
@@ -18,12 +18,12 @@ package org.apache.sis.util.collection;
 
 import java.util.Set;
 import java.util.Iterator;
-import java.util.AbstractSet;
 import java.io.Serializable;
-import org.apache.sis.util.ObjectConverter;
 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.internal.util.SetOfUnknownSize;
 
 
 /**
@@ -61,7 +61,7 @@ import org.apache.sis.util.resources.Err
  * @version 0.3
  * @module
  */
-class DerivedSet<S,E> extends AbstractSet<E> implements CheckedContainer<E>, Serializable {
+class DerivedSet<S,E> extends SetOfUnknownSize<E> implements CheckedContainer<E>, Serializable {
     /**
      * Serial number for inter-operability with different versions.
      */



Mime
View raw message