You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by tl...@apache.org on 2013/08/25 17:49:59 UTC

svn commit: r1517321 [10/16] - in /sis/branches/Shapefile: ./ application/ application/sis-console/ application/sis-console/src/main/artifact/ application/sis-console/src/main/java/org/apache/sis/console/ application/sis-console/src/main/resources/org/...

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -19,7 +19,12 @@ package org.apache.sis.internal.jaxb;
 import java.util.Map;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import org.apache.sis.util.Version;
+import org.apache.sis.util.Exceptions;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.logging.WarningListener;
 import org.apache.sis.xml.MarshalContext;
 import org.apache.sis.xml.ValueConverter;
 import org.apache.sis.xml.ReferenceResolver;
@@ -38,7 +43,7 @@ import org.apache.sis.xml.ReferenceResol
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-3.07)
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public final class Context extends MarshalContext {
@@ -63,6 +68,20 @@ public final class Context extends Marsh
     public static final int SUBSTITUTE_COUNTRY = 4;
 
     /**
+     * The bit flag for enabling substitution of filenames by character strings.
+     *
+     * @see org.apache.sis.xml.XML#STRING_SUBSTITUTES
+     */
+    public static final int SUBSTITUTE_FILENAME = 8;
+
+    /**
+     * The bit flag for enabling substitution of mime types by character strings.
+     *
+     * @see org.apache.sis.xml.XML#STRING_SUBSTITUTES
+     */
+    public static final int SUBSTITUTE_MIMETYPE = 16;
+
+    /**
      * The thread-local context. Elements are created in the constructor, and removed in a
      * {@code finally} block by the {@link #finish()} method. This {@code ThreadLocal} shall
      * not contain any value when no (un)marshalling is in progress.
@@ -70,20 +89,20 @@ public final class Context extends Marsh
     private static final ThreadLocal<Context> CURRENT = new ThreadLocal<Context>();
 
     /**
-     * The value converter currently in use, or {@code null} for {@link ValueConverter#DEFAULT}.
+     * Various boolean attributes determines by the above static constants.
      */
-    private ValueConverter converter;
+    private int bitMasks;
 
     /**
-     * The reference resolver currently in use, or {@code null} for {@link ReferenceResolver#DEFAULT}.
+     * The locale to use for marshalling, or {@code null} if no locale were explicitly specified.
      */
-    private ReferenceResolver resolver;
+    private Locale locale;
 
     /**
-     * The GML version to be marshalled or unmarshalled, or {@code null} if unspecified.
-     * If null, than the latest version is assumed.
+     * The timezone, or {@code null} if unspecified.
+     * In the later case, an implementation-default (typically UTC) timezone is used.
      */
-    private Version versionGML;
+    private TimeZone timezone;
 
     /**
      * The base URL of ISO 19139 (or other standards) schemas. The valid values
@@ -92,20 +111,25 @@ public final class Context extends Marsh
     private Map<String,String> schemas;
 
     /**
-     * The locale to use for marshalling, or {@code null} if no locale were explicitly specified.
+     * The GML version to be marshalled or unmarshalled, or {@code null} if unspecified.
+     * If null, than the latest version is assumed.
      */
-    private Locale locale;
+    private Version versionGML;
 
     /**
-     * The timezone, or {@code null} if unspecified.
-     * In the later case, an implementation-default (typically UTC) timezone is used.
+     * The reference resolver currently in use, or {@code null} for {@link ReferenceResolver#DEFAULT}.
      */
-    private TimeZone timezone;
+    private ReferenceResolver resolver;
 
     /**
-     * Various boolean attributes determines by the above static constants.
+     * The value converter currently in use, or {@code null} for {@link ValueConverter#DEFAULT}.
      */
-    private int bitMasks;
+    private ValueConverter converter;
+
+    /**
+     * The object to inform about warnings, or {@code null} if none.
+     */
+    private WarningListener<?> warningListener;
 
     /**
      * The context which was previously used. This form a linked list allowing
@@ -127,26 +151,29 @@ public final class Context extends Marsh
      *     }
      * }
      *
-     * @param  converter  The converter in use.
-     * @param  resolver   The resolver in use.
-     * @param  versionGML The GML version, or {@code null}.
-     * @param  schemas    The schemas root URL, or {@code null} if none.
-     * @param  locale     The locale, or {@code null} if unspecified.
-     * @param  timezone   The timezone, or {@code null} if unspecified.
-     * @param  bitMasks   A combination of {@link #MARSHALLING}, {@link #SUBSTITUTE_LANGUAGE},
-     *                    {@link #SUBSTITUTE_COUNTRY} or other bit masks.
-     */
-    public Context(final ValueConverter converter, final ReferenceResolver resolver,
-            final Version versionGML, final Map<String,String> schemas,
-            final Locale locale, final TimeZone timezone, final int bitMasks)
+     * @param  bitMasks        A combination of {@link #MARSHALLING}, {@code SUBSTITUTE_*} or other bit masks.
+     * @param  locale          The locale, or {@code null} if unspecified.
+     * @param  timezone        The timezone, or {@code null} if unspecified.
+     * @param  schemas         The schemas root URL, or {@code null} if none.
+     * @param  versionGML      The GML version, or {@code null}.
+     * @param  resolver        The resolver in use.
+     * @param  converter       The converter in use.
+     * @param  warningListener The object to inform about warnings.
+     */
+    public Context(final int                bitMasks,
+                   final Locale             locale,   final TimeZone       timezone,
+                   final Map<String,String> schemas,  final Version        versionGML,
+                   final ReferenceResolver  resolver, final ValueConverter converter,
+                   final WarningListener<?> warningListener)
     {
-        this.converter  = converter;
-        this.resolver   = resolver;
-        this.versionGML = versionGML;
-        this.schemas    = schemas; // No clone, because this class is internal.
-        this.locale     = locale;
-        this.timezone   = timezone;
-        this.bitMasks   = bitMasks;
+        this.bitMasks        = bitMasks;
+        this.locale          = locale;
+        this.timezone        = timezone;
+        this.schemas         = schemas; // No clone, because this class is internal.
+        this.versionGML      = versionGML;
+        this.resolver        = resolver;
+        this.converter       = converter;
+        this.warningListener = warningListener;
         previous = current();
         CURRENT.set(this);
     }
@@ -160,32 +187,20 @@ public final class Context extends Marsh
      */
     private Context(final Context previous) {
         if (previous != null) {
-            converter  = previous.converter;
-            resolver   = previous.resolver;
-            versionGML = previous.versionGML;
-            schemas    = previous.schemas;
-            locale     = previous.locale;
-            timezone   = previous.timezone;
-            bitMasks   = previous.bitMasks;
+            bitMasks         = previous.bitMasks;
+            locale           = previous.locale;
+            timezone         = previous.timezone;
+            schemas          = previous.schemas;
+            versionGML       = previous.versionGML;
+            resolver         = previous.resolver;
+            converter        = previous.converter;
+            warningListener  = previous.warningListener;
         }
         this.previous = previous;
         CURRENT.set(this);
     }
 
     /**
-     * Returns the schema version of the XML document being (un)marshalled.
-     * See the super-class javadoc for the list of prefix that we shall support.
-     */
-    @Override
-    public final Version getVersion(final String prefix) {
-        if (prefix.equals("gml")) {
-            return versionGML;
-        }
-        // Future SIS versions may add more cases here.
-        return null;
-    }
-
-    /**
      * Returns the locale to use for marshalling, or {@code null} if no locale were explicitly
      * specified.
      */
@@ -203,6 +218,19 @@ public final class Context extends Marsh
         return timezone;
     }
 
+    /**
+     * Returns the schema version of the XML document being (un)marshalled.
+     * See the super-class javadoc for the list of prefix that we shall support.
+     */
+    @Override
+    public final Version getVersion(final String prefix) {
+        if (prefix.equals("gml")) {
+            return versionGML;
+        }
+        // Future SIS versions may add more cases here.
+        return null;
+    }
+
     /*
      * ---- END OF PUBLIC API --------------------------------------------------------------
      *
@@ -250,22 +278,58 @@ public final class Context extends Marsh
     }
 
     /**
-     * Returns the value converter in use for the current marshalling or unmarshalling process.
-     * If no converter were explicitely set, then this method returns {@link ValueConverter#DEFAULT}.
+     * Returns the base URL of ISO 19139 (or other standards) schemas.
+     * The valid values are documented in the {@link org.apache.sis.xml.XML#SCHEMAS} property.
+     * If the returned value is not empty, then this method guarantees it ends with {@code '/'}.
      *
      * {@note This method is static for the convenience of performing the check for null context.}
      *
      * @param  context The current context, or {@code null} if none.
-     * @return The current value converter (never null).
+     * @param  key One of the value documented in the "<cite>Map key</cite>" column of
+     *         {@link org.apache.sis.xml.XML#SCHEMAS}.
+     * @param  defaultSchema The value to return if no schema is found for the given key.
+     * @return The base URL of the schema, or an empty buffer if none were specified.
      */
-    public static ValueConverter converter(final Context context) {
+    public static StringBuilder schema(final Context context, final String key, String defaultSchema) {
+        final StringBuilder buffer = new StringBuilder(128);
         if (context != null) {
-            final ValueConverter converter = context.converter;
-            if (converter != null) {
-                return converter;
+            final Map<String,String> schemas = context.schemas;
+            if (schemas != null) {
+                final String schema = schemas.get(key);
+                if (schema != null) {
+                    defaultSchema = schema;
+                }
             }
         }
-        return ValueConverter.DEFAULT;
+        buffer.append(defaultSchema);
+        final int length = buffer.length();
+        if (length != 0 && buffer.charAt(length - 1) != '/') {
+            buffer.append('/');
+        }
+        return buffer;
+    }
+
+    /**
+     * Returns {@code true} if the GML version is equals or newer than the specified version.
+     * If no GML version were specified, then this method returns {@code true}, i.e. newest
+     * version is assumed.
+     *
+     * {@note This method is static for the convenience of performing the check for null context.}
+     *
+     * @param  context The current context, or {@code null} if none.
+     * @param  version The version to compare to.
+     * @return {@code true} if the GML version is equals or newer than the specified version.
+     *
+     * @see #getVersion(String)
+     */
+    public static boolean isGMLVersion(final Context context, final Version version) {
+        if (context != null) {
+            final Version versionGML = context.versionGML;
+            if (versionGML != null) {
+                return versionGML.compareTo(version) >= 0;
+            }
+        }
+        return true;
     }
 
     /**
@@ -288,51 +352,74 @@ public final class Context extends Marsh
     }
 
     /**
-     * Returns the base URL of ISO 19139 (or other standards) schemas.
-     * The valid values are documented in the {@link org.apache.sis.xml.XML#SCHEMAS} property.
+     * Returns the value converter in use for the current marshalling or unmarshalling process.
+     * If no converter were explicitely set, then this method returns {@link ValueConverter#DEFAULT}.
      *
      * {@note This method is static for the convenience of performing the check for null context.}
      *
      * @param  context The current context, or {@code null} if none.
-     * @param  key One of the value documented in the "<cite>Map key</cite>" column of
-     *         {@link org.apache.sis.xml.XML#SCHEMAS}.
-     * @param  defaultSchema The value to return if no schema is found for the given key.
-     * @return The base URL of the schema, or {@code null} if none were specified.
+     * @return The current value converter (never null).
      */
-    public static String schema(final Context context, final String key, final String defaultSchema) {
+    public static ValueConverter converter(final Context context) {
         if (context != null) {
-            final Map<String,String> schemas = context.schemas;
-            if (schemas != null) {
-                final String schema = schemas.get(key);
-                if (schema != null) {
-                    return schema;
-                }
+            final ValueConverter converter = context.converter;
+            if (converter != null) {
+                return converter;
             }
         }
-        return defaultSchema;
+        return ValueConverter.DEFAULT;
     }
 
     /**
-     * Returns {@code true} if the GML version is equals or newer than the specified version.
-     * If no GML version were specified, then this method returns {@code true}, i.e. newest
-     * version is assumed.
-     *
-     * {@note This method is static for the convenience of performing the check for null context.}
-     *
-     * @param  context The current context, or {@code null} if none.
-     * @param  version The version to compare to.
-     * @return {@code true} if the GML version is equals or newer than the specified version.
-     *
-     * @see #getVersion(String)
-     */
-    public static boolean isGMLVersion(final Context context, final Version version) {
+     * Sends the given warning to the warning listener if there is one, or logs the warning otherwise.
+     * In the later case, this method logs to the logger specified by {@link LogRecord#getLoggerName()}
+     * if defined, or to the {@code "org.apache.sis.xml"} logger otherwise.
+     *
+     * @param context The current context, or {@code null} if none.
+     * @param source  The object that emitted a warning. Can not be null.
+     * @param warning The warning.
+     */
+    @SuppressWarnings("unchecked")
+    public static void warningOccured(final Context context, final Object source, final LogRecord warning) {
+        String logger = warning.getLoggerName();
+        if (logger == null) {
+            warning.setLoggerName(logger = "org.apache.sis.xml");
+        }
         if (context != null) {
-            final Version versionGML = context.versionGML;
-            if (versionGML != null) {
-                return versionGML.compareTo(version) >= 0;
+            final WarningListener<?> warningListener = context.warningListener;
+            if (warningListener != null && warningListener.getSourceClass().isInstance(source)) {
+                ((WarningListener) warningListener).warningOccured(source, warning);
+                return;
             }
         }
-        return true;
+        /*
+         * Log the warning without stack-trace, since this method shall be used only for non-fatal warnings
+         * and we want to avoid polluting the logs.
+         */
+        warning.setThrown(null);
+        Logging.getLogger(logger).log(warning);
+    }
+
+    /**
+     * Convenience method for sending a warning for the given exception.
+     * The logger will be {@code "org.apache.sis.xml"}.
+     *
+     * @param context The current context, or {@code null} if none.
+     * @param source  The object that emitted a warning. Can not be null.
+     * @param classe  The name of the class to declare as the warning source.
+     * @param method  The name of the method to declare as the warning source.
+     * @param cause   The exception which occurred.
+     * @param warning {@code true} for {@link Level#WARNING}, or {@code false} for {@link Level#FILE}.
+     */
+    public static void warningOccured(final Context context, final Object source, final Class<?> classe,
+            final String method, final Exception cause, final boolean warning)
+    {
+        final LogRecord record = new LogRecord(warning ? Level.WARNING : Level.FINE,
+                Exceptions.formatChainedMessages(context != null ? context.getLocale() : null, null, cause));
+        record.setSourceClassName(classe.getCanonicalName());
+        record.setSourceMethodName(method);
+        record.setThrown(cause);
+        warningOccured(context, source, record);
     }
 
     /**

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -273,7 +273,7 @@ public class IdentifierMapAdapter extend
             }
         }
         if (code != null) {
-            identifiers.add(SpecializedIdentifier.parse(authority, code));
+            identifiers.add(SpecializedIdentifier.parse(this, authority, code));
         }
         return old;
     }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -22,6 +22,7 @@ import java.util.Collection;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
 import org.apache.sis.xml.IdentifierSpace;
+import org.apache.sis.xml.ValueConverter;
 import org.apache.sis.xml.XLink;
 
 // Related to JDK7
@@ -92,7 +93,9 @@ public final class IdentifierMapWithSpec
     /**
      * Sets the {@code xlink:href} value, which may be null. If an explicit {@code xlink:href}
      * identifier exists, it is removed before to set the new {@code href} in the {@link XLink}
-     * object.
+     * object. The intend is to give precedence to the {@link XLink#getHRef()} property in every
+     * cases where the {@code href} is parsable as a {@link URI}, and use the value associated
+     * to the {@code HREF} key only as a fallback when the string can not be parsed.
      */
     private URI setHRef(final URI href) {
         URI old = super.putSpecialized(IdentifierSpace.HREF, null);
@@ -175,35 +178,54 @@ public final class IdentifierMapWithSpec
 
     /**
      * {@inheritDoc}
+     *
+     * <p>If the given {@code authority} is {@code HREF} and if the given string is parsable as a {@link URI},
+     * then this method will actually store the value as the {@link XLink#getHRef()} property of the {@code XLink}
+     * associated to the {@code XLINK} key. Only if the given string can not be parsed, then the value is stored
+     * <cite>as-is</cite> under the {@code HREF} key.</p>
      */
     @Override
     public String put(final Citation authority, final String code)
             throws UnsupportedOperationException
     {
+        final Context   context;
+        final Object    removed;
+        final Class<?>  type;
         final Exception exception;
         switch (specialCase(authority)) {
             default: {
                 return super.put(authority, code);
             }
             case NonMarshalledAuthority.HREF: {
-                URI id = null;
-                if (code != null) try {
-                    id = new URI(code);
-                } catch (URISyntaxException e) {
-                    exception = e;
-                    break;
+                URI uri = null;
+                if (code != null) {
+                    context = Context.current();
+                    final ValueConverter converter = Context.converter(context);
+                    try {
+                        uri = converter.toURI(context, code);
+                    } catch (URISyntaxException e) {
+                        exception = e;
+                        removed = setHRef(null);
+                        type = URI.class;
+                        break;
+                    }
                 }
                 final String old = getUnspecialized(authority);
-                id = setHRef(id);
-                return (id != null) ? id.toString() : old;
+                uri = setHRef(uri);
+                return (uri != null) ? uri.toString() : old;
             }
         }
-        SpecializedIdentifier.parseFailure(exception);
-        return super.put(authority, code);
+        SpecializedIdentifier.parseFailure(context, this, code, type, exception);
+        final String old = super.put(authority, code);
+        return (old == null && removed != null) ? removed.toString() : old;
     }
 
     /**
      * {@inheritDoc}
+     *
+     * <p>If the given {@code authority} is {@code HREF}, then this method will actually store the value
+     * as the {@link XLink#getHRef()} property of the {@code XLink} associated to the {@code XLINK} key.
+     * The previous {@code HREF} value, if any, is discarded.</p>
      */
     @Override
     @SuppressWarnings("unchecked")
@@ -211,8 +233,12 @@ public final class IdentifierMapWithSpec
             throws UnsupportedOperationException
     {
         switch (specialCase(authority)) {
-            default: return super.putSpecialized(authority, value);
-            case NonMarshalledAuthority.HREF: return (T) setHRef((URI)  value);
+            default: {
+                return super.putSpecialized(authority, value);
+            }
+            case NonMarshalledAuthority.HREF: {
+                return (T) setHRef((URI) value);
+            }
         }
     }
 }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -283,6 +283,10 @@ public final class NonMarshalledAuthorit
                 warningLogged = true;
                 final LogRecord record = Errors.getResources(null).getLogRecord(Level.WARNING,
                         Errors.Keys.MissingRequiredModule_1, "sis-metadata");
+                /*
+                 * Log directly the the logger rather than invoking the Context.warningOccured(…) method because
+                 * this warning does not occur during XML (un)marshalling. It may occurs only during serialization.
+                 */
                 record.setThrown(e);
                 Logging.log(NonMarshalledAuthority.class, "readResolve", record);
             }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -20,13 +20,16 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.UUID;
 import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
 import org.apache.sis.xml.XLink;
 import org.apache.sis.xml.IdentifierMap;
 import org.apache.sis.xml.IdentifierSpace;
+import org.apache.sis.xml.ValueConverter;
 import org.apache.sis.util.Debug;
-import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Messages;
 import org.apache.sis.internal.util.Citations;
 
 // Related to JDK7
@@ -89,43 +92,43 @@ public final class SpecializedIdentifier
      * authorities declared in the {@link IdentifierSpace} interface. Otherwise a
      * plain {@link IdentifierMapEntry} is created.
      *
+     * @param source    The object to declare as the source in case of failure.
      * @param authority The authority, typically as one of the {@link IdentifierSpace} constants.
-     * @param code The identifier code to parse.
+     * @param code      The identifier code to parse.
      *
      * @see IdentifierMapAdapter#put(Citation, String)
      */
-    static Identifier parse(final Citation authority, final String code) {
+    static Identifier parse(final IdentifierMap source, final Citation authority, final String code) {
         if (authority instanceof NonMarshalledAuthority) {
-            switch (((NonMarshalledAuthority) authority).ordinal) {
+            final int ordinal = ((NonMarshalledAuthority) authority).ordinal;
+            switch (ordinal) {
                 case NonMarshalledAuthority.ID: {
                     return new SpecializedIdentifier<String>(IdentifierSpace.ID, code);
                 }
                 case NonMarshalledAuthority.UUID: {
+                    final Context context = Context.current();
+                    final ValueConverter converter = Context.converter(context);
                     try {
-                        return new SpecializedIdentifier<UUID>(IdentifierSpace.UUID, UUID.fromString(code));
+                        return new SpecializedIdentifier<UUID>(IdentifierSpace.UUID, converter.toUUID(context, code));
                     } catch (IllegalArgumentException e) {
-                        parseFailure(e);
+                        parseFailure(context, source, code, UUID.class, e);
                         break;
                     }
                 }
-                case NonMarshalledAuthority.HREF: {
-                    final URI href;
-                    try {
-                        href = new URI(code);
-                    } catch (URISyntaxException e) {
-                        parseFailure(e);
-                        break;
-                    }
-                    return new SpecializedIdentifier<URI>(IdentifierSpace.HREF, href);
-                }
+                case NonMarshalledAuthority.HREF:
                 case NonMarshalledAuthority.XLINK: {
+                    final Context context = Context.current();
+                    final ValueConverter converter = Context.converter(context);
                     final URI href;
                     try {
-                        href = new URI(code);
+                        href = converter.toURI(context, code);
                     } catch (URISyntaxException e) {
-                        parseFailure(e);
+                        parseFailure(context, source, code, URI.class, e);
                         break;
                     }
+                    if (ordinal == NonMarshalledAuthority.HREF) {
+                        return new SpecializedIdentifier<URI>(IdentifierSpace.HREF, href);
+                    }
                     final XLink xlink = new XLink();
                     xlink.setHRef(href);
                     return new SpecializedIdentifier<XLink>(IdentifierSpace.XLINK, xlink);
@@ -139,14 +142,31 @@ public final class SpecializedIdentifier
      * Invoked by {@link #parse(Citation,String)} when a string can not be parsed.
      * This is considered a non-fatal error, because the parse method can fallback
      * on the generic {@link IdentifierMapEntry} in such cases.
-     */
-    static void parseFailure(final Exception e) {
-        // IdentifierMap.put(Citation,String) is the public facade.
-        Logging.recoverableException(IdentifierMap.class, "put", e);
+     *
+     * <p>This method assumes that {@link IdentifierMap#put(Citation, String)} is
+     * the public API by which this method has been invoked.</p>
+     *
+     * @param context The marshalling context, or {@code null} if none.
+     * @param source  The object to declare as the source of the warning.
+     * @param value   The value that we failed to parse.
+     * @param type    The target type of the parsing process.
+     * @param cause   The exception that occurred during the parsing process.
+     */
+    static void parseFailure(final Context context, final IdentifierMap source,
+            final String value, final Class<?> type, final Exception cause)
+    {
+        final Messages resources = Messages.getResources(context != null ? context.getLocale() : null);
+        final LogRecord record = resources.getLogRecord(Level.WARNING, Messages.Keys.UnparsableValueStoredAsText_2, type, value);
+        record.setSourceClassName(IdentifierMap.class.getCanonicalName());
+        record.setSourceMethodName("put");
+        record.setThrown(cause);
+        Context.warningOccured(context, source, record);
     }
 
     /**
      * Returns the authority specified at construction time.
+     *
+     * @return The identifier authority.
      */
     @Override
     public Citation getAuthority() {
@@ -166,6 +186,8 @@ public final class SpecializedIdentifier
     /**
      * Returns a string representation of the {@linkplain #getValue() identifier value},
      * or {@code null} if none.
+     *
+     * @return The identifier value.
      */
     @Override
     public String getCode() {

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -47,9 +47,9 @@ import org.apache.sis.internal.system.Sy
  */
 public abstract class TypeRegistration {
     /**
-     * The JAXB context, or {@code null} if not yet created.
+     * The JAXB context, or {@code null} if not yet created or if the classpath changed.
      */
-    private static Reference<JAXBContext> context;
+    private static volatile Reference<JAXBContext> context;
     static {
         SystemListener.add(new SystemListener(Modules.UTILITIES) {
             @Override protected void classpathChanged() {
@@ -105,9 +105,9 @@ public abstract class TypeRegistration {
      * @throws JAXBException If an error occurred while creating the JAXB context.
      */
     public static synchronized JAXBContext getSharedContext() throws JAXBException {
-        final Reference<JAXBContext> ref = context; // Protect from changes by the listener.
-        if (ref != null) {
-            final JAXBContext instance = ref.get();
+        final Reference<JAXBContext> c = context;
+        if (c != null) {
+            final JAXBContext instance = c.get();
             if (instance != null) {
                 return instance;
             }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/CharSequenceAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/CharSequenceAdapter.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/CharSequenceAdapter.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/CharSequenceAdapter.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -41,7 +41,7 @@ import org.apache.sis.internal.jaxb.gmd.
  * @author  Guilhem Legal (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-3.00)
- * @version 0.3
+ * @version 0.4
  * @module
  *
  * @see StringAdapter
@@ -71,7 +71,7 @@ public final class CharSequenceAdapter e
      * in a XML file or stream. JAXB calls automatically this method at marshalling time.
      *
      * @param  value The string value.
-     * @return The wrapper for the given character sequence.
+     * @return The wrapper for the given character sequence, or {@code null}.
      */
     @Override
     public GO_CharacterString marshal(final CharSequence value) {
@@ -83,11 +83,11 @@ public final class CharSequenceAdapter e
      * in a XML file or stream.
      *
      * @param  value The character representation of the object being marshalled.
-     * @return The wrapper for the given character sequence.
+     * @return The wrapper for the given character sequence, or {@code null}.
      */
     public static GO_CharacterString wrap(CharSequence value) {
         if (value instanceof String) {
-            return wrap(value, (String) value); // Slightly more efficient variant of this method.
+            return wrap(Context.current(), value, (String) value); // Slightly more efficient variant of this method.
         }
         /*
          * <gmd:someElement xsi:type="gmd:PT_FreeText_PropertyType">
@@ -147,25 +147,38 @@ public final class CharSequenceAdapter e
      * This method is a copy of {@link #wrap(CharSequence)} simplified for the case when we know
      * that the character sequence being marshalled is a string.
      *
-     * @param  object The object being marshalled (e.g. {@code URI} or {@code Locale}).
-     * @param  string The string representation of the object being marshalled.
-     * @return The wrapper for the given character sequence.
+     * @param  context The current (un)marshalling context, or {@code null} if none.
+     * @param  object  The object being marshalled (e.g. {@code URI} or {@code Locale}).
+     * @param  string  The string representation of the object being marshalled.
+     * @return The wrapper for the given character sequence, or {@code null}.
      */
-    public static GO_CharacterString wrap(final Object object, String string) {
+    public static GO_CharacterString wrap(final Context context, final Object object, final String string) {
+        final CharSequence text = value(context, object, string);
+        return (text != null) ? new GO_CharacterString(text) : null;
+    }
+
+    /**
+     * Same as {@link #wrap(Object, String)}, but returns directly the {@link GO_CharacterString#text}
+     * value without wrapping in a {@code GO_CharacterString} instance.
+     *
+     * @param  context The current (un)marshalling context, or {@code null} if none.
+     * @param  object  The object being marshalled (e.g. {@code URI} or {@code Locale}).
+     * @param  string  The string representation of the object being marshalled.
+     * @return The text value for the given character sequence, or {@code null}.
+     */
+    public static CharSequence value(final Context context, final Object object, String string) {
         string = CharSequences.trimWhitespaces(string);
         if (string == null || string.isEmpty()) {
             return null;
         }
-        CharSequence value = string;
-        final Context context = Context.current();
         final XLink linkage = Context.resolver(context).anchor(context, object, string);
         if (linkage != null) {
             if (linkage instanceof Anchor) {
-                value = (Anchor) linkage;
+                return (Anchor) linkage;
             } else {
-                value = new Anchor(linkage, string);
+                return new Anchor(linkage, string);
             }
         }
-        return new GO_CharacterString(value);
+        return string;
     }
 }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -17,40 +17,21 @@
 package org.apache.sis.internal.jaxb.gco;
 
 import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.adapters.XmlAdapter;
 
 
 /**
  * Surrounds boolean value by {@code <gco:Boolean>}.
- * The ISO-19139 standard specifies that primitive types have to be surrounded by an element
- * which represents the type of the value, using the namespace {@code gco} linked to the
- * {@code http://www.isotc211.org/2005/gco} URL. The JAXB default behavior is to marshal
- * primitive Java types directly "as is", without wrapping the value in the required element.
- * The role of this class is to add such wrapping.
+ * The ISO-19139 standard requires most types to be surrounded by an element representing the value type.
+ * The JAXB default behavior is to marshal primitive Java types directly, without such wrapper element.
+ * The role of this class is to add the {@code <gco:…>} wrapper element required by ISO 19139.
  *
  * @author  Cédric Briançon (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.5)
- * @version 0.3
+ * @version 0.4
  * @module
  */
-public final class GO_Boolean extends XmlAdapter<GO_Boolean, Boolean> {
-    /**
-     * Wraps the {@link Boolean#TRUE} value.
-     */
-    private static final GO_Boolean TRUE = new GO_Boolean(Boolean.TRUE);
-
-    /**
-     * Wraps the {@link Boolean#FALSE} value.
-     */
-    private static final GO_Boolean FALSE = new GO_Boolean(Boolean.FALSE);
-
-    /**
-     * The boolean value to handle.
-     */
-    @XmlElement(name = "Boolean")
-    public Boolean value;
-
+public final class GO_Boolean extends PropertyType<GO_Boolean, Boolean> {
     /**
      * Empty constructor used only by JAXB.
      */
@@ -58,23 +39,20 @@ public final class GO_Boolean extends Xm
     }
 
     /**
-     * Constructs an adapter for the given value.
+     * Constructs a wrapper for the given value.
      *
      * @param value The value.
      */
     private GO_Boolean(final Boolean value) {
-        this.value = value;
+        super(value, value.booleanValue() == false && value != Boolean.FALSE);
     }
 
     /**
-     * Allows JAXB to generate a Boolean object using the value found in the adapter.
-     *
-     * @param value The value wrapped in an adapter.
-     * @return The boolean value extracted from the adapter.
+     * Returns the Java type which is bound by this adapter.
      */
     @Override
-    public Boolean unmarshal(final GO_Boolean value) {
-        return (value != null) ? value.value : null;
+    protected Class<Boolean> getBoundType() {
+        return Boolean.class;
     }
 
     /**
@@ -86,12 +64,26 @@ public final class GO_Boolean extends Xm
      *         by {@code <gco:Boolean>} element.
      */
     @Override
-    public GO_Boolean marshal(final Boolean value) {
-        if (value == null) {
-            return null;
-        }
-        final GO_Boolean c = value ? TRUE : FALSE;
-        assert value.equals(c.value) : value;
-        return c;
+    protected GO_Boolean wrap(final Boolean value) {
+        return new GO_Boolean(value);
+    }
+
+    /**
+     * Invoked by JAXB at marshalling time for getting the actual value to write.
+     *
+     * @return The value to be marshalled.
+     */
+    @XmlElement(name = "Boolean")
+    public Boolean getElement() {
+        return skip() ? null : metadata;
+    }
+
+    /**
+     * Invoked by JAXB at unmarshalling time for storing the result temporarily.
+     *
+     * @param metadata The unmarshalled value.
+     */
+    public void setElement(final Boolean metadata) {
+        this.metadata = metadata;
     }
 }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_CharacterString.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_CharacterString.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_CharacterString.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_CharacterString.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -16,32 +16,77 @@
  */
 package org.apache.sis.internal.jaxb.gco;
 
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlSeeAlso;
-
 import org.apache.sis.xml.Namespaces;
-import org.apache.sis.internal.jaxb.gmd.PT_FreeText;
-import org.apache.sis.internal.jaxb.gmd.LanguageCode;
-import org.apache.sis.internal.jaxb.gmd.Country;
+import org.apache.sis.internal.jaxb.Context;
 import org.apache.sis.internal.jaxb.gmx.Anchor;
+import org.apache.sis.internal.jaxb.gmx.FileName;
+import org.apache.sis.internal.jaxb.gmx.MimeFileType;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.resources.Messages;
 
 
 /**
- * JAXB wrapper for string value in a {@code <gco:CharacterString>} or {@code <gmx:Anchor>} element,
- * for ISO-19139 compliance.
+ * JAXB wrapper for string value in a {@code <gco:CharacterString>}, {@code <gmx:Anchor>},
+ * {@code <gmx:FileName>} or {@code <gmx:MimeFileType>} element, for ISO-19139 compliance.
+ *
+ * <p>{@code FileName} and {@code MimeFileType} are possible substitutions for {@code CharacterString}.
+ * They make sense only in {@link org.apache.sis.metadata.iso.identification.DefaultBrowseGraphic} or
+ * other classes using URI, but the XML schema does not prevent their usage in place of other strings.
+ * Consequently we unconditionally accept {@code FileName} and {@code MimeFileType} at unmarshalling time.
+ * However marshalling will use the appropriate element for the kind of property to marshal.</p>
  *
  * @author  Cédric Briançon (Geomatys)
  * @since   0.3 (derived from geotk-2.5)
- * @version 0.3
+ * @version 0.4
  * @module
  *
  * @see org.apache.sis.internal.jaxb.gmd.PT_FreeText
  */
 @XmlType(name = "CharacterString_PropertyType")
-@XmlSeeAlso({PT_FreeText.class, LanguageCode.class, Country.class})
+@XmlSeeAlso({
+    org.apache.sis.internal.jaxb.gmd.PT_FreeText.class,
+    org.apache.sis.internal.jaxb.gmd.LanguageCode.class,
+    org.apache.sis.internal.jaxb.gmd.Country.class
+})
 public class GO_CharacterString {
+    /*
+     * Numerical values below are ordered: if two or more values are defined (thoerically not legal,
+     * but we try to be robust), the value associated to the highest constant has precedence.
+     */
+    /**
+     * Value assigned to {@link #type} if the character string
+     * shall be marshalled as a {@code <gmx:MimeFileType>} element.
+     */
+    public static final byte MIME_TYPE = 1;
+
+    /**
+     * Value assigned to {@link #type} if the character string
+     * shall be marshalled as a {@code <gmx:FileName>} element.
+     */
+    public static final byte FILENAME = 2;
+
+    /**
+     * Value assigned to {@link #type} if the current {@link #text}
+     * has been found in a {@code <gco:CharacterString>} element.
+     */
+    private static final byte ANCHOR = 3;
+
+    /**
+     * The XML element names for each possible {@link #type} values.
+     */
+    private static final String[] NAMES = new String[4];
+    static {
+        NAMES[0]         = "CharacterString";
+        NAMES[MIME_TYPE] = "MimeFileType";
+        NAMES[FILENAME]  = "FileName";
+        NAMES[ANCHOR]    = "Anchor";
+    }
+
     /**
      * The text or anchor value, or {@code null} if none. May be an instance
      * of {@link Anchor}, which needs to be handled in a special way.
@@ -49,13 +94,22 @@ public class GO_CharacterString {
     private CharSequence text;
 
     /**
+     * 0 if the text shall be marshalled as a {@code <gco:CharacterString>} or an anchor,
+     * or one of the static constants in this class otherwise.
+     *
+     * @see #FILENAME
+     * @see #MIME_TYPE
+     */
+    public byte type;
+
+    /**
      * Empty constructor for JAXB and subclasses.
      */
-    protected GO_CharacterString() {
+    public GO_CharacterString() {
     }
 
     /**
-     * Builds an wrapper for the given text.
+     * Builds a wrapper for the given text.
      *
      * @param text The string to marshal, or {@code null} if none.
      */
@@ -64,38 +118,118 @@ public class GO_CharacterString {
     }
 
     /**
-     * Builds an wrapper as a copy of the given one.
+     * Sets the {@link #text} field to the given value.
+     * If the given value overwrites a previous one, a warning is emitted.
      *
-     * @param text The wrapper to copy, or {@code null} if none.
+     * @param value    The value to set.
+     * @param property 0 or one of the {@link #MIME_TYPE}, {@link #FILENAME} or {@link #ANCHOR} constants.
      */
-    protected GO_CharacterString(final GO_CharacterString text) {
-        if (text != null) {
-            this.text = text.text;
+    private void setText(final CharSequence value, byte property) {
+        if (text != null && !value.equals(text)) {
+            /*
+             * The given value overwrite a previous one. Determine which value will be discarded
+             * using the 'type' value as a criterion, then emit a warning.
+             */
+            byte discarded = type;
+            boolean noset = false;
+            if (discarded > property) {
+                discarded = property;
+                property  = type;
+                noset     = true;
+            }
+            final Context context = Context.current();
+            final LogRecord record = Messages.getResources(context != null ? context.getLocale() : null)
+                    .getLogRecord(Level.WARNING, Messages.Keys.DiscardedExclusiveProperty_2,
+                                  NAMES[discarded], NAMES[property]);
+            record.setSourceClassName(getClass().getCanonicalName());
+            record.setSourceMethodName("setText");
+            Context.warningOccured(context, value, record);
+            if (noset) {
+                return;
+            }
         }
+        text = value;
+        type = property;
     }
 
     /**
-     * Returns the text. This method is called by JAXB at marshalling time.
+     * Returns the text in a {@code <gco:CharacterString>} element, or {@code null} if none.
      *
      * @return The text, or {@code null}.
      */
     @XmlElement(name = "CharacterString")
     public final String getCharacterString() {
-        final CharSequence text = this.text;
-        return (text == null || text instanceof Anchor) ? null : text.toString();
+        if (type == 0) {
+            final CharSequence text = this.text;
+            if (text != null && !(text instanceof Anchor)) {
+                return text.toString();
+            }
+        }
+        return null;
     }
 
     /**
      * Sets the value to the given string. This method is called by JAXB at unmarshalling time.
      *
-     * @param text The new text.
+     * @param value The new text.
      */
-    public final void setCharacterString(String text) {
-        text = CharSequences.trimWhitespaces(text);
-        if (text != null && text.isEmpty()) {
-            text = null;
+    public final void setCharacterString(String value) {
+        value = CharSequences.trimWhitespaces(value);
+        if (value != null && !value.isEmpty()) {
+            setText(value, (byte) 0);
+        }
+    }
+
+    /**
+     * Returns the text in a {@code <gmx:FileName>} element, or {@code null} if none.
+     */
+    @XmlElement(name = "FileName", namespace = Namespaces.GMX)
+    final FileName getFileName() {
+        if (type == FILENAME) {
+            final CharSequence text = this.text;
+            if (text != null && !(text instanceof Anchor)) {
+                return new FileName(text.toString());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Invoked by JAXB for setting the filename.
+     */
+    final void setFileName(final FileName file) {
+        if (file != null) {
+            final String value = CharSequences.trimWhitespaces(file.toString());
+            if (value != null && !value.isEmpty()) {
+                setText(value, FILENAME);
+            }
+        }
+    }
+
+    /**
+     * Returns the text in a {@code <gmx:MimeFileType>} element, or {@code null} if none.
+     */
+    @XmlElement(name = "MimeFileType", namespace = Namespaces.GMX)
+    final MimeFileType getMimeFileType() {
+        if (type == MIME_TYPE) {
+            final CharSequence text = this.text;
+            if (text != null && !(text instanceof Anchor)) {
+                return new MimeFileType(text.toString());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Invoked by JAXB for setting the MIME type.
+     */
+    final void setMimeFileType(final MimeFileType type) {
+        if (type != null) {
+            final String value = CharSequences.trimWhitespaces(type.toString());
+            if (value != null && !value.isEmpty()) {
+                setText(value, MIME_TYPE);
+            }
         }
-        this.text = text;
     }
 
     /**
@@ -117,7 +251,7 @@ public class GO_CharacterString {
      * @param anchor The new anchor.
      */
     public final void setAnchor(final Anchor anchor) {
-        this.text = anchor;
+        setText(anchor, ANCHOR);
     }
 
     /**

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Decimal.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Decimal.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Decimal.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Decimal.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -17,16 +17,19 @@
 package org.apache.sis.internal.jaxb.gco;
 
 import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.adapters.XmlAdapter;
 
 
 /**
  * Surrounds double values by {@code <gco:Decimal>}.
- * The ISO-19139 standard specifies that primitive types have to be surrounded by an element
- * which represents the type of the value, using the namespace {@code gco} linked to the
- * {@code http://www.isotc211.org/2005/gco} URL. The JAXB default behavior is to marshal
- * primitive Java types directly "as is", without wrapping the value in the required element.
- * The role of this class is to add such wrapping.
+ * The ISO-19139 standard requires most types to be surrounded by an element representing the value type.
+ * The JAXB default behavior is to marshal primitive Java types directly, without such wrapper element.
+ * The role of this class is to add the {@code <gco:…>} wrapper element required by ISO 19139.
+ *
+ * {@section Relationship with <code>GO_Real</code>}
+ * This adapter is identical to {@link GO_Real} except for the element name, which is {@code "Decimal"} instead
+ * than {@code "Real"}. This adapter is used for the {@code westBoundLongitude}, {@code eastBoundLongitude},
+ * {@code southBoundLatitude} and {@code northBoundLatitude} properties of {@code EX_DefaultGeographicBoundingBox}.
+ * The {@code GO_Real} adapter is used for about everything else.
  *
  * @author  Cédric Briançon (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -35,31 +38,9 @@ import javax.xml.bind.annotation.adapter
  * @module
  *
  * @see GO_Real
- * @see AsFloat
+ * @see GO_Decimal32
  */
-public final class GO_Decimal extends XmlAdapter<GO_Decimal, Double> {
-    /**
-     * Frequently used constants.
-     */
-    private static final GO_Decimal
-            P0   = new GO_Decimal(   0.0),
-            P1   = new GO_Decimal(   1.0),
-            N1   = new GO_Decimal(  -1.0),
-            P45  = new GO_Decimal(  45.0),
-            N45  = new GO_Decimal( -45.0),
-            P90  = new GO_Decimal(  90.0),
-            N90  = new GO_Decimal( -90.0),
-            P180 = new GO_Decimal( 180.0),
-            N180 = new GO_Decimal(-180.0),
-            P360 = new GO_Decimal( 360.0),
-            N360 = new GO_Decimal(-360.0);
-
-    /**
-     * The double value to handle.
-     */
-    @XmlElement(name = "Decimal")
-    public Double value;
-
+public final class GO_Decimal extends PropertyType<GO_Decimal, Double> {
     /**
      * Empty constructor used only by JAXB.
      */
@@ -67,23 +48,20 @@ public final class GO_Decimal extends Xm
     }
 
     /**
-     * Constructs an adapter for the given value.
+     * Constructs a wrapper for the given value.
      *
      * @param value The value.
      */
     private GO_Decimal(final Double value) {
-        this.value = value;
+        super(value, value.isNaN());
     }
 
     /**
-     * Allows JAXB to generate a Double object using the value found in the adapter.
-     *
-     * @param value The value wrapped in an adapter.
-     * @return The double value extracted from the adapter.
+     * Returns the Java type which is bound by this adapter.
      */
     @Override
-    public Double unmarshal(final GO_Decimal value) {
-        return (value != null) ? value.value : null;
+    protected Class<Double> getBoundType() {
+        return Double.class;
     }
 
     /**
@@ -95,94 +73,26 @@ public final class GO_Decimal extends Xm
      *         by {@code <gco:Decimal>} element.
      */
     @Override
-    public GO_Decimal marshal(final Double value) {
-        if (value == null) {
-            return null;
-        }
-        final GO_Decimal c;
-        final int index = value.intValue();
-        if (index == value.doubleValue()) {
-            switch (index) {
-                case    0: c = P0;   break;
-                case    1: c = P1;   break;
-                case   -1: c = N1;   break;
-                case   45: c = P45;  break;
-                case  -45: c = N45;  break;
-                case   90: c = P90;  break;
-                case  -90: c = N90;  break;
-                case  180: c = P180; break;
-                case -180: c = N180; break;
-                case  360: c = P360; break;
-                case -360: c = N360; break;
-                default: c = new GO_Decimal(value);
-            }
-        } else {
-            c = new GO_Decimal(value);
-        }
-        assert value.equals(c.value) : value;
-        return c;
+    public GO_Decimal wrap(final Double value) {
+        return new GO_Decimal(value);
     }
 
-
-
+    /**
+     * Invoked by JAXB at marshalling time for getting the actual value to write.
+     *
+     * @return The value to be marshalled.
+     */
+    @XmlElement(name = "Decimal")
+    public Double getElement() {
+        return skip() ? null : metadata;
+    }
 
     /**
-     * Surrounds float values by {@code <gco:Decimal>}.
-     * The ISO-19139 standard specifies that primitive types have to be surrounded by an element
-     * which represents the type of the value, using the namespace {@code gco} linked to the
-     * {@code http://www.isotc211.org/2005/gco} URL. The JAXB default behavior is to marshal
-     * primitive Java types directly "as is", without wrapping the value in the required element.
-     * The role of this class is to add such wrapping.
+     * Invoked by JAXB at unmarshalling time for storing the result temporarily.
      *
-     * @author  Cédric Briançon (Geomatys)
-     * @since   0.3 (derived from geotk-2.5)
-     * @version 0.3
-     * @module
-     */
-    public static final class AsFloat extends XmlAdapter<AsFloat, Float> {
-        /**
-         * The float value to handle.
-         */
-        @XmlElement(name = "Decimal")
-        public Float value;
-
-        /**
-         * Empty constructor used only by JAXB.
-         */
-        public AsFloat() {
-        }
-
-        /**
-         * Constructs an adapter for the given value.
-         *
-         * @param value The value.
-         */
-        private AsFloat(final Float value) {
-            this.value = value;
-        }
-
-        /**
-         * Allows JAXB to generate a Float object using the value found in the adapter.
-         *
-         * @param value The value wrapped in an adapter.
-         * @return The float value extracted from the adapter.
-         */
-        @Override
-        public Float unmarshal(final AsFloat value) {
-            return (value != null) ? value.value : null;
-        }
-
-        /**
-         * Allows JAXB to change the result of the marshalling process, according to the
-         * ISO-19139 standard and its requirements about primitive types.
-         *
-         * @param value The float value we want to surround by an element representing its type.
-         * @return An adaptation of the float value, that is to say a float value surrounded
-         *         by {@code <gco:Decimal>} element.
-         */
-        @Override
-        public AsFloat marshal(final Float value) {
-            return (value != null) ? new AsFloat(value) : null;
-        }
+     * @param metadata The unmarshalled value.
+     */
+    public void setElement(final Double metadata) {
+        this.metadata = metadata;
     }
 }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_GenericName.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_GenericName.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_GenericName.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_GenericName.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -16,19 +16,8 @@
  */
 package org.apache.sis.internal.jaxb.gco;
 
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.adapters.XmlAdapter;
-
-import org.opengis.util.TypeName;
-import org.opengis.util.LocalName;
-import org.opengis.util.MemberName;
 import org.opengis.util.GenericName;
-
 import org.apache.sis.util.iso.AbstractName;
-import org.apache.sis.util.iso.DefaultTypeName;
-import org.apache.sis.util.iso.DefaultMemberName;
-import org.apache.sis.util.iso.DefaultScopedName;
-import org.apache.sis.util.resources.Errors;
 
 
 /**
@@ -50,12 +39,7 @@ import org.apache.sis.util.resources.Err
  * @version 0.3
  * @module
  */
-public final class GO_GenericName extends XmlAdapter<GO_GenericName, GenericName> {
-    /**
-     * The generic name to be marshalled.
-     */
-    private AbstractName name;
-
+public final class GO_GenericName extends NameAdapter<GO_GenericName, GenericName> {
     /**
      * Empty constructor for JAXB only.
      */
@@ -65,128 +49,8 @@ public final class GO_GenericName extend
     /**
      * Wraps a name at marshalling-time.
      */
-    private GO_GenericName(final AbstractName name) {
-        this.name = name;
-    }
-
-    /**
-     * Ensures that the {@linkplain #name} is not already defined.
-     *
-     * @throws IllegalStateException If a name is already defined.
-     */
-    private void ensureUndefined() throws IllegalStateException {
-        if (name != null) {
-            throw new IllegalStateException(Errors.format(Errors.Keys.ValueAlreadyDefined_1, "name"));
-        }
-    }
-
-    /**
-     * Returns the {@code LocalName} generated from the metadata value.
-     * The local name is returned only if it is not a {@link TypeName} or a {@link MemberName}
-     * (otherwise, the corresponding {@code getXXX()} method needs to be invoked instead.
-     *
-     * @return The current local name, or {@code null} if none.
-     *
-     * @see #getTypeName()
-     * @see #getMemberName()
-     */
-    @XmlElement(name = "LocalName")
-    public String getLocalName() {
-        final Object name = this.name;
-        return (name instanceof LocalName) && !(name instanceof TypeName)
-                && !(name instanceof MemberName) ? name.toString() : null;
-    }
-
-    /**
-     * Sets the value for the {@code LocalName}.
-     * This method is called at unmarshalling-time by JAXB.
-     *
-     * @param  name The new name.
-     * @throws IllegalStateException If a name is already defined.
-     */
-    public void setLocalName(final String name) throws IllegalStateException {
-        ensureUndefined();
-        if (name == null) {
-            this.name = null;
-        } else {
-            /*
-             * Following cast should be safe because the getNameFactory() method asked specifically
-             * for a DefaultNameFactory instance, which is known to create AbstractName instances.
-             */
-            this.name = (AbstractName) LocalNameAdapter.getNameFactory().createLocalName(null, name);
-        }
-    }
-
-    /**
-     * Returns the {@code ScopedName} generated from the metadata value.
-     * This method is called at marshalling-time by JAXB.
-     *
-     * @return The current name, or {@code null} if none.
-     */
-    @XmlElement(name = "ScopedName")
-    public DefaultScopedName getScopedName() {
-        final Object name = this.name;
-        return (name instanceof DefaultScopedName) ? (DefaultScopedName) name : null;
-    }
-
-    /**
-     * Sets the value for the {@code ScopedName}.
-     * This method is called at unmarshalling-time by JAXB.
-     *
-     * @param  name The new name.
-     * @throws IllegalStateException If a name is already defined.
-     */
-    public void setScopedName(final DefaultScopedName name) throws IllegalStateException {
-        ensureUndefined();
-        this.name = name;
-    }
-
-    /**
-     * Returns the {@code TypeName} generated from the metadata value.
-     * This method is called at marshalling-time by JAXB.
-     *
-     * @return The current name, or {@code null} if none.
-     */
-    @XmlElement(name = "TypeName")
-    public DefaultTypeName getTypeName() {
-        final Object name = this.name;
-        return (name instanceof DefaultTypeName) ? (DefaultTypeName) name : null;
-    }
-
-    /**
-     * Sets the value for the {@code TypeName}.
-     * This method is called at unmarshalling-time by JAXB.
-     *
-     * @param  name The new name.
-     * @throws IllegalStateException If a name is already defined.
-     */
-    public void setTypeName(final DefaultTypeName name) throws IllegalStateException {
-        ensureUndefined();
-        this.name = name;
-    }
-
-    /**
-     * Returns the {@code MemberName} generated from the metadata value.
-     * This method is called at marshalling-time by JAXB.
-     *
-     * @return The current name, or {@code null} if none.
-     */
-    @XmlElement(name = "MemberName")
-    public DefaultMemberName getMemberName() {
-        final Object name = this.name;
-        return (name instanceof MemberName) ? (DefaultMemberName) name : null;
-    }
-
-    /**
-     * Sets the value for the {@code MemberName}.
-     * This method is called at unmarshalling-time by JAXB.
-     *
-     * @param  name The new name.
-     * @throws IllegalStateException If a name is already defined.
-     */
-    public void setMemberName(final DefaultMemberName name) throws IllegalStateException {
-        ensureUndefined();
-        this.name = name;
+    private GO_GenericName(final AbstractName value) {
+        name = value;
     }
 
     /**
@@ -198,20 +62,7 @@ public final class GO_GenericName extend
      */
     @Override
     public GO_GenericName marshal(final GenericName value) {
-        if (value == null) {
-            return null;
-        }
-        final AbstractName name;
-        if (value instanceof AbstractName) {
-            name = (AbstractName) value;
-        } else {
-            /*
-             * Following cast should be safe because the getNameFactory() method asked specifically
-             * for a DefaultNameFactory instance, which is known to create AbstractName instances.
-             */
-            name = (AbstractName) ScopedNameAdapter.wrap(value, LocalNameAdapter.getNameFactory());
-        }
-        return new GO_GenericName(name);
+        return (value != null) ? new GO_GenericName(AbstractName.castOrCopy(value)) : null;
     }
 
     /**

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Integer.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Integer.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Integer.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Integer.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -17,42 +17,23 @@
 package org.apache.sis.internal.jaxb.gco;
 
 import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.adapters.XmlAdapter;
 
 
 /**
  * Surrounds integer values by {@code <gco:Integer>}.
- * The ISO-19139 standard specifies that primitive types have to be surrounded by an element
- * which represents the type of the value, using the namespace {@code gco} linked to the
- * {@code http://www.isotc211.org/2005/gco} URL. The JAXB default behavior is to marshal
- * primitive Java types directly "as is", without wrapping the value in the required element.
- * The role of this class is to add such wrapping.
+ * The ISO-19139 standard requires most types to be surrounded by an element representing the value type.
+ * The JAXB default behavior is to marshal primitive Java types directly, without such wrapper element.
+ * The role of this class is to add the {@code <gco:…>} wrapper element required by ISO 19139.
  *
  * @author  Cédric Briançon (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.5)
- * @version 0.3
+ * @version 0.4
  * @module
  *
- * @see AsLong
+ * @see GO_Integer64
  */
-public final class GO_Integer extends XmlAdapter<GO_Integer, Integer> {
-    /**
-     * Frequently used constants.
-     */
-    private static final GO_Integer[] CONSTANTS = new GO_Integer[5];
-    static {
-        for (int i=0; i<CONSTANTS.length; i++) {
-            CONSTANTS[i] = new GO_Integer(i);
-        }
-    }
-
-    /**
-     * The integer value to handle.
-     */
-    @XmlElement(name = "Integer")
-    public Integer value;
-
+public final class GO_Integer extends PropertyType<GO_Integer, Integer> {
     /**
      * Empty constructor used only by JAXB.
      */
@@ -60,23 +41,20 @@ public final class GO_Integer extends Xm
     }
 
     /**
-     * Constructs an adapter for the given value.
+     * Constructs a wrapper for the given value.
      *
      * @param value The value.
      */
     private GO_Integer(final Integer value) {
-        this.value = value;
+        super(value, value.intValue() == 0);
     }
 
     /**
-     * Allows JAXB to generate an Integer object using the value found in the adapter.
-     *
-     * @param value The value wrapped in an adapter.
-     * @return The integer value extracted from the adapter.
+     * Returns the Java type which is bound by this adapter.
      */
     @Override
-    public Integer unmarshal(final GO_Integer value) {
-        return (value != null) ? value.value : null;
+    protected Class<Integer> getBoundType() {
+        return Integer.class;
     }
 
     /**
@@ -88,76 +66,26 @@ public final class GO_Integer extends Xm
      *         by {@code <gco:Integer>} element.
      */
     @Override
-    public GO_Integer marshal(final Integer value) {
-        if (value == null) {
-            return null;
-        }
-        final int i = value;
-        final GO_Integer c = (i >= 0 && i < CONSTANTS.length) ? CONSTANTS[i] : new GO_Integer(value);
-        assert value.equals(c.value) : value;
-        return c;
+    public GO_Integer wrap(final Integer value) {
+        return new GO_Integer(value);
     }
 
-
-
+    /**
+     * Invoked by JAXB at marshalling time for getting the actual value to write.
+     *
+     * @return The value to be marshalled.
+     */
+    @XmlElement(name = "Integer")
+    public Integer getElement() {
+        return skip() ? null : metadata;
+    }
 
     /**
-     * Surrounds long values by {@code <gco:Integer>}.
-     * The ISO-19139 standard specifies that primitive types have to be surrounded by an element
-     * which represents the type of the value, using the namespace {@code gco} linked to the
-     * {@code http://www.isotc211.org/2005/gco} URL. The JAXB default behavior is to marshal
-     * primitive Java types directly "as is", without wrapping the value in the required element.
-     * The role of this class is to add such wrapping.
+     * Invoked by JAXB at unmarshalling time for storing the result temporarily.
      *
-     * @author  Cédric Briançon (Geomatys)
-     * @since   0.3 (derived from geotk-2.5)
-     * @version 0.3
-     * @module
-     */
-    public static final class AsLong extends XmlAdapter<AsLong, Long> {
-        /**
-         * The long value to handle.
-         */
-        @XmlElement(name = "Integer")
-        public Long value;
-
-        /**
-         * Empty constructor used only by JAXB.
-         */
-        public AsLong() {
-        }
-
-        /**
-         * Constructs an adapter for the given value.
-         *
-         * @param value The value.
-         */
-        private AsLong(final Long value) {
-            this.value = value;
-        }
-
-        /**
-         * Allows JAXB to generate a Long object using the value found in the adapter.
-         *
-         * @param value The value wrapped in an adapter.
-         * @return The long value extracted from the adapter.
-         */
-        @Override
-        public Long unmarshal(final AsLong value) {
-            return (value != null) ? value.value : null;
-        }
-
-        /**
-         * Allows JAXB to change the result of the marshalling process, according to the
-         * ISO-19139 standard and its requirements about primitive types.
-         *
-         * @param value The integer value we want to surround by an element representing its type.
-         * @return An adaptation of the integer value, that is to say a integer value surrounded
-         *         by {@code <gco:Integer>} element.
-         */
-        @Override
-        public AsLong marshal(final Long value) {
-            return (value != null) ? new AsLong(value) : null;
-        }
+     * @param metadata The unmarshalled value.
+     */
+    public void setElement(final Integer metadata) {
+        this.metadata = metadata;
     }
 }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Real.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Real.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Real.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Real.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -17,31 +17,25 @@
 package org.apache.sis.internal.jaxb.gco;
 
 import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.adapters.XmlAdapter;
 
 
 /**
  * Surrounds double values by {@code <gco:Real>}.
- * The ISO-19139 standard specifies that primitive types have to be surrounded by an element
- * which represents the type of the value, using the namespace {@code gco} linked to the
- * {@code http://www.isotc211.org/2005/gco} URL. The JAXB default behavior is to marshal
- * primitive Java types directly "as is", without wrapping the value in the required element.
- * The role of this class is to add such wrapping.
+ * The ISO-19139 standard requires most types to be surrounded by an element representing the value type.
+ * The JAXB default behavior is to marshal primitive Java types directly, without such wrapper element.
+ * The role of this class is to add the {@code <gco:…>} wrapper element required by ISO 19139.
+ *
+ * {@section Relationship with <code>GO_Decimal</code>}
+ * This adapter is identical to {@link GO_Decimal} except for the element name, which is {@code "Real"}
+ * instead than {@code "Decimal"}. This adapter is the most widely used one in IS 19139 XML schema.
+ * The few exceptions are documented in {@link GO_Decimal}.
  *
  * @author  Cédric Briançon (Geomatys)
  * @since   0.3 (derived from geotk-2.5)
  * @version 0.3
  * @module
- *
- * @see GO_Decimal
  */
-public final class GO_Real extends XmlAdapter<GO_Real, Double> {
-    /**
-     * The double value to handle.
-     */
-    @XmlElement(name = "Real")
-    public Double value;
-
+public final class GO_Real extends PropertyType<GO_Real, Double> {
     /**
      * Empty constructor used only by JAXB.
      */
@@ -49,23 +43,20 @@ public final class GO_Real extends XmlAd
     }
 
     /**
-     * Constructs an adapter for this value.
+     * Constructs a wrapper for the given value.
      *
      * @param value The value.
      */
     private GO_Real(final Double value) {
-        this.value = value;
+        super(value, value.isNaN());
     }
 
     /**
-     * Allows JAXB to generate a Double object using the value found in the adapter.
-     *
-     * @param value The value extract from the adapter.
-     * @return A double object.
+     * Returns the Java type which is bound by this adapter.
      */
     @Override
-    public Double unmarshal(final GO_Real value) {
-        return (value != null) ? value.value : null;
+    protected Class<Double> getBoundType() {
+        return Double.class;
     }
 
     /**
@@ -77,7 +68,26 @@ public final class GO_Real extends XmlAd
      *         by {@code <gco:Real>} element.
      */
     @Override
-    public GO_Real marshal(final Double value) {
-        return (value != null) ? new GO_Real(value) : null;
+    public GO_Real wrap(final Double value) {
+        return new GO_Real(value);
+    }
+
+    /**
+     * Invoked by JAXB at marshalling time for getting the actual value to write.
+     *
+     * @return The value to be marshalled.
+     */
+    @XmlElement(name = "Real")
+    public Double getElement() {
+        return skip() ? null : metadata;
+    }
+
+    /**
+     * Invoked by JAXB at unmarshalling time for storing the result temporarily.
+     *
+     * @param metadata The unmarshalled value.
+     */
+    public void setElement(final Double metadata) {
+        this.metadata = metadata;
     }
 }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/InternationalStringConverter.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/InternationalStringConverter.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/InternationalStringConverter.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/InternationalStringConverter.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -58,6 +58,6 @@ public final class InternationalStringCo
      */
     @Override
     public String marshal(final InternationalString value) {
-        return (value != null) ? value.toString() : null;
+        return StringAdapter.toString(value);
     }
 }

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectIdentification.html
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectIdentification.html?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectIdentification.html (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectIdentification.html Sun Aug 25 15:49:51 2013
@@ -1,4 +1,24 @@
 <!DOCTYPE html>
+
+<!--
+  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.
+-->
+
 <html>
   <head>
     <title>ObjectIdentification</title>

Modified: sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java
URL: http://svn.apache.org/viewvc/sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java?rev=1517321&r1=1517320&r2=1517321&view=diff
==============================================================================
--- sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java [UTF-8] (original)
+++ sis/branches/Shapefile/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java [UTF-8] Sun Aug 25 15:49:51 2013
@@ -17,15 +17,16 @@
 package org.apache.sis.internal.jaxb.gco;
 
 import java.util.UUID;
-
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import org.apache.sis.xml.XLink;
 import org.apache.sis.xml.IdentifierMap;
 import org.apache.sis.xml.IdentifierSpace;
 import org.apache.sis.xml.IdentifiedObject;
 import org.apache.sis.xml.ReferenceResolver;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.internal.jaxb.Context;
-import org.apache.sis.internal.jaxb.IdentifierMapAdapter;
 import org.apache.sis.internal.jaxb.SpecializedIdentifier;
 
 
@@ -51,13 +52,13 @@ import org.apache.sis.internal.jaxb.Spec
  */
 final class ObjectReference {
     /**
-     * A URN to an external resources, or to an other part of a XML document, or an identifier.
+     * A unique identifier to an external resources, or to an other part of a XML document.
      * The {@code uuidref} attribute is used to refer to an XML element that has a corresponding
      * {@code uuid} attribute.
      *
      * @see <a href="http://www.schemacentral.com/sc/niem21/a-uuidref-1.html">Usage of uuidref</a>
      */
-    String anyUUID;
+    UUID uuid;
 
     /**
      * The {@code xlink} attributes, or {@code null} if none.
@@ -65,11 +66,6 @@ final class ObjectReference {
     XLink xlink;
 
     /**
-     * The parsed value of {@link #anyUUID}, computed when first needed.
-     */
-    private transient UUID uuid;
-
-    /**
      * Creates an initially empty object reference.
      */
     ObjectReference() {
@@ -77,23 +73,13 @@ final class ObjectReference {
 
     /**
      * Creates an object reference initialized to the given value.
-     */
-    ObjectReference(final UUID uuid, final String anyUUID, final XLink link) {
-        this.uuid    = uuid;
-        this.anyUUID = anyUUID;
-        this.xlink   = link;
-    }
-
-    /**
-     * Parses the given string as a UUID.
      *
-     * @param  context The marshalling context, or {@code null} if none.
-     * @param  anyUUID The string to parse, or {@code null}.
-     * @return The parsed UUID, or {@code null}.
-     * @throws IllegalArgumentException If {@code anyUUID} can not be parsed.
+     * @see ReferenceResolver#canSubstituteByReference(MarshalContext, Class, Object, UUID)
+     * @see ReferenceResolver#canSubstituteByReference(MarshalContext, Class, Object, XLink)
      */
-    static UUID toUUID(final Context context, final String anyUUID) throws IllegalArgumentException {
-        return (anyUUID != null) ? Context.converter(context).toUUID(context, anyUUID) : null;
+    ObjectReference(final UUID uuid, final XLink link) {
+        this.uuid  = uuid;
+        this.xlink = link;
     }
 
     /**
@@ -101,19 +87,15 @@ final class ObjectReference {
      * declared in this {@code ObjectReference}. If the given metadata object is non-null,
      * assigns to that object the identifiers declared in this {@code ObjectReference}.
      *
-     * <p>This method is invoked at unmarshalling time.</p>
+     * <p>This method is invoked at unmarshalling time by {@link PropertyType#resolve(Context)}.</p>
      *
      * @param  <T>       The compile-time type of the {@code type} argument.
      * @param  context   The marshalling context, or {@code null} if none.
      * @param  type      The expected type of the metadata object.
      * @param  metadata  The metadata object, or {@code null}.
      * @return A metadata object for the identifiers, or {@code null}
-     * @throws IllegalArgumentException If the {@link #anyUUID} field can not be parsed.
      */
-    final <T> T resolve(final Context context, final Class<T> type, T metadata) throws IllegalArgumentException {
-        if (uuid == null) {
-            uuid = toUUID(context, anyUUID);
-        }
+    final <T> T resolve(final Context context, final Class<T> type, T metadata) {
         if (metadata == null) {
             final ReferenceResolver resolver = Context.resolver(context);
             if ((uuid  == null || (metadata = resolver.resolve(context, type, uuid )) == null) &&
@@ -129,38 +111,52 @@ final class ObjectReference {
                 metadata = resolver.newIdentifiedObject(context, type, identifiers);
             }
         } else {
-            // If principle, the XML should contain a full metadata object OR a uuidref attribute.
+            // In principle, the XML should contain a full metadata object OR a uuidref attribute.
             // However if both are present, assign the identifiers to that instance.
             if (metadata instanceof IdentifiedObject) {
                 final IdentifierMap map = ((IdentifiedObject) metadata).getIdentifierMap();
-                if (uuid  != null) putInto(map, IdentifierSpace.UUID,  uuid);
-                if (xlink != null) putInto(map, IdentifierSpace.XLINK, xlink);
+                putInto(context, map, IdentifierSpace.UUID,  uuid);
+                putInto(context, map, IdentifierSpace.XLINK, xlink);
             }
         }
         return metadata;
     }
 
     /**
-     * Adds a new identifier into the given map. This method is a shortcut which bypass the check
-     * for previous values associated to same the authority. It is okay only when constructing
-     * new instances, for example at XML unmarshalling time.
+     * Adds a new identifier into the given map, if non null. No previous value should exist in normal situation.
+     * However a previous value may exit in unusual (probably not very valid) XML, as in the following example:
+     *
+     * {@preformat xml
+     *   <gmd:CI_Citation>
+     *     <gmd:series uuidref="f8f5fcb1-d57b-4013-b3a4-4eaa40df6dcf">
+     *       <gmd:CI_Series uuid="f8f5fcb1-d57b-4013-b3a4-4eaa40df6dcf">
+     *         ...
+     *       </gmd:CI_Series>
+     *     </gmd:series>
+     *   </gmd:CI_Citation>
+     * }
+     *
+     * In such situation, this method is silent if the two identifiers are equal, or logs a warning and restores
+     * the previous value if they are not equal. The previous value is the "{@code uuid}" attribute, which is
+     * assumed more closely tied to the actual metadata than the {@code uuidref} attribute.
      *
      * @param map       The map in which to write the identifier.
      * @param authority The identifier authority.
      * @param value     The identifier value, or {@code null} if not yet defined.
      */
-    private static <T> void putInto(final IdentifierMap map, final IdentifierSpace<T> authority, final T value) {
-        if (map instanceof IdentifierMapAdapter) {
-            final SpecializedIdentifier<T> identifier = new SpecializedIdentifier<T>(authority, value);
-            /*
-             * If the following assert statement appears to fail in practice, then remove
-             * completly this method and use the public putSpecialized(…) method instead.
-             * Note: usage of 'put' is for having the compiler to check the key type.
-             */
-            assert map.put(authority, null) == null : identifier;
-            ((IdentifierMapAdapter) map).identifiers.add(identifier);
-        } else {
-            map.putSpecialized(authority, value);
+    private static <T> void putInto(final Context context, final IdentifierMap map,
+            final IdentifierSpace<T> authority, final T value)
+    {
+        if (value != null) {
+            final T previous = map.putSpecialized(authority, value);
+            if (previous != null && !previous.equals(value)) {
+                final LogRecord record = Errors.getResources((context != null) ? context.getLocale() : null)
+                        .getLogRecord(Level.WARNING, Errors.Keys.InconsistentAttribute_2, authority.getName(), value);
+                record.setSourceClassName(IdentifierMap.class.getName());
+                record.setSourceMethodName("putSpecialized");
+                Context.warningOccured(context, map, record);
+                map.putSpecialized(authority, previous);
+            }
         }
     }
 }