sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1518844 - in /sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing: AbstractIdentifiedObject.java IdentifiedObjects.java
Date Thu, 29 Aug 2013 22:19:06 GMT
Author: desruisseaux
Date: Thu Aug 29 22:19:06 2013
New Revision: 1518844

URL: http://svn.apache.org/r1518844
Log:
Ported more code for AbstractIdentifiedObject.

Modified:
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1518844&r1=1518843&r2=1518844&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
[UTF-8] Thu Aug 29 22:19:06 2013
@@ -20,10 +20,15 @@ import java.util.Map;
 import java.util.Set;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.Locale;
 import java.io.Serializable;
+import javax.xml.bind.annotation.XmlID;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.Identifier;
@@ -32,8 +37,9 @@ import org.opengis.referencing.ObjectFac
 import org.opengis.referencing.AuthorityFactory;
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.ReferenceIdentifier;
-import org.opengis.parameter.InvalidParameterValueException;
 import org.apache.sis.io.wkt.FormattableObject;
+import org.apache.sis.xml.Namespaces;
+import org.apache.sis.util.Classes;
 import org.apache.sis.util.Immutable;
 import org.apache.sis.util.ThreadSafe;
 import org.apache.sis.util.Deprecable;
@@ -43,6 +49,7 @@ import org.apache.sis.util.iso.Types;
 import org.apache.sis.util.resources.Errors;
 
 import static org.apache.sis.util.ArgumentChecks.*;
+import static org.apache.sis.internal.util.Citations.iterator;
 import static org.apache.sis.internal.util.CollectionsExt.nonNull;
 import static org.apache.sis.internal.util.CollectionsExt.immutableSet;
 
@@ -89,6 +96,9 @@ public class AbstractIdentifiedObject ex
 
     /**
      * The name for this object or code. Should never be {@code null}.
+     *
+     * @see #getName()
+     * @see #getIdentifier()
      */
     @XmlElement
     private final ReferenceIdentifier name;
@@ -101,6 +111,9 @@ public class AbstractIdentifiedObject ex
     /**
      * An identifier which references elsewhere the object's defining information.
      * Alternatively an identifier by which this object can be referenced.
+     *
+     * @see #getIdentifiers()
+     * @see #getIdentifier()
      */
     private final Set<ReferenceIdentifier> identifiers;
 
@@ -132,7 +145,6 @@ public class AbstractIdentifiedObject ex
         alias       = nonNull(object.getAlias());
         identifiers = nonNull(object.getIdentifiers());
         remarks     =         object.getRemarks();
-        ensureNonNull("object.name", name);
     }
 
     /**
@@ -163,6 +175,11 @@ public class AbstractIdentifiedObject ex
      *     <td>{@link ReferenceIdentifier#getAuthority()} on the {@linkplain #getName()
name}</td>
      *   </tr>
      *   <tr>
+     *     <td>{@value org.opengis.referencing.ReferenceIdentifier#CODE_KEY}</td>
+     *     <td>{@link String}</td>
+     *     <td>{@link ReferenceIdentifier#getCode()} on the {@linkplain #getName()
name}</td>
+     *   </tr>
+     *   <tr>
      *     <td>{@value org.opengis.referencing.ReferenceIdentifier#CODESPACE_KEY}</td>
      *     <td>{@link String}</td>
      *     <td>{@link ReferenceIdentifier#getCodeSpace()} on the {@linkplain #getName()
name}</td>
@@ -188,37 +205,13 @@ public class AbstractIdentifiedObject ex
      * For example the {@code "remarks_fr"} property stands for remarks in {@linkplain Locale#FRENCH
French} and
      * the {@code "remarks_fr_CA"} property stands for remarks in {@linkplain Locale#CANADA_FRENCH
French Canadian}.
      *
-     * <p>Note that the {@code "authority"} and {@code "version"} properties are ignored
if the
-     * {@code "name"} property is already a {@link Citation} object instead than a {@link
String}.</p>
+     * <p>Note that the {@code "authority"} and {@code "version"} properties are ignored
if the {@code "name"}
+     * property is already a {@link ReferenceIdentifier} object instead than a {@link String}.</p>
      *
      * @param  properties The properties to be given to this identified object.
      * @throws IllegalArgumentException if a property has an invalid value.
      */
     public AbstractIdentifiedObject(final Map<String,?> properties) throws IllegalArgumentException
{
-        this(properties, null, null);
-    }
-
-    /**
-     * Constructs an object from a set of properties and copy unrecognized properties in
the given map.
-     * The {@code properties} argument is treated as in the
-     * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) one argument constructor}.
-     * All properties unknown to this {@code AbstractIdentifiedObject} constructor are copied
-     * in the {@code subProperties} map.
-     *
-     * <p>If {@code localizables} is non-null, then all keys listed in this argument
are treated as localizable one
-     * (i.e. may have a suffix like {@code "_fr"}, {@code "_de"}, <i>etc.</i>).
Localizable properties are stored in
-     * the {@code subProperties} map as {@link InternationalString} objects.</p>
-     *
-     * @param  properties    The properties to be given to this identified object.
-     * @param  subProperties The map in which to copy unrecognized properties, or {@code
null} if none.
-     * @param  localizables  Optional list of localized properties, or {@code null} if none.
-     * @throws IllegalArgumentException if a property has an invalid value.
-     */
-    protected AbstractIdentifiedObject(final Map<String,?>      properties,
-                                       final Map<String,Object> subProperties,
-                                       final String[]           localizables)
-            throws IllegalArgumentException
-    {
         ensureNonNull("properties", properties);
 
         // -------------------------------------
@@ -227,9 +220,10 @@ public class AbstractIdentifiedObject ex
         Object value = properties.get(NAME_KEY);
         if (value == null || value instanceof String) {
             name = new NamedIdentifier(PropertiesConverter.convert(properties));
-        } else {
-            ensureCanCast(NAME_KEY, ReferenceIdentifier.class, value);
+        } else if (value instanceof ReferenceIdentifier) {
             name = (ReferenceIdentifier) value;
+        } else {
+            throw illegalPropertyType(NAME_KEY, value);
         }
 
         // -------------------------------------------------------------------
@@ -237,29 +231,99 @@ public class AbstractIdentifiedObject ex
         // -------------------------------------------------------------------
         value = properties.get(ALIAS_KEY);
         try {
-            alias = nonNull(immutableSet(Types.toGenericNames(value, null)));
+            alias = immutableSet(Types.toGenericNames(value, null));
         } catch (ClassCastException e) {
-            throw new InvalidParameterValueException(Errors.format(Errors.Keys.IllegalArgumentClass_2,
-                    ALIAS_KEY, value.getClass()), e, ALIAS_KEY, value);
+            throw (IllegalArgumentException) illegalPropertyType(ALIAS_KEY, value).initCause(e);
         }
 
         // -----------------------------------------------------------
         // "identifiers": ReferenceIdentifier or ReferenceIdentifier[]
         // -----------------------------------------------------------
         value = properties.get(IDENTIFIERS_KEY);
-        if (value instanceof ReferenceIdentifier) {
+        if (value == null) {
+            identifiers = null;
+        } else if (value instanceof ReferenceIdentifier) {
             identifiers = Collections.singleton((ReferenceIdentifier) value);
+        } else if (value instanceof ReferenceIdentifier[]) {
+            identifiers = immutableSet((ReferenceIdentifier[]) value);
         } else {
-            ensureCanCast(IDENTIFIERS_KEY, ReferenceIdentifier[].class, value);
-            identifiers = nonNull(immutableSet((ReferenceIdentifier[]) value));
+            throw illegalPropertyType(IDENTIFIERS_KEY, value);
         }
 
         // ----------------------------------------
         // "remarks": String or InternationalString
         // ----------------------------------------
-        value = properties.get(REMARKS_KEY);
-        ensureCanCast(REMARKS_KEY, CharSequence.class, value);
-        remarks = Types.toInternationalString((CharSequence) value);
+        remarks = Types.toInternationalString(properties, REMARKS_KEY);
+    }
+
+    /**
+     * Returns the exception to be thrown when a property if of illegal type.
+     */
+    private static IllegalArgumentException illegalPropertyType(final String key, final Object
value) {
+        return new IllegalArgumentException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
key, value.getClass()));
+    }
+
+    /**
+     * The {@code gml:id}, which is mandatory. The current implementation searches for the
first identifier,
+     * regardless its authority. If no identifier is found, then the name is used.
+     * If no name is found (which should not occur for valid objects), then this method returns
{@code null}.
+     *
+     * <p>When an identifier has been found, this method returns the concatenation
of its code space with its code,
+     * <em>without separator</em>. For example this method may return {@code
"EPSG4326"}, not {@code "EPSG:4326"}.</p>
+     *
+     * <p>The returned ID needs to be unique only in the XML document being marshalled.
+     * Consecutive invocations of this method do not need to return the same value,
+     * since it may depends on the marshalling context.</p>
+     */
+    @XmlID
+    @XmlAttribute(name = "id", namespace = Namespaces.GML, required = true)
+    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+    final String getID() {
+        final StringBuilder id = new StringBuilder();
+        /*
+         * We will iterate over the identifiers first. Only after the iteration is over,
+         * if we found no suitable ID, then we will use the primary name as a last resort.
+         */
+        Iterator<ReferenceIdentifier> it = iterator(identifiers);
+        do {
+            final ReferenceIdentifier identifier;
+            if (it != null && it.hasNext()) {
+                identifier = it.next();
+            } else {
+                it = null;
+                identifier = name;
+            }
+            if (identifier != null) {
+                boolean codeSpace = true;
+                do { // Executed exactly twice: once for codespace, then once for code.
+                    final String part = codeSpace ? identifier.getCodeSpace() : identifier.getCode();
+                    if (part != null) {
+                        /*
+                         * Found a codespace (in the first iteration) or a code (in the second
iteration).
+                         * Append to the buffer only the characters that are valid for a
Unicode identifier.
+                         */
+                        for (int i=0; i<part.length();) {
+                            final int c = part.codePointAt(i);
+                            if (id.length() == 0 ? Character.isUnicodeIdentifierStart(c)
+                                                 : Character.isUnicodeIdentifierPart(c))
+                            {
+                                id.appendCodePoint(c);
+                            }
+                            i += Character.charCount(c);
+                        }
+                    }
+                } while ((codeSpace = !codeSpace) == false);
+                if (id.length() != 0) {
+                    /*
+                     * TODO: If we want to check for ID uniqueness or any other condition
before to accept the ID,
+                     * we would do that here. If the ID is rejected, then we just need to
clear the buffer and let
+                     * the iteration continue the search for an other ID.
+                     */
+                    return id.toString();
+                }
+            }
+        } while (it != null);
+        return null;
     }
 
     /**
@@ -283,7 +347,7 @@ public class AbstractIdentifiedObject ex
      */
     @Override
     public Collection<GenericName> getAlias() {
-        return alias;
+        return nonNull(alias); // Needs to be null-safe because we may have a null value
on unmarshalling.
     }
 
     /**
@@ -296,7 +360,19 @@ public class AbstractIdentifiedObject ex
      */
     @Override
     public Set<ReferenceIdentifier> getIdentifiers() {
-        return identifiers;
+        return nonNull(identifiers); // Needs to be null-safe because we may have a null
value on unmarshalling.
+    }
+
+    /**
+     * Returns the first identifier found, or {@code null} if none.
+     * This method is invoked by JAXB at marshalling time.
+     *
+     * @see #name
+     */
+    @XmlElement(name = "identifier")
+    final ReferenceIdentifier getIdentifier() {
+        final Iterator<ReferenceIdentifier> it = iterator(identifiers);
+        return (it != null && it.hasNext()) ? it.next() : null;
     }
 
     /**
@@ -309,17 +385,133 @@ public class AbstractIdentifiedObject ex
         return remarks;
     }
 
+    /**
+     * Returns {@code true} if this object is deprecated. Deprecated objects exist in some
+     * {@linkplain org.opengis.referencing.AuthorityFactory authority factories} like the
+     * EPSG database. Deprecated objects are usually obtained from a deprecated authority
code.
+     * For this reason, the default implementation applies the following rules:
+     *
+     * <ul>
+     *   <li>If the {@linkplain #getName() name} is deprecated, then returns {@code
true}.</li>
+     *   <li>Otherwise if <strong>all</strong> {@linkplain #getIdentifiers()
identifiers}
+     *       are deprecated, ignoring the identifiers that are not instance of {@link Deprecable}
+     *       (because they can not be tested), then returns {@code true}.</li>
+     *   <li>Otherwise returns {@code false}.</li>
+     * </ul>
+     *
+     * @return {@code true} if this object is deprecated.
+     *
+     * @see org.apache.sis.metadata.iso.ImmutableIdentifier#isDeprecated()
+     */
     @Override
     public boolean isDeprecated() {
-        return false;
+        if (name instanceof Deprecable) {
+            if (((Deprecable) name).isDeprecated()) {
+                return true;
+            }
+        }
+        boolean isDeprecated = false;
+        for (final ReferenceIdentifier identifier : nonNull(identifiers)) {
+            if (identifier instanceof Deprecable) {
+                if (!((Deprecable) identifier).isDeprecated()) {
+                    return false;
+                }
+                isDeprecated = true;
+            }
+        }
+        return isDeprecated;
     }
 
+    /**
+     * Returns {@code true} if either the {@linkplain #getName() primary name} or at least
+     * one {@linkplain #getAlias alias} matches the specified string.
+     * This method performs the search in the following order, regardless of any authority:
+     *
+     * <ul>
+     *   <li>The {@linkplain #getName() primary name} of this object</li>
+     *   <li>The {@linkplain ScopedName fully qualified name} of an alias</li>
+     *   <li>The {@linkplain LocalName local name} of an alias</li>
+     * </ul>
+     *
+     * @param  name The name to compare.
+     * @return {@code true} if the primary name of at least one alias matches the specified
{@code name}.
+     *
+     * @see IdentifiedObjects#nameMatches(IdentifiedObject, String)
+     */
     public boolean nameMatches(final String name) {
-        return false;
+        return IdentifiedObjects.nameMatches(this, alias, name);
+    }
+
+    /**
+     * Compares the specified object with this object for equality.
+     * This method is implemented as below (omitting assertions):
+     *
+     * {@preformat java
+     *     return equals(other, ComparisonMode.STRICT);
+     * }
+     *
+     * @param  object The other object (may be {@code null}).
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public final boolean equals(final Object object) {
+        final boolean eq = equals(object, ComparisonMode.STRICT);
+        // If objects are equal, then they must have the same hash code value.
+        assert !eq || hashCode() == object.hashCode() : this;
+        return eq;
     }
 
     @Override
     public boolean equals(Object other, ComparisonMode mode) {
         throw new UnsupportedOperationException("Not supported yet.");
     }
+
+    /**
+     * Returns a hash value for this identified object.
+     * This method invokes {@link #computeHashCode()} when first needed and caches the value
for future invocations.
+     * Subclasses shall override {@code computeHashCode()} instead than this method.
+     *
+     * {@section Implementation specific feature}
+     * In the Apache SIS implementation, the {@linkplain #getName() name}, {@linkplain #getIdentifiers()
identifiers}
+     * and {@linkplain #getRemarks() remarks} are not used for hash code computation.
+     * Consequently two identified objects will return the same hash value if they are equal
in the sense of
+     * <code>{@linkplain #equals(Object, ComparisonMode) equals}(…, {@linkplain ComparisonMode#IGNORE_METADATA})</code>.
+     * This feature allows users to implement metadata-insensitive {@link java.util.HashMap}.
+     *
+     * @return The hash code value. This value may change between different execution of
the Apache SIS library.
+     */
+    @Override
+    public final int hashCode() { // No need to synchronize; ok if invoked twice.
+        int hash = hashCode;
+        if (hash == 0) {
+            hash = computeHashCode();
+            if (hash == 0) {
+                hash = -1;
+            }
+            hashCode = hash;
+        }
+        assert hash == -1 || hash == computeHashCode() : this;
+        return hash;
+    }
+
+    /**
+     * Computes a hash value for this identified object.
+     * This method is invoked by {@link #hashCode()} when first needed.
+     *
+     * <p>The default implementation computes a code derived from the list of {@link
IdentifiedObject} interfaces
+     * implemented by this instance. The {@linkplain #getName() name}, {@linkplain #getIdentifiers()
identifiers}
+     * and {@linkplain #getRemarks() remarks} are intentionally <strong>not</strong>
used for hash code computation.
+     * See the <cite>Implementation specific feature</cite> section in {@link
#hashCode()} for more information.</p>
+     *
+     * @return The hash code value. This value may change between different execution of
the Apache SIS library.
+     */
+    protected int computeHashCode() {
+        // Subclasses need to overrides this!!!!
+        int code = (int) serialVersionUID;
+        for (final Class<?> type : Classes.getLeafInterfaces(getClass(), IdentifiedObject.class))
{
+            // Use a plain addition in order to be insensitive to array element order.
+            code += type.hashCode();
+        }
+        return code;
+    }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java?rev=1518844&r1=1518843&r2=1518844&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
[UTF-8] Thu Aug 29 22:19:06 2013
@@ -308,6 +308,8 @@ public final class IdentifiedObjects ext
      */
     public static boolean nameMatches(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).nameMatches(name);
         } else {
             ensureNonNull("object", object);



Mime
View raw message