Package org.apache.sis.referencing

Source Code of org.apache.sis.referencing.IdentifiedObjects

/*
* 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;

import java.util.Map;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.Collection;

import org.opengis.util.NameSpace;
import org.opengis.util.GenericName;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.ReferenceIdentifier;
import org.opengis.referencing.operation.CoordinateOperation;

import org.apache.sis.util.Static;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.iso.DefaultNameSpace;
import org.apache.sis.metadata.iso.citation.Citations; // For javadoc.

import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
import static org.apache.sis.util.Characters.Filter.LETTERS_AND_DIGITS;
import static org.apache.sis.internal.util.Citations.iterator;
import static org.apache.sis.internal.util.Citations.identifierMatches;


/**
* Utility methods working on arbitrary implementations of the {@link IdentifiedObject} interface.
*
* @author  Martin Desruisseaux (IRD, Geomatys)
* @author  Guilhem Legal (Geomatys)
* @since   0.4 (derived from geotk-1.2)
* @version 0.4
* @module
*
* @see CRS
* @see org.apache.sis.geometry.Envelopes
*/
public final class IdentifiedObjects extends Static {
    /**
     * Do not allows instantiation of this class.
     */
    private IdentifiedObjects() {
    }

    /**
     * Returns the information provided in the specified identified object as a map of properties.
     * The returned map contains the following entries for each key not contained in the {@code excludes} list
     * and for which the corresponding method returns a non-null and non-empty value.
     *
     * <table class="sis">
     *   <tr><th>Key</th> <th>Value</th></tr>
     *   <tr><td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
     *       <td>{@link IdentifiedObject#getName()}</td></tr>
     *   <tr><td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td>
     *       <td>{@link IdentifiedObject#getAlias()}</td></tr>
     *   <tr><td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td>
     *       <td>{@link IdentifiedObject#getIdentifiers()}</td></tr>
     *   <tr><td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td>
     *       <td>{@link IdentifiedObject#getRemarks()}</td></tr>
     *   <tr><td>{@value org.opengis.referencing.operation.CoordinateOperation#SCOPE_KEY}</td>
     *       <td>{@link CoordinateOperation#getScope()} (also in datum and reference systems)</td></tr>
     *   <tr><td>{@value org.opengis.referencing.operation.CoordinateOperation#DOMAIN_OF_VALIDITY_KEY}</td>
     *       <td>{@link CoordinateOperation#getDomainOfValidity()} (also in datum and reference systems)</td></tr>
     *   <tr><td>{@value org.opengis.referencing.operation.CoordinateOperation#OPERATION_VERSION_KEY}</td>
     *       <td>{@link CoordinateOperation#getOperationVersion()}</td></tr>
     *   <tr><td>{@value org.opengis.referencing.operation.CoordinateOperation#COORDINATE_OPERATION_ACCURACY_KEY}</td>
     *       <td>{@link CoordinateOperation#getCoordinateOperationAccuracy()}</td></tr>
     * </table>
     *
     * @param  object The identified object to view as a properties map.
     * @param  excludes The keys of properties to exclude from the map.
     * @return An view of the identified object as an immutable map.
     */
    public static Map<String,?> getProperties(final IdentifiedObject object, final String... excludes) {
        ensureNonNull("object", object);
        ensureNonNull("excludes", excludes);
        return new Properties(object, excludes);
    }

    /**
     * Returns every object names and aliases according the given authority. This method performs
     * the same work than {@link #getName(IdentifiedObject, Citation)}, except that it does not
     * stop at the first match. This method is useful in the rare cases where the same authority
     * declares more than one name, and all those names are of interest.
     *
     * @param  object The object to get the names and aliases from, or {@code null}.
     * @param  authority The authority for the names to return, or {@code null} for any authority.
     * @return The object's names and aliases, or an empty set if no name or alias matching the
     *         specified authority has been found.
     */
    public static Set<String> getNames(final IdentifiedObject object, final Citation authority) {
        final Set<String> names = new LinkedHashSet<String>(8);
        getName(object, authority, names);
        return names;
    }

    /**
     * Returns an object name according the given authority.
     * This method checks first the {@linkplain AbstractIdentifiedObject#getName() primary name},
     * then all {@linkplain AbstractIdentifiedObject#getAlias() aliases} in their iteration order.
     *
     * <ul>
     *   <li><p>If the name or alias implements the {@link ReferenceIdentifier} interface,
     *       then this method compares the {@linkplain ReferenceIdentifier#getAuthority()
     *       identifier authority} against the specified citation using the
     *       {@link Citations#identifierMatches(Citation, Citation)} method.
     *       If a matching is found, then this method returns the
     *       {@linkplain ReferenceIdentifier#getCode() identifier code} of that object.</p></li>
     *
     *   <li><p>Otherwise, if the alias implements the {@link GenericName} interface, then this method
     *       compares the {@linkplain GenericName#scope() name scope} against the specified citation
     *       using the {@link Citations#identifierMatches(Citation, String)} method.
     *       If a matching is found, then this method returns the
     *       {@linkplain GenericName#tip() name tip} of that object.</p></li>
     * </ul>
     *
     * Note that alias may implement both the {@link ReferenceIdentifier} and {@link GenericName}
     * interfaces (for example {@link NamedIdentifier}). In such cases, the identifier view has
     * precedence.
     *
     * @param  object The object to get the name from, or {@code null}.
     * @param  authority The authority for the name to return, or {@code null} for any authority.
     * @return The object's name (either an {@linkplain ReferenceIdentifier#getCode() identifier code}
     *         or a {@linkplain GenericName#tip() name tip}), or {@code null} if no name matching the
     *         specified authority has been found.
     *
     * @see AbstractIdentifiedObject#getName()
     */
    public static String getName(final IdentifiedObject object, final Citation authority) {
        return getName(object, authority, null);
    }

    /**
     * Returns an object name according the given authority. This method is {@code null}-safe:
     * every properties are checked for null values, even the properties that are supposed to
     * be mandatory (not all implementation defines all mandatory values).
     *
     * @param  object    The object to get the name from, or {@code null}.
     * @param  authority The authority for the name to return, or {@code null} for any authority.
     * @param  addTo     If non-null, the collection where to add all names found.
     * @return The object's name (either an {@linkplain ReferenceIdentifier#getCode() identifier code}
     *         or a {@linkplain GenericName#tip() name tip}), or {@code null} if no name matching the
     *         specified authority has been found.
     */
    private static String getName(final IdentifiedObject object, final Citation authority, final Collection<String> addTo) {
        if (object != null) {
            Identifier identifier = object.getName();
            if (authority == null) {
                if (identifier != null) {
                    final String name = identifier.getCode();
                    if (name != null) {
                        if (addTo == null) {
                            return name;
                        }
                        addTo.add(name);
                    }
                }
                final Iterator<GenericName> it = iterator(object.getAlias());
                if (it != null) while (it.hasNext()) {
                    final GenericName alias = it.next();
                    if (alias != null) {
                        final String name = (alias instanceof Identifier) ?
                                ((Identifier) alias).getCode() : alias.toString();
                        if (name != null) {
                            if (addTo == null) {
                                return name;
                            }
                            addTo.add(name);
                        }
                    }
                }
            } else {
                if (identifier != null) {
                    if (identifierMatches(authority, identifier.getAuthority())) {
                        final String name = identifier.getCode();
                        if (name != null) {
                            if (addTo == null) {
                                return name;
                            }
                            addTo.add(name);
                        }
                    }
                }
                final Iterator<GenericName> it = iterator(object.getAlias());
                if (it != null) while (it.hasNext()) {
                    final GenericName alias = it.next();
                    if (alias != null) {
                        if (alias instanceof Identifier) {
                            identifier = (Identifier) alias;
                            if (identifierMatches(authority, identifier.getAuthority())) {
                                final String name = identifier.getCode();
                                if (name != null) {
                                    if (addTo == null) {
                                        return name;
                                    }
                                    addTo.add(name);
                                }
                            }
                        } else {
                            final NameSpace ns = alias.scope();
                            if (ns != null) {
                                final GenericName scope = ns.name();
                                if (scope != null) {
                                    if (identifierMatches(authority, scope.toString())) {
                                        final String name = alias.toString();
                                        if (name != null) {
                                            if (addTo == null) {
                                                return name;
                                            }
                                            addTo.add(name);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Returns an identifier for the given object according the given authority.
     * This method checks all {@linkplain AbstractIdentifiedObject#getIdentifiers() identifiers} in their iteration
     * order and returns the first identifier with an {@linkplain NamedIdentifier#getAuthority() authority} citation
     * {@linkplain Citations#identifierMatches(Citation, Citation) matching} the specified authority.
     *
     * @param  object The object to get the identifier from, or {@code null}.
     * @param  authority The authority for the identifier to return, or {@code null} for
     *         the first identifier regardless its authority.
     * @return The object's identifier, or {@code null} if no identifier matching the specified authority
     *         has been found.
     *
     * @see AbstractIdentifiedObject#getIdentifier()
     */
    public static ReferenceIdentifier getIdentifier(final IdentifiedObject object, final Citation authority) {
        if (object != null) {
            final Iterator<ReferenceIdentifier> it = iterator(object.getIdentifiers());
            if (it != null) while (it.hasNext()) {
                final ReferenceIdentifier identifier = it.next();
                if (identifier != null) { // Paranoiac check.
                    if (authority == null || identifierMatches(authority, identifier.getAuthority())) {
                        return identifier;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Returns the string representation of the first identifier, or the object name if there is no identifier.
     * This method searches for the first non-null element in
     * <code>object.{@linkplain AbstractIdentifiedObject#getIdentifiers() getIdentifiers()}</code>. If there is none,
     * then this method fallback on <code>object.{@linkplain AbstractIdentifiedObject#getName() getName()}</code>.
     * The first element found is formatted by {@link #toString(Identifier)}.
     *
     * {@section Recommanded alternatives}
     * <ul>
     *   <li>If the code of a specific authority is wanted (typically EPSG), then consider
     *       using {@link #getIdentifier(IdentifiedObject, Citation)} instead.</li>
     *   <li>In many cases, the identifier is not specified. For an exhaustive scan of the EPSG
     *       database looking for a match, use one of the search methods defined below.</li>
     * </ul>
     *
     * @param  object The identified object, or {@code null}.
     * @return A string representation of the first identifier or name, or {@code null} if none.
     *
     * @see #getIdentifier(IdentifiedObject, Citation)
     * @see #searchIdentifierCode(IdentifiedObject, boolean)
     */
    public static String getIdentifierOrName(final IdentifiedObject object) {
        if (object != null) {
            final Iterator<ReferenceIdentifier> it = iterator(object.getIdentifiers());
            if (it != null) while (it.hasNext()) {
                final String code = toString(it.next());
                if (code != null) { // Paranoiac check.
                    return code;
                }
            }
            final String name = toString(object.getName());
            if (name != null) { // Paranoiac check.
                return name;
            }
        }
        return null;
    }

    /**
     * Returns the first name, alias or identifier which is a
     * {@linkplain CharSequences#isUnicodeIdentifier(CharSequence) valid Unicode identifier}.
     * This method performs the search in the following order:
     *
     * <ul>
     *   <li><code>object.{@linkplain AbstractIdentifiedObject#getName() getName()}</code></li>
     *   <li><code>object.{@linkplain AbstractIdentifiedObject#getAlias() getAlias()}</code> in iteration order</li>
     *   <li><code>object.{@linkplain AbstractIdentifiedObject#getIdentifiers() getIdentifiers()}</code> in iteration order</li>
     * </ul>
     *
     * @param  object The identified object, or {@code null}.
     * @return The first name, alias or identifier which is a valid Unicode identifier, or {@code null} if none.
     */
    public static String getUnicodeIdentifier(final IdentifiedObject object) {
        if (object != null) {
            ReferenceIdentifier identifier = object.getName();
            if (identifier != null) { // Paranoiac check.
                final String code = identifier.getCode();
                if (CharSequences.isUnicodeIdentifier(code)) {
                    return code;
                }
            }
            final Iterator<GenericName> it = iterator(object.getAlias());
            if (it != null) while (it.hasNext()) {
                GenericName alias = it.next();
                if (alias != null && (alias = alias.tip()) != null) {
                    final String code = alias.toString();
                    if (CharSequences.isUnicodeIdentifier(code)) {
                        return code;
                    }
                }
            }
            final Iterator<ReferenceIdentifier> id = iterator(object.getIdentifiers());
            if (id != null) while (id.hasNext()) {
                identifier = id.next();
                if (identifier != null) { // Paranoiac check.
                    final String code = identifier.getCode();
                    if (CharSequences.isUnicodeIdentifier(code)) {
                        return code;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Returns {@code true} if either the {@linkplain AbstractIdentifiedObject#getName() primary name} or at least
     * one {@linkplain AbstractIdentifiedObject#getAlias() alias} matches the given string according heuristic rules.
     * If the given object is an instance of {@link AbstractIdentifiedObject}, then this method delegates to its
     * {@link AbstractIdentifiedObject#isHeuristicMatchForName(String) isHeuristicMatchForName(String)} method
     * in order to leverage the additional rules implemented by sub-classes.
     * Otherwise the fallback implementation returns {@code true} if the given {@code name} is equal,
     * ignoring aspects documented below, to one of the following names:
     *
     * <ul>
     *   <li>The {@linkplain AbstractIdentifiedObject#getName() primary name}'s {@linkplain NamedIdentifier#getCode() code}
     *       (without {@linkplain NamedIdentifier#getCodeSpace() codespace}).</li>
     *   <li>Any {@linkplain AbstractIdentifiedObject#getAlias() alias}'s {@linkplain NamedIdentifier#tip() tip}
     *       (without {@linkplain NamedIdentifier#scope() scope} and namespace).</li>
     * </ul>
     *
     * The comparison ignores the following aspects:
     * <ul>
     *   <li>Lower/upper cases.</li>
     *   <li>Some Latin diacritical signs (e.g. {@code "Réunion"} and {@code "Reunion"} are considered equal).</li>
     *   <li>All characters that are not {@linkplain Character#isLetterOrDigit(int) letters or digits}
     *       (e.g. {@code "Mercator (1SP)"} and {@code "Mercator_1SP"} are considered equal).</li>
     *   <li>Namespaces or scopes, because this method is typically invoked with either the value of an other
     *       <code>IdentifiedObject.getName().getCode()</code> or with the <cite>Well Known Text</cite> (WKT)
     *       projection or parameter name.</li>
     * </ul>
     *
     * @param  object The object for which to check the name or alias.
     * @param  name The name to compare with the object name or aliases.
     * @return {@code true} if the primary name of at least one alias matches the specified {@code name}.
     *
     * @see AbstractIdentifiedObject#isHeuristicMatchForName(String)
     */
    public static boolean isHeuristicMatchForName(final IdentifiedObject object, final String name) {
        if (object instanceof AbstractIdentifiedObject) {
            // DefaultCoordinateSystemAxis overrides this method.
            // We really need to delegate to the overridden method.
            return ((AbstractIdentifiedObject) object).isHeuristicMatchForName(name);
        } else {
            ensureNonNull("object", object);
            return isHeuristicMatchForName(object, object.getAlias(), name);
        }
    }

    /**
     * Returns {@code true} if the {@linkplain AbstractIdentifiedObject#getName() primary name} of the given object
     * or one of the given alias matches the given name. The comparison ignores case, some Latin diacritical signs
     * and any characters that are not letters or digits.
     *
     * @param  object  The object to check.
     * @param  aliases The list of alias in {@code object} (may be {@code null}).
     *                 This method will never modify this list. Consequently, the
     *                 given list can be a direct reference to an internal list.
     * @param  name    The name for which to check for equality.
     * @return {@code true} if the primary name or at least one alias matches the given {@code name}.
     */
    static boolean isHeuristicMatchForName(final IdentifiedObject object, final Collection<GenericName> aliases,
            CharSequence name)
    {
        name = CharSequences.toASCII(name);
        final ReferenceIdentifier id = object.getName();
        if (id != null) { // Paranoiac check.
            final CharSequence code = CharSequences.toASCII(id.getCode());
            if (code != null) { // Paranoiac check.
                if (CharSequences.equalsFiltered(name, code, LETTERS_AND_DIGITS, true)) {
                    return true;
                }
            }
        }
        if (aliases != null) {
            for (final GenericName alias : aliases) {
                if (alias != null) { // Paranoiac check.
                    final CharSequence tip = CharSequences.toASCII(alias.tip().toString());
                    if (CharSequences.equalsFiltered(name, tip, LETTERS_AND_DIGITS, true)) {
                        return true;
                    }
                    /*
                     * Note: a previous version compared also the scoped names. We removed that part,
                     * because experience has shown that this method is used only for the "code" part
                     * of an object name. If we really want to compare scoped name, it would probably
                     * be better to take a GenericName argument instead than String.
                     */
                }
            }
        }
        return false;
    }

    /**
     * Returns a string representation of the given identifier.
     * This method applies the following rules:
     *
     * <ul>
     *   <li>If the given identifier implements the {@link GenericName} interface,
     *       then this method delegates to the {@link GenericName#toString()} method.</li>
     *   <li>Otherwise if the given identifier has a {@linkplain ReferenceIdentifier#getCodeSpace() code space},
     *       then formats the identifier as "{@code codespace:code}".</li>
     *   <li>Otherwise if the given identifier has an {@linkplain Identifier#getAuthority() authority},
     *       then formats the identifier as "{@code authority:code}".</li>
     *   <li>Otherwise returns the {@linkplain Identifier#getCode() identifier code}.</li>
     * </ul>
     *
     * This method is provided because the {@link GenericName#toString()} behavior is specified by its javadoc,
     * while {@link ReferenceIdentifier} has no such contract. For example like most ISO 19115 objects in SIS,
     * the {@link org.apache.sis.metadata.iso.DefaultIdentifier} implementation is formatted as a tree.
     * This static method can be used when a "name-like" representation is needed for any implementation.
     *
     * @param  identifier The identifier, or {@code null}.
     * @return A string representation of the given identifier, or {@code null}.
     *
     * @see org.apache.sis.metadata.iso.ImmutableIdentifier#toString()
     * @see NamedIdentifier#toString()
     */
    public static String toString(final Identifier identifier) {
        if (identifier == null) {
            return null;
        }
        if (identifier instanceof GenericName) {
            // The toString() behavior is specified by the GenericName javadoc.
            return identifier.toString();
        }
        final String code = identifier.getCode();
        final String cs;
        if (identifier instanceof ReferenceIdentifier) {
            cs = ((ReferenceIdentifier) identifier).getCodeSpace();
        } else {
            cs = org.apache.sis.internal.util.Citations.getIdentifier(identifier.getAuthority());
        }
        if (cs != null && !cs.isEmpty()) {
            return cs + DefaultNameSpace.DEFAULT_SEPARATOR + code;
        }
        return code;
    }
}
TOP

Related Classes of org.apache.sis.referencing.IdentifiedObjects

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.