sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1624681 - in /sis/branches/JDK8/core/sis-utility/src: main/java/org/apache/sis/util/ main/java/org/apache/sis/util/iso/ test/java/org/apache/sis/test/suite/ test/java/org/apache/sis/util/iso/
Date Fri, 12 Sep 2014 23:30:20 GMT
Author: desruisseaux
Date: Fri Sep 12 23:30:19 2014
New Revision: 1624681

URL: http://svn.apache.org/r1624681
Log:
Complete the support of a TypeName <-> Class mapping.

Added:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
  (with props)
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java?rev=1624681&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
(added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -0,0 +1,64 @@
+/*
+ * 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.util;
+
+
+/**
+ * Thrown when an operation can not complete because a given name is unrecognized.
+ * The unrecognized name may be a {@link org.opengis.util.GenericName}, an
+ * {@link org.opengis.metadata.Identifier} or a {@link String} for instance.
+ *
+ * <p><b>Note:</b> in the particular case of objects created from a {@link
org.opengis.util.Factory},
+ * the exception for unrecognized identifiers is rather {@link org.opengis.util.NoSuchIdentifierException}.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+public class UnknownNameException extends RuntimeException {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 8184564092008827669L;
+
+    /**
+     * Constructs a new exception with no message.
+     */
+    public UnknownNameException() {
+        super();
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message.
+     *
+     * @param message The detail message, or {@code null} if none.
+     */
+    public UnknownNameException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and cause.
+     *
+     * @param message The detail message, or {@code null} if none.
+     * @param cause The cause, or {@code null} if none.
+     */
+    public UnknownNameException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -20,10 +20,10 @@ import org.apache.sis.util.resources.Err
 
 
 /**
- * Thrown when an operation can't use arbitrary implementation of an interface, and
- * a given instance doesn't meet the requirement. For example this exception may be
- * thrown when an operation requires an Apache SIS implementation of a
- * <A HREF="http://www.geoapi.org">GeoAPI</A> interface.
+ * Thrown when an operation can not use arbitrary implementation of an interface,
+ * and a given instance does not meet the requirement. For example this exception
+ * may be thrown when an operation requires an Apache SIS implementation of a
+ * <a href="http://www.geoapi.org">GeoAPI</a> interface.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.3 (derived from geotk-2.0)

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -415,7 +415,7 @@ public class DefaultNameFactory extends 
      * and {@code TypeName} objects as documented in the {@link DefaultTypeName} javadoc.
      *
      * <p>In order to protect against potential changes in the {@code Class} ↔ {@code
TypeName} mapping, users are
-     * encouraged to retrieve the {@code valueClass} by invoking the {@link DefaultTypeName#getValueClass()}
method
+     * encouraged to retrieve the {@code valueClass} by invoking the {@link DefaultTypeName#toClass()}
method
      * instead than parsing the name.</p>
      *
      * @param  valueClass The Java class for which to get a type name, or {@code null}.
@@ -424,31 +424,37 @@ public class DefaultNameFactory extends 
      * @since 0.5
      */
     public TypeName toTypeName(final Class<?> valueClass) {
-        if (valueClass == null) {
+        if (!TypeNames.isValid(valueClass)) {
             return null;
         }
         /*
          * Note: we do not cache the TypeName for the valueClass argument because:
-         * - It is not needed (at least in the default implementation) for getting unique
instance.
-         * - It is not the best place for performance improvement, since TypeName are usually
only
-         *   a step in the creation of bigger object (typically AttributeType). Users are
better to
-         *   cache the bigger object instead.
+         *
+         *  - It is not needed (at least in the default implementation) for getting unique
instance.
+         *  - It is not the best place for performance improvement, since TypeName are usually
only
+         *    a step in the creation of bigger object (typically AttributeType). Users are
better to
+         *    cache the bigger object instead.
          */
         TypeNames t = typeNames;
         if (t == null) {
-            synchronized (pool) {
-                /*
-                 * Double-check strategy is ok if 'typeNames' is volatile. We synchronize
on 'pool' because
-                 * the TypeNames constructor will call back methods from this class, which
will use the pool.
-                 * We are better to use only one lock for reducing the risk of dead-lock.
Inconvenient is that
-                 * we potentially invoke user code (since our methods are overrideable) while
holding the lock.
-                 */
+            /*
+             * Create TypeNames outide the synchronized block because the TypeNames constructor
will call back
+             * methods from this class. Since those methods are overrideable, this could
invoke user's code.
+             * Note also that methods in this class use the 'pool', which is itself synchronized,
so we are
+             * better to avoid double synchronization for reducing the risk of dead-lock.
+             */
+            final TypeNames c = new TypeNames(this);
+            synchronized (this) { // Double-check strategy is ok if 'typeNames' is volatile.
                 t = typeNames;
                 if (t == null) {
-                    typeNames = t = new TypeNames(this);
+                    typeNames = t = c;
                 }
             }
         }
-        return t.toTypeName(this, valueClass);
+        final TypeName name = t.toTypeName(this, valueClass);
+        if (name instanceof DefaultTypeName) {
+            ((DefaultTypeName) name).valueClass = valueClass;
+        }
+        return name;
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -17,10 +17,10 @@
 package org.apache.sis.util.iso;
 
 import java.util.Map;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Collections;
-import java.util.Date;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
 import org.opengis.util.Type;
 import org.opengis.util.TypeName;
 import org.opengis.util.LocalName;
@@ -30,7 +30,6 @@ import org.opengis.util.NameSpace;
 import org.opengis.util.RecordSchema;
 import org.opengis.util.RecordType;
 import org.apache.sis.util.Debug;
-import org.apache.sis.util.Numbers;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ObjectConverter;
 import org.apache.sis.util.ObjectConverters;
@@ -54,7 +53,7 @@ import org.apache.sis.internal.converter
  * by overriding the following methods:
  *
  * <ul>
- *   <li>{@link #toTypeName(Class)}</li>
+ *   <li>{@link DefaultNameFactory#toTypeName(Class)} if the factory given to the constructor.</li>
  * </ul>
  *
  * {@section Thread safety}
@@ -86,6 +85,12 @@ public class DefaultRecordSchema impleme
     protected final NameFactory nameFactory;
 
     /**
+     * The helper class to use for mapping Java classes to {@code TypeName} instances, or
{@code null} if not needed.
+     * This helper class is needed only if {@link #nameFactory} is not an instance of {@link
DefaultNameFactory}.
+     */
+    private final TypeNames typeFactory;
+
+    /**
      * The namespace of {@link RecordType} to be created by this class.
      * This is also (indirectly) the {@linkplain #getSchemaName() schema name}.
      */
@@ -98,9 +103,8 @@ public class DefaultRecordSchema impleme
 
     /**
      * The pool of attribute types created so far.
-     * Shall be used only in synchronized blocks.
      */
-    private final Map<Class<?>,Type> attributeTypes;
+    private final ConcurrentMap<Class<?>,Type> attributeTypes;
 
     /**
      * The converter to use for converting Java {@link Class} to ISO 19103 {@link Type}.
@@ -126,9 +130,10 @@ public class DefaultRecordSchema impleme
             nameFactory = DefaultFactories.NAMES;
         }
         this.nameFactory    = nameFactory;
+        this.typeFactory    = (nameFactory instanceof DefaultNameFactory) ? null : new TypeNames(nameFactory);
         this.namespace      = nameFactory.createNameSpace(nameFactory.createLocalName(parent,
schemaName), null);
         this.description    = new WeakValueHashMap<>(TypeName.class);
-        this.attributeTypes = new HashMap<>();
+        this.attributeTypes = new ConcurrentHashMap<>();
     }
 
     /**
@@ -189,79 +194,38 @@ public class DefaultRecordSchema impleme
     }
 
     /**
-     * Suggests an attribute type for the given value class. For a short list of known classes,
-     * this method returns the ISO 19103 type as used in XML documents. Examples:
-     *
-     * <table class="sis">
-     *   <caption>Attribute types for Java classes (non exhaustive list)</caption>
-     *   <tr><th>Java class</th>        <th>Attribute type</th></tr>
-     *   <tr><td>{@link String}</td>    <td>{@code gco:CharacterString}</td></tr>
-     *   <tr><td>{@link Date}</td>      <td>{@code gco:DateTime}</td></tr>
-     *   <tr><td>{@link Double}</td>    <td>{@code gco:Real}</td></tr>
-     *   <tr><td>{@link Integer}</td>   <td>{@code gco:Integer}</td></tr>
-     *   <tr><td>{@link Boolean}</td>   <td>{@code gco:Boolean}</td></tr>
-     * </table>
+     * Suggests an attribute type for the given value class. The {@code TypeName} will use
the UML identifier
+     * of OGC/ISO specification when possible, e.g. {@code "GCO:CharacterString"} for {@code
java.lang.String}.
+     * See <cite>Mapping Java classes to type names</cite> in {@link DefaultTypeName}
javadoc for more information.
      *
      * @param  valueClass The value class to represent as an attribute type.
      * @return Attribute type for the given value class.
      */
-    private Type toAttributeType(final Class<?> valueClass) {
-        Type type;
-        synchronized (attributeTypes) {
-            type = attributeTypes.get(valueClass);
+    final Type toAttributeType(final Class<?> valueClass) {
+        if (!TypeNames.isValid(valueClass)) {
+            return null;
         }
+        Type type = attributeTypes.get(valueClass);
         if (type == null) {
-            type = new SimpleAttributeType<>(toTypeName(valueClass), valueClass);
-            synchronized (attributeTypes) {
-                final Type old = attributeTypes.put(valueClass, type);
-                if (old != null) { // May happen if the type has been computed concurrently.
-                    attributeTypes.put(valueClass, old);
-                    return old;
-                }
+            if (valueClass == Void.TYPE) {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2,
"valueClass", "void"));
+            }
+            final TypeName name;
+            if (nameFactory instanceof DefaultNameFactory) {
+                name = ((DefaultNameFactory) nameFactory).toTypeName(valueClass);
+            } else {
+                name = typeFactory.toTypeName(nameFactory, valueClass);
+            }
+            type = new SimpleAttributeType<>(name, valueClass);
+            final Type old = attributeTypes.putIfAbsent(valueClass, type);
+            if (old != null) { // May happen if the type has been computed concurrently.
+                return old;
             }
         }
         return type;
     }
 
     /**
-     * Suggests a type name for the given value class. For a short list of known classes,
-     * this method returns the ISO 19103 type name as used in XML documents. Examples:
-     *
-     * <table class="sis">
-     *   <caption>Type names for Java classes (non exhaustive list)</caption>
-     *   <tr><th>Java class</th>        <th>Type name</th></tr>
-     *   <tr><td>{@link String}</td>    <td>"{@code gco:CharacterString}"</td></tr>
-     *   <tr><td>{@link Date}</td>      <td>"{@code gco:DateTime}"</td></tr>
-     *   <tr><td>{@link Double}</td>    <td>"{@code gco:Real}"</td></tr>
-     *   <tr><td>{@link Integer}</td>   <td>"{@code gco:Integer}"</td></tr>
-     *   <tr><td>{@link Boolean}</td>   <td>"{@code gco:Boolean}"</td></tr>
-     * </table>
-     *
-     * Subclasses can override this method for defining more type names.
-     *
-     * @param  valueClass The value class for which to get a type name.
-     * @return Type name for the given value class.
-     */
-    protected TypeName toTypeName(final Class<?> valueClass) {
-        String ns = "gco";
-        final String name;
-        if (CharSequence.class.isAssignableFrom(valueClass)) {
-            name = "CharacterString";
-        } else if (Number.class.isAssignableFrom(valueClass)) {
-            name = Numbers.isInteger(valueClass) ? "Integer" : "Real";
-        } else if (Date.class.isAssignableFrom(valueClass)) {
-            name = "DateTime";
-        } else if (valueClass == Boolean.class) {
-            name = "Boolean";
-        } else {
-            ns   = "java";
-            name = valueClass.getCanonicalName();
-        }
-        return nameFactory.createTypeName(nameFactory.createNameSpace(
-                nameFactory.createLocalName(null, ns), null), name);
-    }
-
-    /**
      * Returns the dictionary of all (<var>name</var>, <var>record type</var>)
pairs in this schema.
      *
      * @return All (<var>name</var>, <var>record type</var>) pairs
in this schema.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -20,7 +20,7 @@ import javax.xml.bind.annotation.XmlType
 import javax.xml.bind.annotation.XmlRootElement;
 import org.opengis.util.TypeName;
 import org.opengis.util.NameSpace;
-import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.UnknownNameException;
 
 
 /**
@@ -42,7 +42,7 @@ import org.apache.sis.util.resources.Err
  * small and is sometime not an exact match.
  *
  * <table class="sis">
- *   <caption>Mapping from Java classes to type names (non exhaustive list)</caption>
+ *   <caption>Mapping from Java classes to type names (non-exhaustive list)</caption>
  *   <tr><th>Java class</th>                                   <th>Type
name (unofficial)</th>      <th>Definition identifier in OGC namespace</th></tr>
  *   <tr><td>{@link org.opengis.util.InternationalString}</td> <td>{@code
OGC:FreeText}</td>        <td></td></tr>
  *   <tr><td>{@link java.lang.String}</td>                     <td>{@code
OGC:CharacterString}</td> <td>urn:ogc:def:dataType:OGC::string</td></tr>
@@ -58,7 +58,7 @@ import org.apache.sis.util.resources.Err
  *
  * The mapping defined by Apache SIS may change in any future version depending on standardization
progress.
  * To protect against such changes, users are encouraged to rely on methods or constructors
like
- * {@link DefaultNameFactory#toTypeName(Class)} or {@link #getValueClass()} instead than
parsing the name.
+ * {@link DefaultNameFactory#toTypeName(Class)} or {@link #toClass()} instead than parsing
the name.
  *
  *
  * {@section Immutability and thread safety}
@@ -85,11 +85,17 @@ public class DefaultTypeName extends Def
     private static final long serialVersionUID = 7182126541436753582L;
 
     /**
-     * The value class to be returned by {@link #getValueClass()}, or {@code null} if not
yet computed.
-     * This field is serialized because it may be specified at construction time.
+     * The value class to be returned by {@link #toClass()}, or {@code null} if not yet computed.
      * {@link Void#TYPE} is used as a sentinel value meaning explicit {@code null}.
+     *
+     * <p>This value is only computed. We do not allow the user to explicitely specify
it, because we
+     * need that {@code DefaultTypeName}s having identical name also have the same {@code
valueClass}.
+     * This is necessary {@link DefaultNameFactory#pool} cache integrity. Users who want
to explicitely
+     * specify their own value class can override {@link #toClass()} instead.</p>
+     *
+     * @see #toClass()
      */
-    private Class<?> valueClass;
+    transient Class<?> valueClass;
 
     /**
      * Empty constructor to be used by JAXB only. Despite its "final" declaration,
@@ -99,37 +105,16 @@ public class DefaultTypeName extends Def
     }
 
     /**
-     * Constructs a type name from the given character sequence. The argument are given unchanged
-     * to the {@linkplain DefaultLocalName#DefaultLocalName(NameSpace,CharSequence) super-class
-     * constructor}.
+     * Constructs a type name from the given character sequence. The argument are given unchanged
to the
+     * {@linkplain DefaultLocalName#DefaultLocalName(NameSpace,CharSequence) super-class
constructor}.
      *
      * @param scope The scope of this name, or {@code null} for a global scope.
      * @param name  The local name (never {@code null}).
-     */
-    protected DefaultTypeName(final NameSpace scope, final CharSequence name) {
-        super(scope, name);
-    }
-
-    /**
-     * Constructs a type name from the given character sequence and value class.
-     * The given value class is stored for retrieval by {@link #getValueClass()}.
      *
-     * @param scope      The scope of this name, or {@code null} for a global scope.
-     * @param name       The local name (never {@code null}).
-     * @param valueClass The Java class associated to this {@code TypeName}, or {@code null}.
-     *
-     * @see DefaultNameFactory#toTypeName(Class)
-     *
-     * @since 0.5
+     * @see DefaultNameFactory#createTypeName(NameSpace, CharSequence)
      */
-    protected DefaultTypeName(final NameSpace scope, final CharSequence name, Class<?>
valueClass) {
+    protected DefaultTypeName(final NameSpace scope, final CharSequence name) {
         super(scope, name);
-        if (valueClass == null) {
-            valueClass = Void.TYPE;
-        } else if (valueClass == Void.TYPE) {
-            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2,
"valueClass", "void"));
-        }
-        this.valueClass = valueClass;
     }
 
     /**
@@ -158,23 +143,48 @@ public class DefaultTypeName extends Def
     }
 
     /**
-     * Returns the Java class associated to this type name. If a Java class has been explicitely
specified to the
-     * {@linkplain #DefaultTypeName(NameSpace, CharSequence, Class) constructor}, then that
class is returned
-     * (note that it may be explicitely {@code null}). Otherwise if this {@code TypeName}
is as documented in
-     * the <cite>Mapping Java classes to type names</cite> section of class javadoc,
then the corresponding
-     * Java class is returned. Otherwise this method returns {@code null}.
+     * Returns the Java class associated to this type name.
+     * The default implementation performs the following choices:
+     *
+     * <ul>
+     *   <li>If the {@linkplain #scope() scope} of this name is not {@code "OGC"} or
{@code "class"},
+     *       then this method returns {@code null}.</li>
+     *   <li>Otherwise this method interprets this {@code TypeName} as documented in
the
+     *       <cite>Mapping Java classes to type names</cite> section of class
javadoc.
+     *       The result is either a non-null class or an {@code UnknownNameException}.</li>
+     * </ul>
      *
-     * @return The Java class associated to this {@code TypeName}, or {@code null} if none.
+     * @return The Java class associated to this {@code TypeName}, or {@code null} if and
only if
+     *         the {@linkplain #scope() scope} is not one of the supported namespaces.
+     * @throws UnknownNameException if the scope is one of the supported namespaces, but
the name is not recognized.
      *
      * @since 0.5
      */
-    public Class<?> getValueClass() {
-        if (valueClass == Void.TYPE) {
+    public Class<?> toClass() throws UnknownNameException {
+        /*
+         * No synchronization, because it is not a problem if two threads compute the same
value concurrently.
+         * No volatile field neither, because instances of Class are safely published (well,
I presume...).
+         */
+        Class<?> c = valueClass;
+        if (c == Void.TYPE) {
             return null;
         }
-        if (valueClass == null) {
-            // TODO: infer here.
+        if (c == null) {
+            /*
+             * Invoke super.foo() instead than this.foo() because we do not want to invoke
any overridden method.
+             * This is for ensuring that two TypeNames constructed with the same name will
map to the same class.
+             * See 'valueClass' javadoc for more information.
+             */
+            try {
+                c = TypeNames.toClass(super.scope().name().toString(), super.toString());
+            } catch (ClassNotFoundException e) {
+                throw new UnknownNameException(TypeNames.unknown(super.toFullyQualifiedName()),
e);
+            }
+            if (c == null) {
+                throw new UnknownNameException(TypeNames.unknown(super.toFullyQualifiedName()));
+            }
+            valueClass = c;
         }
-        return valueClass;
+        return c;
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java [UTF-8]
(original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java [UTF-8]
Fri Sep 12 23:30:19 2014
@@ -24,6 +24,7 @@ import org.opengis.util.NameSpace;
 import org.opengis.util.InternationalString;
 import org.apache.sis.util.Static;
 import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.util.UnknownNameException;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
 
@@ -212,4 +213,49 @@ public final class Names extends Static 
         return new StringBuilder(ns.length() + localPart.length() + 2)
                 .append('{').append(ns).append('}').append(localPart).toString();
     }
+
+    /**
+     * Returns the Java class associated to the given type name.
+     * The method performs the following choices:
+     *
+     * <ul>
+     *   <li>If the given type is {@code null}, then this method returns {@code null}.</li>
+     *   <li>Otherwise if the given type is an instance of {@code DefaultTypeName},
+     *       then this method delegates to {@link DefaultTypeName#toClass()}.</li>
+     *   <li>Otherwise if the {@linkplain #scope() scope} of the given name is not
{@code "OGC"}
+     *       or {@code "class"}, then this method returns {@code null}.</li>
+     *   <li>Otherwise this method interprets the given name as documented in the
+     *       <cite>Mapping Java classes to type names</cite> section of {@link
DefaultTypeName} javadoc.
+     *       The result is either a non-null class or an {@code UnknownNameException}.</li>
+     * </ul>
+     *
+     * @param  type The type name from which to infer a Java class.
+     * @return The Java class associated to the given {@code TypeName}, or {@code null} if
the given type
+     *         is null or if the {@linkplain #scope() scope} is not one of the supported
namespaces.
+     * @throws UnknownNameException if the scope is one of the supported namespaces, but
the name is not recognized.
+     *
+     * @since 0.5
+     */
+    public static Class<?> toClass(final TypeName type) throws UnknownNameException
{
+        if (type == null) {
+            return null;
+        }
+        Class<?> c;
+        if (type instanceof DefaultTypeName) {
+            c = ((DefaultTypeName) type).toClass();
+        } else {
+            try {
+                c = TypeNames.toClass(type.scope().name().toString(), type.toString());
+            } catch (ClassNotFoundException e) {
+                throw new UnknownNameException(TypeNames.unknown(type), e);
+            }
+            if (c == null) {
+                throw new UnknownNameException(TypeNames.unknown(type));
+            }
+            if (c == Void.TYPE) {
+                c = null;
+            }
+        }
+        return c;
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -17,12 +17,17 @@
 package org.apache.sis.util.iso;
 
 import java.util.Date;
+import java.util.Locale;
 import java.util.Map;
-import java.util.HashMap;
-import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Iterator;
+import java.net.URI;
+import org.opengis.util.GenericName;
 import org.opengis.util.TypeName;
 import org.opengis.util.NameSpace;
 import org.opengis.util.NameFactory;
+import org.opengis.util.InternationalString;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.Numbers;
 
 
@@ -36,6 +41,32 @@ import org.apache.sis.util.Numbers;
  */
 final class TypeNames {
     /**
+     * The mapping between {@link TypeName} and {@link Class} as documented in {@link DefaultTypeName}.
+     * When searching for a name from a class, the values will be tested in iteration order.
+     *
+     * <p>{@link #toTypeName(NameFactory, Class)} will <strong>not</strong>
iterate over all entries.
+     * Numbers and character strings are handled in a special way, so we do not need to iterate
on them.
+     * We arbitrarily use {@code Boolean.class} as the sentinel value for detecting when
to stop iteration.</p>
+     *
+     * <p>This map shall not be modified after construction.</p>
+     */
+    private static final Map<String,Class<?>> MAPPING = new LinkedHashMap<>(16);
+    static {
+        final Map<String,Class<?>> m = MAPPING;
+        m.put("URI",                URI.class);
+        m.put("DateTime",           Date.class);
+        m.put("PT_Locale",          Locale.class);
+        m.put("Boolean",            Boolean.class);  // Used as a sentinel value for stopping
iteration.
+
+        // Entries below this point are handled in a special way.
+        m.put("FreeText",           InternationalString.class);
+        m.put("CharacterString",    String.class);
+        m.put("Real",               Double.class);
+        m.put("Decimal",            Double.class);
+        m.put("Integer",            Integer.class);
+    };
+
+    /**
      * The "OGC" namespace.
      */
     private final NameSpace ogcNS;
@@ -49,42 +80,101 @@ final class TypeNames {
      * Creates a new factory of type names.
      */
     TypeNames(final NameFactory factory) {
-        final Map<String,String> properties = new HashMap<>(4);
-        properties.put(DefaultNameFactory.SEPARATOR_KEY, ".");
-        properties.put(DefaultNameFactory.HEAD_SEPARATOR_KEY, DefaultNameSpace.DEFAULT_SEPARATOR_STRING);
-        ogcNS   = factory.createNameSpace(factory.createLocalName(null, "OGC"),   properties);
-        classNS = factory.createNameSpace(factory.createLocalName(null, "class"), properties);
+        ogcNS   = factory.createNameSpace(factory.createLocalName(null, "OGC"),   null);
+        classNS = factory.createNameSpace(factory.createLocalName(null, "class"), null);
     }
 
     /**
      * Infers the type name from the given class.
      *
+     * @param  factory    The same factory than the one given to the constructor.
      * @param  valueClass The value class for which to get a type name.
+     * @return A type name for the given class (never {@code null}).
      */
     final TypeName toTypeName(final NameFactory factory, final Class<?> valueClass)
{
         String name;
         NameSpace ns = ogcNS;
         if (CharSequence.class.isAssignableFrom(valueClass)) {
-            name = "CharacterString";
+            name = InternationalString.class.isAssignableFrom(valueClass) ? "FreeText" :
"CharacterString";
         } else if (Number.class.isAssignableFrom(valueClass)) {
             name = Numbers.isInteger(valueClass) ? "Integer" : "Real";
-        } else if (Date.class.isAssignableFrom(valueClass)) {
-            name = "DateTime";
-        } else if (valueClass == Boolean.class) {
-            name = "Boolean";
         } else {
+            /*
+             * Iterate over the special cases, excluding the numbers and character sequences
+             * since they were verified in the above statements.
+             */
+            final Iterator<Map.Entry<String,Class<?>>> it = MAPPING.entrySet().iterator();
+            Class<?> base;
+            do {
+                final Map.Entry<String,Class<?>> entry = it.next();
+                base = entry.getValue();
+                if (base.isAssignableFrom(valueClass)) {
+                    name = entry.getKey();
+                    return factory.createTypeName(ns, name);
+                }
+            } while (base != Boolean.class); // See MAPPING javadoc for the role of Boolean
as a sentinel value.
+            /*
+             * Found no special case. Checks for the UML annotation, to be also formatted
in the "OGC:" namespace.
+             * If no UML identifier is found, then we will format the Java class in the "class:"
namespace.
+             */
             name = Types.getStandardName(valueClass);
             if (name == null) {
                 ns = classNS;
                 name = valueClass.getCanonicalName();
-                final int s = name.lastIndexOf('.');
-                if (s >= 0) {
-                    ns = factory.createNameSpace(factory.parseGenericName(ns, name.substring(0,
s)),
-                            Collections.singletonMap(DefaultNameFactory.SEPARATOR_KEY, "."));
-                    name = name.substring(s+1);
-                }
             }
         }
         return factory.createTypeName(ns, name);
     }
+
+    /**
+     * Returns the class for the given name. This method is the converse of {@link #toTypeName(NameFactory,
Class)}.
+     * This method returns 3 kind of values:
+     *
+     * <ul>
+     *   <li>{@code Void.TYPE} if {@code namespace} is unrecognized.
+     *       This is a sentinel value expected by {@link DefaultTypeName#toClass()} for such
case.</li>
+     *   <li>{@code null} if {@code namespace} is recognized, but not the {@code name}.</li>
+     *   <li>Otherwise the class for the given name.</li>
+     * </ul>
+     *
+     * @param  namespace The namespace ("OGC" or "class"), case-insensitive.
+     * @param  name The name, case-sensitive.
+     * @return The class, or {@code Void.TYPE} if the given namespace is not recognized,
+     *         or {@code null} if the namespace is recognized but not the name.
+     * @throws ClassNotFoundException if {@code namespace} is {@code "class"} but {@code
name} is not
+     *         the name of a reachable class.
+     */
+    static Class<?> toClass(final String namespace, final String name) throws ClassNotFoundException
{
+        Class<?> c;
+        if (namespace.equalsIgnoreCase("OGC")) {
+            c = MAPPING.get(name);
+            if (c == null) {
+                c = Types.forStandardName(name);
+            }
+        } else if (namespace.equalsIgnoreCase("class")) {
+            c = Class.forName(name);
+        } else {
+            c = Void.TYPE; // Not an "OGC" or "class" namespace.
+        }
+        return c;
+    }
+
+    /**
+     * Ensures that the given class is not {@link Void#TYPE}.
+     * This is a helper method for callers of {@link #toTypeName(NameFactory, Class)}.
+     */
+    static boolean isValid(final Class<?> valueClass) {
+        if (valueClass == Void.TYPE) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2,
"valueClass", "void"));
+        }
+        return (valueClass != null);
+    }
+
+    /**
+     * Formats the error message for an unknown type.
+     * This is a helper method for callers of {@link #toClass(String, String)}.
+     */
+    static String unknown(final GenericName name) {
+        return Errors.format(Errors.Keys.UnknownType_1, name.toFullyQualifiedName());
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -86,6 +86,7 @@ import org.junit.BeforeClass;
     org.apache.sis.util.iso.AbstractNameTest.class,
     org.apache.sis.util.iso.DefaultNameFactoryTest.class,
     org.apache.sis.util.iso.NamesTest.class,
+    org.apache.sis.util.iso.TypeNamesTest.class,
     org.apache.sis.internal.simple.SimpleReferenceIdentifierTest.class,
     org.apache.sis.util.iso.DefaultRecordTypeTest.class,
     org.apache.sis.util.iso.DefaultRecordSchemaTest.class,

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -69,25 +69,25 @@ public final strictfp class DefaultRecor
             switch (count) {
                 case 0: {
                     expectedName  = "city";
-                    expectedType  = "gco:CharacterString";
+                    expectedType  = "OGC:CharacterString";
                     expectedClass = String.class;
                     break;
                 }
                 case 1: {
                     expectedName  = "latitude";
-                    expectedType  = "gco:Real";
+                    expectedType  = "OGC:Real";
                     expectedClass = Double.class;
                     break;
                 }
                 case 2: {
                     expectedName  = "longitude";
-                    expectedType  = "gco:Real";
+                    expectedType  = "OGC:Real";
                     expectedClass = Double.class;
                     break;
                 }
                 case 3: {
                     expectedName  = "population";
-                    expectedType  = "gco:Integer";
+                    expectedType  = "OGC:Integer";
                     expectedClass = Integer.class;
                     break;
                 }

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -40,7 +40,7 @@ import static org.apache.sis.test.TestUt
  * @version 0.5
  * @module
  */
-@DependsOn(AbstractNameTest.class)
+@DependsOn(TypeNamesTest.class)
 public final strictfp class DefaultRecordTypeTest extends TestCase {
     /** Value of {@link DefaultRecordType#getContainer()}.   */ private DefaultRecordSchema
container;
     /** Value of {@link DefaultRecordType#getTypeName()}.    */ private DefaultTypeName 
   recordTypeName;

Added: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java?rev=1624681&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
(added)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
[UTF-8] Fri Sep 12 23:30:19 2014
@@ -0,0 +1,130 @@
+/*
+ * 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.util.iso;
+
+
+import java.net.URI;
+import java.util.Date;
+import java.util.Locale;
+import org.opengis.util.TypeName;
+import org.opengis.util.InternationalString;
+import org.opengis.metadata.Metadata;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link TypeNames} class. Tests are performed through the {@link DefaultNameFactory#toTypeName(Class)}
+ * method on the {@link DefaultFactories#SIS_NAMES} instance.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+@DependsOn({
+    TypesTest.class,
+    DefaultNameFactoryTest.class
+})
+public final strictfp class TypeNamesTest extends TestCase {
+    /**
+     * Verifies that the call to {@link TypeNames#toTypeName(NameFactory, Class)} returns
a {@code TypeName} having the
+     * given name and namespace, then tests the reverse operation with {@link TypeNames#toClass(String,
String)}.
+     */
+    private static void verifyLookup(final String namespace, final String name, final Class<?>
valueClass)
+            throws ClassNotFoundException
+    {
+        final TypeName type = DefaultFactories.SIS_NAMES.toTypeName(valueClass);
+        assertNotNull(name, type);
+        assertSame   (name, valueClass, ((DefaultTypeName) type).valueClass);
+        assertEquals (name, namespace,  type.scope().name().toString());
+        assertEquals (name, name,       type.toString());
+        assertEquals (name, valueClass, TypeNames.toClass(namespace, name));
+    }
+
+    /**
+     * Returns the string representation of the fully qualified path of the type name for
the given value.
+     */
+    private static String toTypeName(final Class<?> valueClass) {
+        return DefaultFactories.SIS_NAMES.toTypeName(valueClass).toFullyQualifiedName().toString();
+    }
+
+    /**
+     * Tests the mapping of basic types like strings, URI, dates and numbers.
+     *
+     * @throws ClassNotFoundException Should not happen since we do invoke {@link Class#forName(String)}
in this test.
+     */
+    @Test
+    public void testBasicTypes() throws ClassNotFoundException {
+        verifyLookup("OGC", "URI",              URI.class);
+        verifyLookup("OGC", "PT_Locale",        Locale.class);
+        verifyLookup("OGC", "DateTime",         Date.class);
+        verifyLookup("OGC", "FreeText",         InternationalString.class);
+        verifyLookup("OGC", "CharacterString",  String.class);
+        verifyLookup("OGC", "Boolean",          Boolean.class);
+        verifyLookup("OGC", "Real",             Double.class);
+        verifyLookup("OGC", "Integer",          Integer.class);
+    }
+
+    /**
+     * Tests {@link TypeNames#toTypeName(NameFactory, Class)} with numbers.
+     */
+    @Test
+    public void testNumbers() {
+        assertEquals("Short",  "OGC:Integer", toTypeName(Short .class));
+        assertEquals("Long",   "OGC:Integer", toTypeName(Long  .class));
+        assertEquals("Float",  "OGC:Real",    toTypeName(Float .class));
+        assertEquals("Double", "OGC:Real",    toTypeName(Double.class));
+    }
+
+    /**
+     * Tests the mapping of more complex object that are not basic types.
+     *
+     * @throws ClassNotFoundException Should not happen since we do invoke {@link Class#forName(String)}
in this test.
+     */
+    @Test
+    public void testMetadataClasses() throws ClassNotFoundException {
+        verifyLookup("OGC", "MD_Metadata", Metadata.class);
+        verifyLookup("OGC", "SC_CRS",      CoordinateReferenceSystem.class);
+    }
+
+    /**
+     * Tests the mapping of objects not defined by OGC.
+     *
+     * @throws ClassNotFoundException If the call to {@link Class#forName(String)} failed.
+     */
+    @Test
+    public void testOtherClasses() throws ClassNotFoundException {
+        verifyLookup("class", "java.util.Random", java.util.Random.class);
+    }
+
+    /**
+     * Checks for the sentinel values in case of invalid names.
+     *
+     * @throws ClassNotFoundException Should not happen since we do invoke {@link Class#forName(String)}
in this test.
+     */
+    @Test
+    public void testInvalidNames() throws ClassNotFoundException {
+        assertEquals("Dummy:any", Void.TYPE, TypeNames.toClass("Dummy", "any"));
+        assertNull  ("OGC:Dummy",            TypeNames.toClass("OGC", "Dummy"));
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8



Mime
View raw message