Search in sources :

Example 1 with SetOfUnknownSize

use of org.apache.sis.internal.util.SetOfUnknownSize in project sis by apache.

the class MultiAuthoritiesFactory method getAuthorityCodes.

/**
 * 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>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>
 *   <li>The returned set is not thread-safe. Each thread should ask its own instance and let
 *       the garbage collector disposes it as soon as the collection is not needed anymore.</li>
 *   <li>Call to the {@link Set#size()} method on the returned collection should be avoided
 *       since it may be costly.</li>
 * </ul>
 *
 * @param  type  the spatial reference objects type.
 * @return the set of authority codes for spatial reference objects of the given type.
 * @throws FactoryException if access to an underlying factory failed.
 */
@Override
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 still unknown, but we know that the set is not empty.
                        size = -2;
                        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();
        }
    };
}
Also used : ConcurrentModificationException(java.util.ConcurrentModificationException) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) Set(java.util.Set) LazySet(org.apache.sis.internal.referencing.LazySet) FactoryException(org.opengis.util.FactoryException) BackingStoreException(org.apache.sis.util.collection.BackingStoreException) InternationalString(org.opengis.util.InternationalString) SetOfUnknownSize(org.apache.sis.internal.util.SetOfUnknownSize) AbstractIterator(org.apache.sis.internal.util.AbstractIterator) Iterator(java.util.Iterator) Collection(java.util.Collection) AbstractIterator(org.apache.sis.internal.util.AbstractIterator) HashMap(java.util.HashMap) ConcurrentMap(java.util.concurrent.ConcurrentMap) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Aggregations

Collection (java.util.Collection)1 ConcurrentModificationException (java.util.ConcurrentModificationException)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 IdentityHashMap (java.util.IdentityHashMap)1 Iterator (java.util.Iterator)1 LinkedHashSet (java.util.LinkedHashSet)1 Map (java.util.Map)1 Set (java.util.Set)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 ConcurrentMap (java.util.concurrent.ConcurrentMap)1 LazySet (org.apache.sis.internal.referencing.LazySet)1 AbstractIterator (org.apache.sis.internal.util.AbstractIterator)1 SetOfUnknownSize (org.apache.sis.internal.util.SetOfUnknownSize)1 BackingStoreException (org.apache.sis.util.collection.BackingStoreException)1 FactoryException (org.opengis.util.FactoryException)1 InternationalString (org.opengis.util.InternationalString)1