| /* |
| * 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.referencing.factory; |
| |
| import java.util.ServiceLoader; |
| import java.util.Collections; |
| import java.util.Collection; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.Set; |
| import java.util.Map; |
| import java.util.HashMap; |
| 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 java.util.logging.Level; |
| import java.util.logging.LogRecord; |
| import javax.measure.Unit; |
| import org.opengis.referencing.*; |
| import org.opengis.referencing.cs.*; |
| import org.opengis.referencing.crs.*; |
| import org.opengis.referencing.datum.*; |
| import org.opengis.referencing.operation.*; |
| import org.opengis.metadata.citation.Citation; |
| import org.opengis.metadata.extent.Extent; |
| import org.opengis.parameter.ParameterDescriptor; |
| import org.opengis.util.FactoryException; |
| import org.opengis.util.InternationalString; |
| import org.apache.sis.internal.system.Loggers; |
| import org.apache.sis.internal.util.Constants; |
| import org.apache.sis.internal.util.AbstractIterator; |
| import org.apache.sis.internal.util.DefinitionURI; |
| import org.apache.sis.internal.util.CollectionsExt; |
| import org.apache.sis.internal.util.SetOfUnknownSize; |
| import org.apache.sis.internal.metadata.NameMeaning; |
| import org.apache.sis.internal.referencing.LazySet; |
| import org.apache.sis.internal.referencing.Resources; |
| import org.apache.sis.internal.system.DefaultFactories; |
| import org.apache.sis.metadata.iso.citation.Citations; |
| import org.apache.sis.referencing.CRS; |
| import org.apache.sis.referencing.IdentifiedObjects; |
| import org.apache.sis.util.ArraysExt; |
| import org.apache.sis.util.CharSequences; |
| import org.apache.sis.util.ArgumentChecks; |
| import org.apache.sis.util.logging.Logging; |
| import org.apache.sis.util.resources.Errors; |
| import org.apache.sis.util.collection.BackingStoreException; |
| |
| |
| /** |
| * A factory that delegates the object creation to another factory determined from the <var>authority</var> part |
| * in “<var>authority</var>:<var>code</var>” arguments. |
| * The list of factories to use as delegates can be specified at construction time. |
| * |
| * <p>This factory requires that every codes given to a {@code createFoo(String)} method are prefixed by a namespace, |
| * for example {@code "EPSG:4326"} or {@code "EPSG::4326"}. |
| * When a {@code createFoo(String)} method is invoked, this class uses the <var>authority</var> part in the |
| * “<var>authority</var>:<var>code</var>” argument for locating a factory capable to create a geodetic object |
| * for the <var>code</var> part. If a factory is found in the list of factories given at construction time, |
| * then the work is delegated to that factory. Otherwise a {@link NoSuchAuthorityFactoryException} is thrown.</p> |
| * |
| * <h2>URI syntax</h2> |
| * This factory can also parse URNs or URLs of the following forms: |
| * |
| * <ul> |
| * <li>{@code "urn:ogc:def:}<var>type</var>{@code :}<var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var>{@code "}</li> |
| * <li>{@code "http://www.opengis.net/def/}<var>type</var>{@code /}<var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var>{@code "}</li> |
| * <li>{@code "http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var>{@code "}</li> |
| * </ul> |
| * |
| * In such cases, the <var>type</var> specified in the URN may be used for invoking a more specific method. |
| * However {@code MultiAuthoritiesFactory} uses the type information in the URN only for |
| * delegating to a more specific method, never for delegating to a less specific method. |
| * An exception will be thrown if the type in the URN is incompatible with the invoked method. |
| * |
| * <div class="note"><b>Example:</b> |
| * if <code>{@linkplain #createObject(String) createObject}("urn:ogc:def:<b>crs</b>:EPSG::4326")</code> is invoked, |
| * then {@code MultiAuthoritiesFactory} will delegate (indirectly, ignoring caching for this example) the object |
| * creation to {@link org.apache.sis.referencing.factory.sql.EPSGDataAccess#createCoordinateReferenceSystem(String)} |
| * instead of {@link org.apache.sis.referencing.factory.sql.EPSGDataAccess#createObject(String)} because of the |
| * {@code "crs"} part in the URN. The more specific method gives better performances and avoid ambiguities.</div> |
| * |
| * This class accepts also combined URIs of the following forms |
| * (only two components shown, but arbitrary number of components is allowed): |
| * |
| * <ul> |
| * <li>{@code "urn:ogc:def:}<var>type</var>{@code ,} |
| * <var>type₁</var>{@code :}<var>authority₁</var>{@code :}<var>version₁</var>{@code :}<var>code₁</var>{@code ,} |
| * <var>type₂</var>{@code :}<var>authority₂</var>{@code :}<var>version₂</var>{@code :}<var>code₂</var>{@code "}</li> |
| * <li>{@code "http://www.opengis.net/def/crs-compound?}<br> |
| * {@code 1=http://www.opengis.net/def/crs/}<var>authority₁</var>{@code /}<var>version₁</var>{@code /}<var>code₁</var>{@code &}<br> |
| * {@code 2=http://www.opengis.net/def/crs/}<var>authority₂</var>{@code /}<var>version₂</var>{@code /}<var>code₂</var>{@code "}</li> |
| * </ul> |
| * |
| * Given such URIs, {@code MultiAuthoritiesFactory} invokes {@link #createObject(String)} for each component |
| * and combines the result as described by the {@link CRS#compound(CoordinateReferenceSystem...)} method. |
| * URNs (but not URLs) can also combine a |
| * {@linkplain org.apache.sis.referencing.datum.DefaultGeodeticDatum geodetic datum} with an |
| * {@linkplain org.apache.sis.referencing.cs.DefaultEllipsoidalCS ellipsoidal coordinate system} for creating a new |
| * {@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS geographic CRS}, or a base geographic CRS with a |
| * {@linkplain org.apache.sis.referencing.operation.DefaultConversion conversion} and a |
| * {@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian coordinate system} for creating a new |
| * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS projected coordinate reference system}, or |
| * {@linkplain org.apache.sis.referencing.operation.AbstractCoordinateOperation coordinate operations} |
| * for creating a concatenated operation. |
| * |
| * <h2>Multiple versions for the same authority</h2> |
| * {@code MultiAuthoritiesFactory} accepts an arbitrary amount of factories for the same authority, provided that |
| * those factories have different version numbers. If a {@code createFoo(String)} method is invoked with a URN |
| * containing a version number different than zero, then {@code MultiAuthoritiesFactory} will search for a factory |
| * with that exact version, or throw a {@link NoSuchAuthorityFactoryException} if no suitable factory is found. |
| * If a {@code createFoo(String)} method is invoked with the version number omitted, then {@code MultiAuthoritiesFactory} |
| * will use the first factory in iteration order for the requested authority regardless of its version number. |
| * |
| * <div class="note"><b>Example:</b> |
| * a {@code MultiAuthoritiesFactory} instance could contain two {@code EPSGFactory} instances: |
| * one for version 8.2 and another one for version 7.9 of the EPSG dataset. |
| * A specific version can be requested in the URN given to {@code createFoo(String)} methods, |
| * for example <code>"urn:ogc:def:crs:EPSG:<b>8.2</b>:4326"</code>. |
| * If no version is given of if the given version is zero, |
| * then the first EPSG factory in iteration order is used regardless of its version number. |
| * </div> |
| * |
| * <h2>Multi-threading</h2> |
| * This class is thread-safe if all delegate factories are themselves thread-safe. |
| * However the factory <em>providers</em>, which are given to the constructor as {@link Iterable} instances, |
| * do not need to be thread-safe. See constructor Javadoc for more information. |
| * |
| * @author Martin Desruisseaux (IRD, Geomatys) |
| * @version 0.8 |
| * |
| * @see org.apache.sis.referencing.CRS#getAuthorityFactory(String) |
| * |
| * @since 0.7 |
| * @module |
| */ |
| public class MultiAuthoritiesFactory extends GeodeticAuthorityFactory implements CRSAuthorityFactory, |
| CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory |
| { |
| /** |
| * The factory providers given at construction time. Elements in the array are for {@link CRSAuthorityFactory}, |
| * {@link CSAuthorityFactory}, {@link DatumAuthorityFactory} and {@link CoordinateOperationAuthorityFactory} |
| * in that order. That order is defined by the constant values in {@link AuthorityFactoryIdentifier}. |
| * |
| * <p>Note that this array is shorter than the amount of {@link AuthorityFactoryIdentifier} values. |
| * The last {@link AuthorityFactoryIdentifier} values are handled in a special way.</p> |
| * |
| * <p>The array may contain {@code null} elements when there is no provider for a given type. |
| * Content of this array shall be immutable after construction time in order to avoid the need |
| * for synchronization when reading the array. However usage of an {@code Iterable} element |
| * shall be synchronized on that {@code Iterable}.</p> |
| */ |
| private final Iterable<? extends AuthorityFactory>[] providers; |
| |
| /** |
| * The factories obtained from {@link #getAuthorityFactory(Class, String, String)} and similar methods. |
| */ |
| private final ConcurrentMap<AuthorityFactoryIdentifier, AuthorityFactory> factories; |
| |
| /** |
| * A bit masks identifying which providers have given us all their factories. |
| * The value {@code (1 << type)} is set when {@code MultiAuthoritiesFactory} |
| * has iterated until the end of {@code providers[type].iterator()}. |
| */ |
| private final AtomicInteger isIterationCompleted; |
| |
| /** |
| * The code spaces of all factories given to the constructor, created when first requested. |
| * |
| * @see #getCodeSpaces() |
| */ |
| private volatile Set<String> codeSpaces; |
| |
| /** |
| * Whether this factory should relax some rules when processing a given authority code. |
| * See {@link #isLenient()} javadoc for a description of relaxed rules. |
| * |
| * @see #isLenient() |
| */ |
| private volatile boolean isLenient; |
| |
| /** |
| * The factories for which we have logged a warning. This is used in order to avoid logging the same |
| * warnings many time. We do not bother using a concurrent map here since this map should be rarely used. |
| */ |
| private final Map<AuthorityFactoryIdentifier, Boolean> warnings; |
| |
| /** |
| * Creates a new multi-factories instance using the given lists of factories. |
| * Calls to {@code createFoo(String)} methods will scan the supplied factories in their iteration order when first needed. |
| * The first factory having the requested {@linkplain GeodeticAuthorityFactory#getCodeSpaces() namespace} will be used. |
| * |
| * <h4>Requirements</h4> |
| * {@code MultiAuthoritiesFactory} may iterate over the same {@code Iterable} more than once. |
| * Each iteration <strong>shall</strong> return the same instances than previous iterations, |
| * unless {@link #reload()} has been invoked. |
| * |
| * <p>The {@code Iterable}s do not need to be thread-safe. |
| * {@code MultiAuthoritiesFactory} will use them only in blocks synchronized on the {@code Iterable} instance. |
| * For example all usages of {@code crsFactory} will be done inside a {@code synchronized(crsFactory)} block.</p> |
| * |
| * <h4>Name collision</h4> |
| * If an {@code Iterable} contains more than one factory for the same namespace and version, |
| * then only the first occurrence will be used. All additional factories for the same namespace |
| * and version will be ignored, after a warning has been logged. |
| * |
| * <h4>Caching</h4> |
| * {@code MultiAuthoritiesFactory} caches the factories found from the given {@code Iterable}s, |
| * but does not cache the objects created by those factories. |
| * This constructor assumes that the given factories already do their own caching. |
| * |
| * @param crsFactories the factories for creating {@link CoordinateReferenceSystem} objects, or null if none. |
| * @param csFactories the factories for creating {@link CoordinateSystem} objects, or null if none. |
| * @param datumFactories the factories for creating {@link Datum} objects, or null if none. |
| * @param copFactories the factories for creating {@link CoordinateOperation} objects, or null if none. |
| */ |
| @SuppressWarnings({"unchecked", "rawtypes", "empty-statement"}) // Generic array creation. |
| public MultiAuthoritiesFactory(final Iterable<? extends CRSAuthorityFactory> crsFactories, |
| final Iterable<? extends CSAuthorityFactory> csFactories, |
| final Iterable<? extends DatumAuthorityFactory> datumFactories, |
| final Iterable<? extends CoordinateOperationAuthorityFactory> copFactories) |
| { |
| final Iterable<? extends AuthorityFactory>[] p = new Iterable[4]; |
| p[AuthorityFactoryIdentifier.CRS] = crsFactories; |
| p[AuthorityFactoryIdentifier.CS] = csFactories; |
| p[AuthorityFactoryIdentifier.DATUM] = datumFactories; |
| p[AuthorityFactoryIdentifier.OPERATION] = copFactories; |
| /* |
| * Mark null Iterables as if we already iterated over all their elements. |
| * Opportunistically reduce the array size by trimming trailing null elements. |
| * The memory gain is negligible, but this will reduce the number of iterations in loops. |
| */ |
| int length = 0, nullMask = 0; |
| for (int i=0; i < p.length; i++) { |
| if (p[i] != null) length = i+1; |
| else nullMask |= (1 << i); |
| } |
| providers = ArraysExt.resize(p, length); |
| factories = new ConcurrentHashMap<>(); |
| warnings = new HashMap<>(); |
| isIterationCompleted = new AtomicInteger(nullMask); |
| } |
| |
| /** |
| * Returns whether this factory should relax some rules when processing a given authority code. |
| * If this value is {@code true}, then the behavior of this {@code MultiAuthoritiesFactory} |
| * is changed as below: |
| * |
| * <ul> |
| * <li>If a version is specified in a URN but there is no factory for that specific version, |
| * then fallback on a factory for the same authority but the default version.</li> |
| * </ul> |
| * |
| * The default value is {@code false}, which means that an exception will be thrown |
| * if there is no factory specifically for the requested version. |
| * |
| * @return whether this factory should relax some rules when processing a given authority code. |
| */ |
| public boolean isLenient() { |
| return isLenient; |
| } |
| |
| /** |
| * Sets whether this factory should relax some rules when processing a given code. |
| * |
| * @param lenient whether this factory should relax some rules when processing a given authority code. |
| */ |
| public void setLenient(final boolean lenient) { |
| isLenient = lenient; |
| } |
| |
| /** |
| * Returns the database or specification that defines the codes recognized by this factory. |
| * The default implementation returns {@code null} since {@code MultiAuthoritiesFactory} is not |
| * about a particular authority. |
| */ |
| @Override |
| public Citation getAuthority() { |
| return null; |
| } |
| |
| /** |
| * 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(Constants.DEFAULT_SEPARATOR) < 0) { |
| code = prefix + Constants.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();} |
| }; |
| } |
| |
| /** |
| * Returns the code spaces of all factories given to the constructor. |
| * |
| * <div class="note"><b>Implementation note:</b> |
| * the current implementation may be relatively costly since it implies instantiation of all factories. |
| * </div> |
| * |
| * @return the code spaces of all factories. |
| */ |
| @Override |
| @SuppressWarnings("ReturnOfCollectionOrArrayField") |
| public Set<String> getCodeSpaces() { |
| Set<String> union = codeSpaces; |
| if (union == null) { |
| union = new LinkedHashSet<>(); |
| for (final Iterator<AuthorityFactory> it = getAllFactories(); it.hasNext();) { |
| union.addAll(getCodeSpaces(it.next())); |
| } |
| codeSpaces = union = CollectionsExt.unmodifiableOrCopy(union); |
| } |
| return union; |
| } |
| |
| /** |
| * Returns the code spaces for the given factory. |
| * This method delegates to {@link GeodeticAuthorityFactory#getCodeSpaces()} if possible, |
| * or reproduces its default implementation otherwise. |
| */ |
| private static Set<String> getCodeSpaces(final AuthorityFactory factory) { |
| if (factory instanceof GeodeticAuthorityFactory) { |
| return ((GeodeticAuthorityFactory) factory).getCodeSpaces(); |
| } else { |
| final String authority = Citations.toCodeSpace(factory.getAuthority()); |
| return (authority != null) ? Collections.singleton(authority) : Collections.emptySet(); |
| } |
| } |
| |
| /** |
| * 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. |
| * |
| * @param identifier the type, authority and version of the factory to cache. |
| * @param factory the factory to cache. |
| * @return the given {@code factory} if no previous instance was cached, or the existing instance otherwise. |
| */ |
| private AuthorityFactory cache(final AuthorityFactoryIdentifier identifier, final AuthorityFactory factory) { |
| final AuthorityFactory existing = factories.putIfAbsent(identifier.intern(), factory); |
| return (existing != null) ? existing : factory; |
| } |
| |
| /** |
| * Returns an iterator over all factories in this {@link MultiAuthoritiesFactory}. |
| * 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>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 met, threads can safely use their iterators concurrently.</p> |
| */ |
| final Iterator<AuthorityFactory> getAllFactories() { |
| return new LazySynchronizedIterator<>(providers); |
| } |
| |
| /** |
| * Returns the factory identified by the given type, authority and version. |
| * |
| * @param <T> the compile-time value of {@code type}. |
| * @param type the type of the desired factory as one of the {@link CRSAuthorityFactory}, {@link CSAuthorityFactory}, |
| * {@link DatumAuthorityFactory} or {@link CoordinateOperationFactory} interfaces. |
| * @param authority the namespace or authority identifier of the desired factory. |
| * Examples: {@code "EPSG"}, {@code "CRS"} or {@code "AUTO2"}. |
| * @param version the version of the desired factory, or {@code null} for the default version. |
| * @return the factory for the given type, authority and version. |
| * @throws NoSuchAuthorityFactoryException if no suitable factory has been found. |
| */ |
| /* |
| * This method is declared final for avoiding the false impression than overriding this method would change |
| * the behavior of MultiAuthoritiesFactory. It would not because the 'create(…)' method invokes the private |
| * 'getAuthorityFactory(…)' instead of the public one. |
| */ |
| public final <T extends AuthorityFactory> T getAuthorityFactory(final Class<T> type, |
| final String authority, final String version) throws NoSuchAuthorityFactoryException |
| { |
| ArgumentChecks.ensureNonNull("type", type); |
| ArgumentChecks.ensureNonNull("authority", authority); |
| return type.cast(getAuthorityFactory(AuthorityFactoryIdentifier.create(type, authority, version))); |
| } |
| |
| /** |
| * Returns the factory identified by the given type, authority and version. If no such factory is found in |
| * the cache, then this method iterates over the factories created by the providers given at construction time. |
| * |
| * @param request the type, authority and version of the desired factory. |
| * @return the factory for the given type, authority and version. |
| * @throws NoSuchAuthorityFactoryException if no suitable factory has been found. |
| */ |
| private AuthorityFactory getAuthorityFactory(final AuthorityFactoryIdentifier request) |
| throws NoSuchAuthorityFactoryException |
| { |
| AuthorityFactory factory = factories.get(request); |
| if (factory != null) { |
| return factory; |
| } |
| /* |
| * If there is no factory in the cache for the given type, authority and version, then check if the |
| * default factory (when no version is specified) is actually the factory for the requested version. |
| * The reason why we have to do this check is because we do not ask the version of a factory before |
| * we really need to do so, since fetching this information is relatively costly for some factories |
| * (e.g. EPSGFactory needs to look in the "Version History" table of the dataset) and rarely needed. |
| */ |
| if (request.hasVersion()) { |
| factory = factories.get(request.versionOf(null)); |
| if (factory != null) { |
| if (request.versionOf(factory.getAuthority()) == request) { |
| // Default factory is for the version that user requested. Cache that finding. |
| return cache(request, factory); |
| } |
| factory = null; |
| } |
| } |
| /* |
| * At this point we know that there is no factory in the cache for the requested type, authority and version. |
| * Create new factories with the Iterables given at construction time. If we already started an iteration in |
| * a previous call to this getAuthorityFactory(…) method, we will continue the search after skipping already |
| * cached instances. |
| */ |
| int doneMask = isIterationCompleted.get(); |
| final int type = request.type; |
| if ((doneMask & (1 << type)) == 0) { |
| if (type >= 0 && type < providers.length) { |
| final Iterable<? extends AuthorityFactory> provider = providers[type]; |
| final Iterator<? extends AuthorityFactory> it; |
| synchronized (provider) { // Should never be null because of the 'doneMask' check. |
| it = provider.iterator(); |
| while (it.hasNext()) { |
| factory = it.next(); |
| if (factory != null) break; // Paranoiac check against null factories. |
| } |
| } |
| /* |
| * Search for a factory for the given authority. Caches all factories that we find |
| * during the iteration process. Some factories may already be cached as a result |
| * of a partial iteration in a previous call to getAuthorityFactory(…). |
| */ |
| while (factory != null) { |
| for (final String namespace : getCodeSpaces(factory)) { |
| final AuthorityFactoryIdentifier unversioned = request.unversioned(namespace); |
| AuthorityFactory cached = cache(unversioned, factory); |
| final AuthorityFactory found = request.equals(unversioned) ? cached : null; |
| /* |
| * Only if we have no choice, ask to the factory what is its version number. |
| * We have no choice when ignoring the version number causes a conflict, or |
| * when the user asked for a specific version. |
| */ |
| if (factory != cached || (request.hasVersion() && request.isSameAuthority(unversioned))) { |
| final AuthorityFactoryIdentifier versioned = unversioned.versionOf(factory.getAuthority()); |
| if (versioned != unversioned) { |
| /* |
| * Before to cache the factory with a key containing the factory version, make sure |
| * that we took in account the version of the default factory. This will prevent the |
| * call to 'cache(versioned, factory)' to overwrite the default factory. |
| */ |
| if (factory != cached) { |
| cache(unversioned.versionOf(cached.getAuthority()), cached); |
| } |
| cached = cache(versioned, factory); |
| } |
| /* |
| * If there is a conflict, log a warning provided that we did not already reported |
| * that conflict. |
| */ |
| if (factory != cached && canLog(versioned)) { |
| versioned.logConflict(cached); |
| } |
| if (request.equals(versioned)) { |
| return cached; |
| } |
| } |
| if (found != null) { |
| return found; |
| } |
| } |
| factory = null; |
| synchronized (provider) { |
| while (it.hasNext()) { |
| factory = it.next(); |
| if (factory != null) break; // Paranoiac check against null factories. |
| } |
| } |
| } |
| } else if (type >= AuthorityFactoryIdentifier.GEODETIC) { |
| /* |
| * Special cases: if the requested factory is ANY, take the first factory that we can find |
| * regardless of its type. We will try CRS, CS, DATUM and OPERATION factories in that order. |
| * The GEODETIC type is like ANY except for the additional restriction that the factory shall |
| * be an instance of the SIS-specific GeodeticAuthorityFactory class. |
| */ |
| assert providers.length <= Math.min(type, Byte.MAX_VALUE) : type; |
| for (byte i=0; i < providers.length; i++) { |
| factory = getAuthorityFactory(request.newType(i)); |
| switch (type) { |
| case AuthorityFactoryIdentifier.ANY: { |
| return factory; |
| } |
| case AuthorityFactoryIdentifier.GEODETIC: { |
| if (factory instanceof GeodeticAuthorityFactory) { |
| return factory; |
| } |
| } |
| } |
| } |
| } |
| /* |
| * Remember that we have iterated over all elements of this provider, so we will not try again. |
| * Note that the mask values may also be modified in other threads for other providers, so we |
| * need to atomically verify that the current value has not been modified before to set it. |
| */ |
| while (!isIterationCompleted.compareAndSet(doneMask, doneMask | (1 << type))) { |
| doneMask = isIterationCompleted.get(); |
| } |
| } |
| /* |
| * No factory found. Before to fail, search for a factory for the default version if we are allowed to. |
| */ |
| if (request.hasVersion() && isLenient) { |
| factory = getAuthorityFactory(request.versionOf(null)); |
| if (canLog(request)) { |
| request.logFallback(); |
| } |
| return factory; |
| } |
| final String authority = request.getAuthorityAndVersion().toString(); |
| throw new NoSuchAuthorityFactoryException(Resources.format(Resources.Keys.UnknownAuthority_1, authority), authority); |
| } |
| |
| /** |
| * Returns {@code true} if this {@code MultiAuthoritiesFactory} can log a warning for the given factory. |
| */ |
| private boolean canLog(AuthorityFactoryIdentifier identifier) { |
| synchronized (warnings) { |
| if (warnings.containsKey(identifier)) { |
| return false; |
| } |
| // Invoke identifier.intern() only if needed. |
| return warnings.putIfAbsent(identifier.intern(), Boolean.TRUE) == null; |
| } |
| } |
| |
| /** |
| * Creates an object from a code using the given proxy. |
| * |
| * @param <T> the type of the object to be returned. |
| * @param proxy the proxy to use for creating the object. |
| * @param code the code of the object to create. |
| * @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 { |
| ArgumentChecks.ensureNonNull("code", code); |
| final String authority, version; |
| final String[] parameters; |
| final DefinitionURI uri = DefinitionURI.parse(code); |
| if (uri != null) { |
| Class<? extends T> type = proxy.type; |
| proxy = proxy.specialize(uri.type); |
| /* |
| * If the URN or URL contains combined references for compound coordinate reference systems, |
| * create the components. First we verify that all component references have been parsed |
| * before to start creating any object. |
| */ |
| if (uri.code == null) { |
| final DefinitionURI[] components = uri.components; |
| if (components != null) { |
| for (int i=0; i < components.length; i++) { |
| if (components[i] == null) { |
| throw new NoSuchAuthorityCodeException(Resources.format( |
| Resources.Keys.CanNotParseCombinedReference_2, i+1, uri.isHTTP ? 1 : 0), |
| uri.authority, null, uri.toString()); |
| } |
| } |
| if (proxy != null) type = proxy.type; // Use the more specific type declared in the URN. |
| return combine(type, components, uri.isHTTP); |
| } |
| } |
| /* |
| * At this point we determined that the URN or URL references a single instance (not combined references). |
| * Example: "urn:ogc:def:crs:EPSG:9.1:4326". Verifies that the object type is recognized and that a code |
| * is present. The remainder steps are the same as if the user gave a simple code (e.g. "EPSG:4326"). |
| */ |
| if (uri.authority == null) { |
| // We want this check before the 'code' value is modified below. |
| throw new NoSuchAuthorityCodeException( |
| Resources.format(Resources.Keys.MissingAuthority_1, code), null, uri.code, code); |
| } |
| authority = uri.authority; |
| version = uri.version; |
| code = uri.code; |
| parameters = uri.parameters; |
| if (code == null || proxy == null) { |
| final String s = uri.toString(); |
| final String message; |
| if (code == null) { |
| message = Errors.format(Errors.Keys.MissingComponentInElement_2, s, "code"); |
| } else { |
| message = Resources.format(Resources.Keys.CanNotCreateObjectAsInstanceOf_2, type, |
| DefinitionURI.PREFIX + DefinitionURI.SEPARATOR + uri.type); |
| } |
| throw new NoSuchAuthorityCodeException(message, authority, code, s); |
| } |
| } else { |
| /* |
| * Separate the authority from the rest of the code. The authority is mandatory; if missing, |
| * an exception will be thrown. Note that the CharSequences.skipLeading/TrailingWhitespaces(…) |
| * methods are robust to negative index, so the code will work even if code.indexOf(…) returned -1. |
| */ |
| int afterAuthority = code.indexOf(Constants.DEFAULT_SEPARATOR); |
| int end = CharSequences.skipTrailingWhitespaces(code, 0, afterAuthority); |
| int start = CharSequences.skipLeadingWhitespaces(code, 0, end); |
| if (start >= end) { |
| throw new NoSuchAuthorityCodeException(Resources.format(Resources.Keys.MissingAuthority_1, code), null, code); |
| } |
| authority = code.substring(start, end); |
| /* |
| * Separate the version from the rest of the code. The version is optional. The code may have no room |
| * for version (e.g. "EPSG:4326"), or specify an empty version (e.g. "EPSG::4326"). If the version is |
| * equals to an empty string or to the "0" string, it will be considered as no version. Usage of 0 as |
| * a pseudo-version is a practice commonly found in other software products. |
| */ |
| int afterVersion = code.indexOf(Constants.DEFAULT_SEPARATOR, ++afterAuthority); |
| start = CharSequences.skipLeadingWhitespaces(code, afterAuthority, afterVersion); |
| end = CharSequences.skipTrailingWhitespaces(code, start, afterVersion); |
| version = (start < end && !code.regionMatches(start, DefinitionURI.NO_VERSION, 0, |
| DefinitionURI.NO_VERSION.length())) ? code.substring(start, end) : null; |
| if (version != null && !Character.isUnicodeIdentifierPart(version.codePointAt(0))) { |
| throw new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.InvalidVersionIdentifier_1, version), authority, code); |
| } |
| /* |
| * Separate the code from the authority and the version. |
| */ |
| code = CharSequences.trimWhitespaces(code, Math.max(afterAuthority, afterVersion + 1), code.length()).toString(); |
| parameters = null; |
| } |
| /* |
| * At this point we have the code without the authority and version parts. |
| * Push back the authority part if the factory may need it. For now we do that only if the code has |
| * parameters, since interpretation of the unit parameter in "AUTO(2):42001,unit,longitude,latitude" |
| * depends on whether the authority is "AUTO" or "AUTO2". This works for now, but we may need a more |
| * rigorous approach in a future SIS version. |
| */ |
| if (parameters != null || code.indexOf(CommonAuthorityFactory.SEPARATOR) >= 0) { |
| final StringBuilder buffer = new StringBuilder(authority.length() + code.length() + 1) |
| .append(authority).append(Constants.DEFAULT_SEPARATOR).append(code); |
| if (parameters != null) { |
| for (final String p : parameters) { |
| buffer.append(CommonAuthorityFactory.SEPARATOR).append(p); |
| } |
| } |
| code = buffer.toString(); |
| } |
| return proxy.createFromAPI(getAuthorityFactory(AuthorityFactoryIdentifier.create(proxy.factoryType, authority, version)), code); |
| } |
| |
| /** |
| * Returns a description of the object corresponding to a code. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li>{@code urn:ogc:def:}<var>type</var>{@code :}<var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/def/}<var>type</var>{@code /}<var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return a description of the object, or {@code null} if the object |
| * corresponding to the specified {@code code} has no description. |
| * @throws FactoryException if an error occurred while fetching the description. |
| */ |
| @Override |
| public InternationalString getDescriptionText(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.DESCRIPTION, code); |
| } |
| |
| /** |
| * Creates an arbitrary object from a code. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var> — note that this form is ambiguous</li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var> — note that this form is ambiguous</li> |
| * <li>{@code urn:ogc:def:}<var>type</var>{@code :}<var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/def/}<var>type</var>{@code /}<var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * The two first formats are ambiguous when used with this {@code createObject(String)} method |
| * because different kinds of objects can have the same code. |
| * |
| * @return the object for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public IdentifiedObject createObject(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.OBJECT, code); |
| } |
| |
| /** |
| * Creates an arbitrary coordinate reference system from a code. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public CoordinateReferenceSystem createCoordinateReferenceSystem(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.CRS, code); |
| } |
| |
| /** |
| * Creates a 2- or 3-dimensional coordinate reference system based on an ellipsoidal approximation of the geoid. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public GeographicCRS createGeographicCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.GEOGRAPHIC_CRS, code); |
| } |
| |
| /** |
| * Creates a 3-dimensional coordinate reference system with the origin at the approximate centre of mass of the earth. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public GeocentricCRS createGeocentricCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.GEOCENTRIC_CRS, code); |
| } |
| |
| /** |
| * Creates a 2-dimensional coordinate reference system used to approximate the shape of the earth on a planar surface. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public ProjectedCRS createProjectedCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.PROJECTED_CRS, code); |
| } |
| |
| /** |
| * Creates a 1-dimensional coordinate reference system used for recording heights or depths. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public VerticalCRS createVerticalCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.VERTICAL_CRS, code); |
| } |
| |
| /** |
| * Creates a 1-dimensional coordinate reference system used for the recording of time. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public TemporalCRS createTemporalCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.TEMPORAL_CRS, code); |
| } |
| |
| /** |
| * Creates a CRS describing the position of points through two or more independent coordinate reference systems. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public CompoundCRS createCompoundCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.COMPOUND_CRS, code); |
| } |
| |
| /** |
| * Creates a CRS that is defined by its coordinate conversion from another CRS (not by a datum). |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public DerivedCRS createDerivedCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.DERIVED_CRS, code); |
| } |
| |
| /** |
| * Creates a 1-, 2- or 3-dimensional contextually local coordinate reference system. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public EngineeringCRS createEngineeringCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.ENGINEERING_CRS, code); |
| } |
| |
| /** |
| * Creates a 2-dimensional engineering coordinate reference system applied to locations in images. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>crs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>crs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * <li>{@code http://www.opengis.net/gml/srs/}<var>authority</var>{@code .xml#}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate reference system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public ImageCRS createImageCRS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.IMAGE_CRS, code); |
| } |
| |
| /** |
| * Creates an arbitrary datum from a code. The returned object will typically be an |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>datum</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>datum</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the datum for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public Datum createDatum(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.DATUM, code); |
| } |
| |
| /** |
| * Creates a datum defining the location and orientation of an ellipsoid that approximates the shape of the earth. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>datum</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>datum</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the datum for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public GeodeticDatum createGeodeticDatum(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.GEODETIC_DATUM, code); |
| } |
| |
| /** |
| * Creates a datum identifying a particular reference level surface used as a zero-height surface. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>datum</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>datum</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the datum for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public VerticalDatum createVerticalDatum(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.VERTICAL_DATUM, code); |
| } |
| |
| /** |
| * Creates a datum defining the origin of a temporal coordinate reference system. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>datum</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>datum</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the datum for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public TemporalDatum createTemporalDatum(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.TEMPORAL_DATUM, code); |
| } |
| |
| /** |
| * Creates a datum defining the origin of an engineering coordinate reference system. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>datum</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>datum</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the datum for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public EngineeringDatum createEngineeringDatum(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.ENGINEERING_DATUM, code); |
| } |
| |
| /** |
| * Creates a datum defining the origin of an image coordinate reference system. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>datum</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>datum</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the datum for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public ImageDatum createImageDatum(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.IMAGE_DATUM, code); |
| } |
| |
| /** |
| * Creates a geometric figure that can be used to describe the approximate shape of the earth. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>ellipsoid</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>ellipsoid</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the ellipsoid for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public Ellipsoid createEllipsoid(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.ELLIPSOID, code); |
| } |
| |
| /** |
| * Creates a prime meridian defining the origin from which longitude values are determined. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>meridian</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>meridian</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the prime meridian for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public PrimeMeridian createPrimeMeridian(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.PRIME_MERIDIAN, code); |
| } |
| |
| /** |
| * Creates information about spatial, vertical, and temporal extent (usually a domain of validity) from a code. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * </ul> |
| * |
| * @return the extent for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public Extent createExtent(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.EXTENT, code); |
| } |
| |
| /** |
| * Creates an arbitrary coordinate system from a code. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>cs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>cs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public CoordinateSystem createCoordinateSystem(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.COORDINATE_SYSTEM, code); |
| } |
| |
| /** |
| * Creates a 2- or 3-dimensional coordinate system for geodetic latitude and longitude, sometime with ellipsoidal height. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>cs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>cs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public EllipsoidalCS createEllipsoidalCS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.ELLIPSOIDAL_CS, code); |
| } |
| |
| /** |
| * Creates a 1-dimensional coordinate system for heights or depths of points. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>cs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>cs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public VerticalCS createVerticalCS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.VERTICAL_CS, code); |
| } |
| |
| /** |
| * Creates a 1-dimensional coordinate system for heights or depths of points. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>cs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>cs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public TimeCS createTimeCS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.TIME_CS, code); |
| } |
| |
| /** |
| * Creates a 2- or 3-dimensional Cartesian coordinate system made of straight orthogonal axes. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>cs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>cs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public CartesianCS createCartesianCS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.CARTESIAN_CS, code); |
| } |
| |
| /** |
| * Creates a 3-dimensional coordinate system with one distance measured from the origin and two angular coordinates. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>cs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>cs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public SphericalCS createSphericalCS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.SPHERICAL_CS, code); |
| } |
| |
| /** |
| * Creates a 3-dimensional coordinate system made of a polar coordinate system |
| * extended by a straight perpendicular axis. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>cs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>cs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public CylindricalCS createCylindricalCS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.CYLINDRICAL_CS, code); |
| } |
| |
| /** |
| * Creates a 2-dimensional coordinate system for coordinates represented by a distance from the origin |
| * and an angle from a fixed direction. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>cs</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>cs</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the coordinate system for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public PolarCS createPolarCS(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.POLAR_CS, code); |
| } |
| |
| /** |
| * Creates a coordinate system axis with name, direction, unit and range of values. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>axis</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>axis</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the axis for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public CoordinateSystemAxis createCoordinateSystemAxis(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.AXIS, code); |
| } |
| |
| /** |
| * Creates an unit of measurement from a code. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>uom</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>uom</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the unit of measurement for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public Unit<?> createUnit(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.UNIT, code); |
| } |
| |
| /** |
| * Creates a definition of a single parameter used by an operation method. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>parameter</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>parameter</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the parameter descriptor for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public ParameterDescriptor<?> createParameterDescriptor(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.PARAMETER, code); |
| } |
| |
| /** |
| * Creates a description of the algorithm and parameters used to perform a coordinate operation. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>method</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>method</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the operation method for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public OperationMethod createOperationMethod(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.METHOD, code); |
| } |
| |
| /** |
| * Creates an operation for transforming coordinates in the source CRS to coordinates in the target CRS. |
| * The given code can use any of the following patterns, where <var>version</var> is optional: |
| * <ul> |
| * <li><var>authority</var>{@code :}<var>code</var></li> |
| * <li><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>urn:ogc:def:<b>coordinateOperation</b>:</code><var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var></li> |
| * <li><code>http://www.opengis.net/def/<b>coordinateOperation</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> |
| * </ul> |
| * |
| * @return the operation for the given code. |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public CoordinateOperation createCoordinateOperation(final String code) throws FactoryException { |
| return create(AuthorityFactoryProxy.OPERATION, code); |
| } |
| |
| /** |
| * Creates operations from source and target coordinate reference system codes. |
| * If the authority for the two given CRS is handled by the same factory, then |
| * this method delegates to that factory. Otherwise this method returns an empty set. |
| * |
| * @throws FactoryException if the object creation failed. |
| */ |
| @Override |
| public Set<CoordinateOperation> createFromCoordinateReferenceSystemCodes( |
| final String sourceCRS, final String targetCRS) throws FactoryException |
| { |
| final Deferred deferred = new Deferred(); |
| final CoordinateOperationAuthorityFactory factory = create(deferred, sourceCRS); |
| final String source = deferred.code; |
| if (create(deferred, targetCRS) == factory) { |
| return factory.createFromCoordinateReferenceSystemCodes(source, deferred.code); |
| } |
| /* |
| * No coordinate operation because of mismatched factories. This is not illegal (the result is an empty set) |
| * but it is worth to notify the user because this case has some chances to be an user error. |
| */ |
| final LogRecord record = Resources.forLocale(null).getLogRecord(Level.WARNING, |
| Resources.Keys.MismatchedOperationFactories_2, sourceCRS, targetCRS); |
| record.setLoggerName(Loggers.CRS_FACTORY); |
| Logging.log(MultiAuthoritiesFactory.class, "createFromCoordinateReferenceSystemCodes", record); |
| return super.createFromCoordinateReferenceSystemCodes(sourceCRS, targetCRS); |
| } |
| |
| /** |
| * A proxy that does not execute immediately the {@code create} method on a factory, |
| * but instead stores information for later execution. |
| */ |
| private static final class Deferred extends AuthorityFactoryProxy<CoordinateOperationAuthorityFactory> { |
| Deferred() {super(CoordinateOperationAuthorityFactory.class, AuthorityFactoryIdentifier.OPERATION);} |
| |
| /** The authority code saved by the {@code createFromAPI(…)} method. */ |
| String code; |
| |
| /** |
| * Saves the given code in the {@link #code} field and returns the given factory unchanged. |
| * @throws FactoryException if the given factory is not an instance of {@link CoordinateOperationAuthorityFactory}. |
| */ |
| @Override |
| CoordinateOperationAuthorityFactory createFromAPI(final AuthorityFactory factory, final String code) |
| throws FactoryException |
| { |
| this.code = code; |
| return opFactory(factory); |
| } |
| } |
| |
| /** |
| * Invoked when a {@code createFoo(…)} method is given a combined URI. |
| * A combined URI is a URN or URL referencing other components. For example if the given URI |
| * is {@code "urn:ogc:def:crs, crs:EPSG::27700, crs:EPSG::5701"}, then the components are: |
| * <ol> |
| * <li>{@code "urn:ogc:def:crs:EPSG:9.1:27700"}</li> |
| * <li>{@code "urn:ogc:def:crs:EPSG:9.1:5701"}</li> |
| * </ol> |
| * |
| * We do not require the components to be instance of CRS, since the "Definition identifier URNs in |
| * OGC namespace" best practice paper allows other kinds of combination (e.g. of coordinate operations). |
| * |
| * @param <T> compile-time value of {@code type} argument. |
| * @param type type of object to create. |
| * @param references parsed URI of the components. |
| * @param isHTTP whether the user URI is an URL (i.e. {@code "http://something"}) instead than a URN. |
| * @return the combined object. |
| * @throws FactoryException if an error occurred while creating the combined object. |
| */ |
| private <T> T combine(final Class<T> type, final DefinitionURI[] references, final boolean isHTTP) throws FactoryException { |
| /* |
| * Identify the type requested by the user and create all components with the assumption that they will |
| * be of that type. This is the most common case. If during iteration we find an object of another kind, |
| * then the array type will be downgraded to IdentifiedObject[]. The 'componentType' variable will keep |
| * its non-null value only if the array stay of the expected sub-type. |
| */ |
| final byte requestedType; |
| IdentifiedObject[] components; |
| Class<? extends IdentifiedObject> componentType; |
| if (CoordinateReferenceSystem.class.isAssignableFrom(type)) { |
| requestedType = AuthorityFactoryIdentifier.CRS; |
| componentType = CoordinateReferenceSystem.class; |
| components = new CoordinateReferenceSystem[references.length]; // Intentional covariance. |
| } else if (CoordinateOperation.class.isAssignableFrom(type)) { |
| requestedType = AuthorityFactoryIdentifier.OPERATION; |
| componentType = CoordinateOperation.class; |
| components = new CoordinateOperation[references.length]; // Intentional covariance. |
| } else { |
| throw new FactoryException(Resources.format(Resources.Keys.CanNotCombineUriAsType_1, type)); |
| } |
| final String expected = NameMeaning.toObjectType(componentType); // Note: "compound-crs" ⟶ "crs". |
| for (int i=0; i<references.length; i++) { |
| final DefinitionURI ref = references[i]; |
| final IdentifiedObject component = createObject(ref.toString()); |
| if (componentType != null && (!componentType.isInstance(component) || !expected.equalsIgnoreCase(ref.type))) { |
| componentType = null; |
| components = Arrays.copyOf(components, components.length, IdentifiedObject[].class); |
| } |
| components[i] = component; |
| } |
| /* |
| * At this point we have successfully created all components. The way to interpret those components |
| * depends mostly on the type of object requested by the user. For a given requested type, different |
| * rules apply depending on the type of components. Those rules are described in OGC 07-092r1 (2007): |
| * "Definition identifier URNs in OGC namespace". |
| */ |
| IdentifiedObject combined = null; |
| switch (requestedType) { |
| case AuthorityFactoryIdentifier.OPERATION: { |
| if (componentType != null) { |
| /* |
| * URN combined references for concatenated operations. We build an operation name from |
| * the operation identifiers (rather than CRS identifiers) because this is what the user |
| * gave to us, and because source/target CRS are not guaranteed to be defined. We do not |
| * yet support swapping roles of source and target CRS if an implied-reverse coordinate |
| * operation is included. |
| */ |
| final CoordinateOperation[] ops = (CoordinateOperation[]) components; |
| String name = IdentifiedObjects.getIdentifierOrName(ops[0]) + " ⟶ " |
| + IdentifiedObjects.getIdentifierOrName(ops[ops.length - 1]); |
| combined = DefaultFactories.forBuildin(CoordinateOperationFactory.class) |
| .createConcatenatedOperation(Collections.singletonMap(CoordinateOperation.NAME_KEY, name), ops); |
| } |
| break; |
| } |
| case AuthorityFactoryIdentifier.CRS: { |
| if (componentType != null) { |
| /* |
| * URN combined references for compound coordinate reference systems. |
| * The URNs of the individual well-known CRSs are listed in the same order in which the |
| * individual coordinate tuples are combined to form the CompoundCRS coordinate tuple. |
| */ |
| combined = CRS.compound((CoordinateReferenceSystem[]) components); |
| } else if (!isHTTP) { |
| final CoordinateSystem cs = remove(references, components, CoordinateSystem.class); |
| if (cs != null) { |
| final Datum datum = remove(references, components, Datum.class); |
| if (datum != null) { |
| /* |
| * URN combined references for datum and coordinate system. In this case, the URN shall |
| * concatenate the URNs of one well-known datum and one well-known coordinate system. |
| */ |
| if (ArraysExt.allEquals(references, null)) { |
| combined = combine((GeodeticDatum) datum, cs); |
| } |
| } else { |
| /* |
| * URN combined references for projected or derived CRSs. In this case, the URN shall |
| * concatenate the URNs of the one well-known CRS, one well-known Conversion, and one |
| * well-known CartesianCS. Similar action can be taken for derived CRS. |
| */ |
| CoordinateReferenceSystem baseCRS = remove(references, components, CoordinateReferenceSystem.class); |
| CoordinateOperation op = remove(references, components, CoordinateOperation.class); |
| if (ArraysExt.allEquals(references, null) && op instanceof Conversion) { |
| combined = combine(baseCRS, (Conversion) op, cs); |
| } |
| } |
| } |
| } |
| break; |
| } |
| } |
| /* |
| * At this point the combined object has been created if we know how to create it. |
| * Maybe the result matches the definition of an existing object in the database, |
| * in which case we will use the existing definition for better metadata. |
| */ |
| if (combined == null) { |
| throw new FactoryException(Resources.format(Resources.Keys.UnexpectedComponentInURI)); |
| } |
| final IdentifiedObject existing = newIdentifiedObjectFinder().findSingleton(combined); |
| return type.cast(existing != null ? existing : combined); |
| } |
| |
| /** |
| * If the given {@code type} is found in the given {@code references}, sets that reference element to {@code null} |
| * and returns the corresponding {@code components} element. Otherwise returns {@code null}. This is equivalent to |
| * {@link Map#remove(Object, Object)} where {@code references} are the keys and {@code components} are the values. |
| * We do not bother building that map because the arrays are very short (2 or 3 elements). |
| */ |
| private static <T> T remove(final DefinitionURI[] references, final IdentifiedObject[] components, final Class<T> type) { |
| final String expected = NameMeaning.toObjectType(type); |
| for (int i=0; i<references.length; i++) { |
| final DefinitionURI ref = references[i]; |
| if (ref != null && expected.equalsIgnoreCase(ref.type)) { |
| references[i] = null; |
| return type.cast(components[i]); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Invoked when a {@code createFoo(…)} method is given a combined URI containing a datum and a coordinate system. |
| * If the given information are not sufficient or not applicable, then this method returns {@code null}. |
| * |
| * @param datum the datum, or {@code null} if missing. |
| * @param cs the coordinate system (never null). |
| * @return the combined CRS, or {@code null} if the given information are not sufficient. |
| * @throws FactoryException if an error occurred while creating the combined CRS. |
| */ |
| private static GeodeticCRS combine(final GeodeticDatum datum, final CoordinateSystem cs) throws FactoryException { |
| final Map<String,?> properties = IdentifiedObjects.getProperties(datum, Datum.IDENTIFIERS_KEY); |
| final CRSFactory factory = DefaultFactories.forBuildin(CRSFactory.class); |
| if (datum instanceof GeodeticDatum) { |
| if (cs instanceof EllipsoidalCS) { |
| return factory.createGeographicCRS(properties, datum, (EllipsoidalCS) cs); |
| } else if (cs instanceof SphericalCS) { |
| return factory.createGeocentricCRS(properties, datum, (SphericalCS) cs); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Invoked when a {@code createFoo(…)} method is given a combined URI containing a conversion and a coordinate |
| * system. If the given information are not sufficient or not applicable, then this method returns {@code null}. |
| * |
| * @param baseCRS the CRS on which the derived CRS will be based on, or {@code null} if missing. |
| * @param fromBase the conversion from {@code baseCRS} to the CRS to be created by this method. |
| * @param cs the coordinate system (never null). |
| * @return the combined CRS, or {@code null} if the given information are not sufficient. |
| * @throws FactoryException if an error occurred while creating the combined CRS. |
| */ |
| private static GeneralDerivedCRS combine(final CoordinateReferenceSystem baseCRS, final Conversion fromBase, |
| final CoordinateSystem cs) throws FactoryException |
| { |
| if (baseCRS != null && fromBase.getSourceCRS() == null && fromBase.getTargetCRS() == null) { |
| final Map<String,?> properties = IdentifiedObjects.getProperties(fromBase, Datum.IDENTIFIERS_KEY); |
| final CRSFactory factory = DefaultFactories.forBuildin(CRSFactory.class); |
| if (baseCRS instanceof GeographicCRS && cs instanceof CartesianCS) { |
| return factory.createProjectedCRS(properties, (GeographicCRS) baseCRS, fromBase, (CartesianCS) cs); |
| } else { |
| return factory.createDerivedCRS(properties, baseCRS, fromBase, cs); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Creates a finder which can be used for looking up unidentified objects. |
| * The default implementation delegates the lookups to the underlying factories. |
| * |
| * @return a finder to use for looking up unidentified objects. |
| * @throws FactoryException if the finder can not be created. |
| */ |
| @Override |
| public IdentifiedObjectFinder newIdentifiedObjectFinder() throws FactoryException { |
| return new Finder(this); |
| } |
| |
| /** |
| * A {@link IdentifiedObjectFinder} which tests every factories declared in the |
| * {@linkplain MultiAuthoritiesFactory#getAllFactories() collection of factories}. |
| */ |
| private static class Finder extends IdentifiedObjectFinder { |
| /** |
| * The finders of all factories, or {@code null} if not yet fetched. |
| * We will create this array only when first needed in order to avoid instantiating the factories |
| * before needed (for example we may be able to find an object using only its code). However if we |
| * need to create this array, then we will create it fully (for all factories at once). |
| */ |
| private IdentifiedObjectFinder[] finders; |
| |
| /** |
| * Creates a new finder. |
| */ |
| protected Finder(final MultiAuthoritiesFactory factory) throws FactoryException { |
| super(factory); |
| } |
| |
| /** |
| * Sets the domain of the search (for example whether to include deprecated objects in the search). |
| */ |
| @Override |
| public void setSearchDomain(final Domain domain) { |
| super.setSearchDomain(domain); |
| if (finders != null) { |
| for (final IdentifiedObjectFinder finder : finders) { |
| finder.setSearchDomain(domain); |
| } |
| } |
| } |
| |
| /** |
| * Sets whether the search should ignore coordinate system axes. |
| */ |
| @Override |
| public void setIgnoringAxes(final boolean ignore) { |
| super.setIgnoringAxes(ignore); |
| if (finders != null) { |
| for (final IdentifiedObjectFinder finder : finders) { |
| finder.setIgnoringAxes(ignore); |
| } |
| } |
| } |
| |
| /** |
| * Delegates to every factories registered in the enclosing {@link MultiAuthoritiesFactory}, |
| * in iteration order. This method is invoked only if the parent class failed to find the |
| * object by its identifiers and by its name. At this point, as a last resource, we will |
| * scan over the objects in the database. |
| * |
| * <p>This method shall <strong>not</strong> delegate the job to the parent class, as the default |
| * implementation in the parent class is very inefficient. We need to delegate to the finders of |
| * all factories, so we can leverage their potentially more efficient algorithms.</p> |
| */ |
| @Override |
| final Set<IdentifiedObject> createFromCodes(final IdentifiedObject object) throws FactoryException { |
| if (finders == null) try { |
| final ArrayList<IdentifiedObjectFinder> list = new ArrayList<>(); |
| final Map<AuthorityFactory,Boolean> unique = new IdentityHashMap<>(); |
| final Iterator<AuthorityFactory> it = ((MultiAuthoritiesFactory) factory).getAllFactories(); |
| while (it.hasNext()) { |
| final AuthorityFactory candidate = it.next(); |
| if (candidate instanceof GeodeticAuthorityFactory && unique.put(candidate, Boolean.TRUE) == null) { |
| IdentifiedObjectFinder finder = ((GeodeticAuthorityFactory) candidate).newIdentifiedObjectFinder(); |
| if (finder != null) { // Should never be null according method contract, but we are paranoiac. |
| finder.ignoreIdentifiers = true; |
| finder.setWrapper(this); |
| list.add(finder); |
| } |
| } |
| } |
| finders = list.toArray(new IdentifiedObjectFinder[list.size()]); |
| } catch (BackingStoreException e) { |
| throw e.unwrapOrRethrow(FactoryException.class); |
| } |
| final Set<IdentifiedObject> found = new LinkedHashSet<>(); |
| for (final IdentifiedObjectFinder finder : finders) { |
| found.addAll(finder.find(object)); |
| } |
| return found; |
| } |
| } |
| |
| /** |
| * Clears the cache and notifies this {@code MultiAuthoritiesFactory} that all factories will need to |
| * be fetched again from the providers given at construction time. In addition, all providers that are |
| * instances of {@link ServiceLoader} will have their {@link ServiceLoader#reload() reload()} method invoked. |
| * |
| * <p>This method is intended for use in situations in which new factories can be installed into a running |
| * Java virtual machine.</p> |
| */ |
| public void reload() { |
| for (int type=0; type < providers.length; type++) { |
| final Iterable<?> provider = providers[type]; |
| if (provider != null) { |
| synchronized (provider) { |
| if (provider instanceof LazySet<?>) { |
| ((LazySet<?>) provider).reload(); |
| } |
| if (provider instanceof ServiceLoader<?>) { |
| ((ServiceLoader<?>) provider).reload(); |
| } |
| /* |
| * Clear the 'iterationCompleted' bit before to clear the cache so that if another thread |
| * invokes 'getAuthorityFactory(…)', it will block on the synchronized(provider) statement |
| * until we finished the cleanup. |
| */ |
| applyAndMask(~(1 << type)); |
| /* |
| * Clear the cache on a provider-by-provider basis, not by a call to factories.clear(). |
| * This is needed because this MultiAuthoritiesFactory instance may be used concurrently |
| * by other threads, and we have no global lock for the whole factory. |
| */ |
| final Iterator<AuthorityFactoryIdentifier> it = factories.keySet().iterator(); |
| while (it.hasNext()) { |
| if (it.next().type == type) { |
| it.remove(); |
| } |
| } |
| } |
| } |
| } |
| applyAndMask(providers.length - 1); // Clears all bits other than the bits for providers. |
| } |
| |
| /** |
| * Sets {@link #isIterationCompleted} to {@code iterationCompleted & mask}. |
| * This is used by {@link #reload()} for clearing bits. |
| */ |
| private void applyAndMask(final int mask) { |
| int value; |
| do value = isIterationCompleted.get(); |
| while (!isIterationCompleted.compareAndSet(value, value & mask)); |
| } |
| } |