| /* |
| * 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.gazetteer; |
| |
| import java.util.Map; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.HashMap; |
| import javax.xml.bind.annotation.XmlTransient; |
| import org.opengis.util.InternationalString; |
| import org.apache.sis.referencing.AbstractReferenceSystem; |
| import org.apache.sis.util.collection.Containers; |
| import org.apache.sis.util.ComparisonMode; |
| import org.apache.sis.util.Utilities; |
| import org.apache.sis.util.iso.Types; |
| import org.apache.sis.util.Debug; |
| import org.apache.sis.io.wkt.Formatter; |
| import org.apache.sis.io.wkt.ElementKind; |
| import org.apache.sis.metadata.iso.extent.Extents; |
| import org.apache.sis.internal.referencing.WKTUtilities; |
| import org.apache.sis.io.wkt.FormattableObject; |
| import org.apache.sis.util.resources.Vocabulary; |
| |
| // Branch-dependent imports |
| import org.opengis.metadata.citation.Party; |
| import org.opengis.referencing.gazetteer.LocationType; |
| import org.opengis.referencing.gazetteer.ReferenceSystemUsingIdentifiers; |
| |
| |
| /** |
| * Base class of reference systems that describe locations using geographic identifiers instead than coordinates. |
| * |
| * <h2>Immutability and thread safety</h2> |
| * This base class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself) |
| * given to the constructor are also immutable. Most SIS subclasses and related classes are immutable under similar |
| * conditions. This means that unless otherwise noted in the javadoc, {@code ReferencingByIdentifiers} instances |
| * created using only SIS factories and static constants can be shared by many objects and passed between threads |
| * without synchronization. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 0.8 |
| * |
| * @see ModifiableLocationType |
| * @see AbstractLocation |
| * |
| * @since 0.8 |
| * @module |
| */ |
| @XmlTransient |
| public class ReferencingByIdentifiers extends AbstractReferenceSystem implements ReferenceSystemUsingIdentifiers { |
| /** |
| * Serial number for inter-operability with different versions. |
| */ |
| private static final long serialVersionUID = 5353942546043471933L; |
| |
| /** |
| * Property used to characterize the spatial reference system. |
| * |
| * @see #getTheme() |
| */ |
| private final InternationalString theme; |
| |
| /** |
| * Authority with overall responsibility for the spatial reference system. |
| * |
| * @see #getOverallOwner() |
| */ |
| private final Party overallOwner; |
| |
| /** |
| * Description of location type(s) in the spatial reference system. |
| * This collection shall be unmodifiable. |
| * |
| * @see #getLocationTypes() |
| */ |
| private final List<LocationType> locationTypes; |
| |
| /** |
| * Creates a reference system from the given properties. |
| * The properties given in argument follow the same rules than for the |
| * {@linkplain AbstractReferenceSystem#AbstractReferenceSystem(Map) super-class constructor}. |
| * The following table is a reminder of main (not all) properties: |
| * |
| * <table class="sis"> |
| * <caption>Recognized properties (non exhaustive list)</caption> |
| * <tr> |
| * <th>Property name</th> |
| * <th>Value type</th> |
| * <th>Returned by</th> |
| * </tr> |
| * <tr> |
| * <td>{@value org.opengis.referencing.gazetteer.ReferenceSystemUsingIdentifiers#THEME_KEY}</td> |
| * <td>{@link String} or {@link InternationalString}</td> |
| * <td>{@link #getTheme()}</td> |
| * </tr> |
| * <tr> |
| * <td>{@value org.opengis.referencing.gazetteer.ReferenceSystemUsingIdentifiers#OVERALL_OWNER_KEY}</td> |
| * <td>{@link Party}</td> |
| * <td>{@link #getOverallOwner()}</td> |
| * </tr> |
| * <tr> |
| * <th colspan="3" class="hsep">Defined in parent class (reminder)</th> |
| * </tr> |
| * <tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td> |
| * <td>{@link org.opengis.metadata.Identifier} or {@link String}</td> |
| * <td>{@link #getName()}</td> |
| * </tr> |
| * <tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td> |
| * <td>{@link org.opengis.util.GenericName} or {@link CharSequence} (optionally as array)</td> |
| * <td>{@link #getAlias()}</td> |
| * </tr> |
| * <tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td> |
| * <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td> |
| * <td>{@link #getIdentifiers()}</td> |
| * </tr> |
| * <tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td> |
| * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> |
| * <td>{@link #getRemarks()}</td> |
| * </tr> |
| * <tr> |
| * <td>{@value org.opengis.referencing.ReferenceSystem#DOMAIN_OF_VALIDITY_KEY}</td> |
| * <td>{@link org.opengis.metadata.extent.Extent}</td> |
| * <td>{@link #getDomainOfValidity()}</td> |
| * </tr> |
| * <tr> |
| * <td>{@value org.opengis.referencing.ReferenceSystem#SCOPE_KEY}</td> |
| * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> |
| * <td>{@link #getScope()}</td> |
| * </tr> |
| * </table> |
| * |
| * This constructor copies the given {@link LocationType} instances as per |
| * {@link ModifiableLocationType#snapshot(ReferenceSystemUsingIdentifiers, LocationType...)}. |
| * Changes in the given location types after construction will not affect this {@code ReferencingByIdentifiers}. |
| * |
| * @param properties the properties to be given to the reference system. |
| * @param types description of location type(s) in the spatial reference system. |
| */ |
| @SuppressWarnings("ThisEscapedInObjectConstruction") |
| public ReferencingByIdentifiers(final Map<String,?> properties, final LocationType... types) { |
| super(properties); |
| theme = Types.toInternationalString(properties, THEME_KEY); |
| overallOwner = Containers.property(properties, OVERALL_OWNER_KEY, Party.class); |
| /* |
| * Having the 'this' reference escaped in object construction should not be an issue here because |
| * we invoke package-private method in such a way that if an exception is thrown, the whole tree |
| * (with all 'this' references) will be discarded. |
| */ |
| locationTypes = AbstractLocationType.snapshot(this, types); |
| } |
| |
| /** |
| * Convenience method for helping subclasses to build their argument for the constructor. |
| * The returned properties have the domain of validity set to the whole word and the theme to "mapping". |
| * |
| * @param name the reference system name as an {@link org.opengis.metadata.Identifier} or a {@link String}. |
| * @param party the overall owner, or {@code null} if none. |
| */ |
| static Map<String,Object> properties(final Object name, final Party party) { |
| final Map<String,Object> properties = new HashMap<>(6); |
| properties.put(NAME_KEY, name); |
| properties.put(DOMAIN_OF_VALIDITY_KEY, Extents.WORLD); |
| properties.put(THEME_KEY, Vocabulary.formatInternational(Vocabulary.Keys.Mapping)); |
| properties.put(OVERALL_OWNER_KEY, party); |
| return properties; |
| } |
| |
| /** |
| * Returns the GeoAPI interface implemented by this class. |
| * The default implementation returns {@code ReferenceSystemUsingIdentifiers.class}. |
| * |
| * @return the GeoAPI interface implemented by this class. |
| */ |
| @Override |
| public Class<? extends ReferenceSystemUsingIdentifiers> getInterface() { |
| return ReferenceSystemUsingIdentifiers.class; |
| } |
| |
| /** |
| * Property used to characterize the spatial reference system. |
| * |
| * @return property used to characterize the spatial reference system. |
| * |
| * @see ModifiableLocationType#getTheme() |
| */ |
| @Override |
| public InternationalString getTheme() { |
| return theme; |
| } |
| |
| /** |
| * Authority with overall responsibility for the spatial reference system. |
| * |
| * @return authority with overall responsibility for the spatial reference system. |
| * |
| * @see ModifiableLocationType#getOwner() |
| * @see AbstractLocation#getAdministrator() |
| */ |
| @Override |
| public Party getOverallOwner() { |
| return overallOwner; |
| } |
| |
| /** |
| * Description of location type(s) in the spatial reference system. |
| * The collection returned by this method is unmodifiable. |
| * |
| * @return description of location type(s) in the spatial reference system. |
| * |
| * @see ModifiableLocationType#getReferenceSystem() |
| */ |
| @Override |
| @SuppressWarnings("ReturnOfCollectionOrArrayField") // Because the collection is unmodifiable. |
| public List<? extends LocationType> getLocationTypes() { |
| return locationTypes; |
| } |
| |
| /** |
| * Returns the first location type. |
| */ |
| final LocationType rootType() { |
| return locationTypes.get(0); |
| } |
| |
| /** |
| * Compares this reference system with the specified object for equality. |
| * If the {@code mode} argument value is {@link ComparisonMode#STRICT STRICT} or |
| * {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}, then all available properties are |
| * compared including the {@linkplain #getTheme() theme} and |
| * the {@linkplain #getOverallOwner() overall owner}. |
| * |
| * @param object the object to compare to {@code this}. |
| * @param mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or |
| * {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only |
| * properties relevant to location identifications. |
| * @return {@code true} if both objects are equal. |
| */ |
| @Override |
| @SuppressWarnings("fallthrough") |
| public boolean equals(final Object object, final ComparisonMode mode) { |
| if (!super.equals(object, mode)) { |
| return false; |
| } |
| switch (mode) { |
| case STRICT: { |
| final ReferencingByIdentifiers that = (ReferencingByIdentifiers) object; |
| return Objects.equals(theme, that.theme) && |
| Objects.equals(overallOwner, that.overallOwner) && |
| locationTypes.equals(that.locationTypes); |
| } |
| case BY_CONTRACT: { |
| final ReferenceSystemUsingIdentifiers that = (ReferenceSystemUsingIdentifiers) object; |
| if (!Utilities.deepEquals(getTheme(), that.getTheme(), mode) || |
| !Utilities.deepEquals(getOverallOwner(), that.getOverallOwner(), mode)) |
| { |
| return false; |
| } |
| // Fall through |
| } |
| default: { |
| // Theme and owner are metadata, so they can be ignored. |
| final ReferenceSystemUsingIdentifiers that = (ReferenceSystemUsingIdentifiers) object; |
| return Utilities.deepEquals(getLocationTypes(), that.getLocationTypes(), mode); |
| } |
| } |
| } |
| |
| /** |
| * Invoked by {@code hashCode()} for computing the hash code when first needed. |
| * See {@link org.apache.sis.referencing.AbstractIdentifiedObject#computeHashCode()} |
| * for more information. |
| * |
| * @return the hash code value. This value may change in any future Apache SIS version. |
| */ |
| @Override |
| protected long computeHashCode() { |
| return super.computeHashCode() + Objects.hash(theme, overallOwner, locationTypes); |
| } |
| |
| /** |
| * Formats a pseudo-<cite>Well Known Text</cite> (WKT) representation for this object. |
| * The format produced by this method is non-standard and may change in any future Apache SIS version. |
| * |
| * @param formatter the formatter where to format the inner content of this pseudo-WKT element. |
| * @return an arbitrary keyword for the pseudo-WKT element. |
| */ |
| @Debug |
| @Override |
| protected String formatTo(final Formatter formatter) { |
| WKTUtilities.appendName(this, formatter, ElementKind.NAME); |
| if (theme != null) { |
| formatter.newLine(); |
| formatter.append(new SubElement("Theme", theme)); |
| } |
| if (overallOwner != null) { |
| formatter.newLine(); |
| formatter.append(new SubElement("Owner", overallOwner.getName())); |
| } |
| for (final LocationType type : locationTypes) { |
| formatter.newLine(); |
| formatter.append(new SubElement("LocationType", type.getName())); |
| } |
| return "ReferenceSystemUsingIdentifiers"; |
| } |
| |
| /** |
| * A sub-element inside the pseudo-WKT. |
| */ |
| private static final class SubElement extends FormattableObject { |
| /** The pseudo-WKT name of the element to format. */ |
| private final String name; |
| |
| /** The value of the element to format. */ |
| private final InternationalString value; |
| |
| /** Creates a new citation with the given value. */ |
| SubElement(final String name, final InternationalString value) { |
| this.name = name; |
| this.value = value; |
| } |
| |
| /** Formats the sub-element. */ |
| @Override |
| protected String formatTo(final Formatter formatter) { |
| formatter.append(value != null ? value.toString(formatter.getLocale()) : null, null); |
| return name; |
| } |
| } |
| } |