sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1495611 [2/3] - in /sis/trunk: ./ core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/code/ core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gts/ core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/ c...
Date Fri, 21 Jun 2013 22:02:34 GMT
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] Fri Jun 21 22:02:32 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;
@@ -70,20 +75,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 +97,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 +137,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 +173,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 +204,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 +264,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 +338,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/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] Fri Jun 21 22:02:32 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/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -198,7 +198,7 @@ public final class IdentifierMapWithSpec
                 return (id != null) ? id.toString() : old;
             }
         }
-        SpecializedIdentifier.parseFailure(exception);
+        SpecializedIdentifier.parseFailure(this, exception);
         return super.put(authority, code);
     }
 

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java [UTF-8] Fri Jun 21 22:02:32 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/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -26,7 +26,6 @@ import org.apache.sis.xml.XLink;
 import org.apache.sis.xml.IdentifierMap;
 import org.apache.sis.xml.IdentifierSpace;
 import org.apache.sis.util.Debug;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.internal.util.Citations;
 
 // Related to JDK7
@@ -89,12 +88,13 @@ 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) {
                 case NonMarshalledAuthority.ID: {
@@ -104,7 +104,7 @@ public final class SpecializedIdentifier
                     try {
                         return new SpecializedIdentifier<UUID>(IdentifierSpace.UUID, UUID.fromString(code));
                     } catch (IllegalArgumentException e) {
-                        parseFailure(e);
+                        parseFailure(source, e);
                         break;
                     }
                 }
@@ -113,7 +113,7 @@ public final class SpecializedIdentifier
                     try {
                         href = new URI(code);
                     } catch (URISyntaxException e) {
-                        parseFailure(e);
+                        parseFailure(source, e);
                         break;
                     }
                     return new SpecializedIdentifier<URI>(IdentifierSpace.HREF, href);
@@ -123,7 +123,7 @@ public final class SpecializedIdentifier
                     try {
                         href = new URI(code);
                     } catch (URISyntaxException e) {
-                        parseFailure(e);
+                        parseFailure(source, e);
                         break;
                     }
                     final XLink xlink = new XLink();
@@ -140,13 +140,15 @@ public final class SpecializedIdentifier
      * 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) {
+    static void parseFailure(final IdentifierMap source, final Exception e) {
         // IdentifierMap.put(Citation,String) is the public facade.
-        Logging.recoverableException(IdentifierMap.class, "put", e);
+        Context.warningOccured(Context.current(), source, IdentifierMap.class, "put", e, true);
     }
 
     /**
      * Returns the authority specified at construction time.
+     *
+     * @return The identifier authority.
      */
     @Override
     public Citation getAuthority() {
@@ -166,6 +168,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/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/InternationalStringConverter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/InternationalStringConverter.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/InternationalStringConverter.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/InternationalStringConverter.java [UTF-8] Fri Jun 21 22:02:32 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/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -17,7 +17,6 @@
 package org.apache.sis.internal.jaxb.gco;
 
 import java.util.UUID;
-
 import org.apache.sis.xml.XLink;
 import org.apache.sis.xml.IdentifierMap;
 import org.apache.sis.xml.IdentifierSpace;
@@ -129,7 +128,7 @@ 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();

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -28,6 +28,7 @@ import org.apache.sis.xml.Namespaces;
 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.internal.jaxb.Context;
 import org.apache.sis.util.iso.SimpleInternationalString;
 
@@ -112,12 +113,12 @@ public abstract class PropertyType<Value
         extends XmlAdapter<ValueType,BoundType>
 {
     /**
-     * The wrapped GeoAPI metadata interface.
+     * The wrapped GeoAPI metadata instance.
      */
     protected BoundType metadata;
 
     /**
-     * Either an {@link ObjectReference} or a {@link String}.
+     * Either {@code null}, an {@link ObjectReference} or a {@link String}.
      *
      * <ul>
      *   <li>{@link ObjectReference} defines the {@code idref}, {@code uuidref}, {@code xlink:href},
@@ -138,34 +139,44 @@ public abstract class PropertyType<Value
     }
 
     /**
-     * Builds an adapter for the given GeoAPI interface.
+     * Builds an adapter for the given GeoAPI interface. This constructor checks if the given metadata
+     * implements the {@link NilObject} or {@link IdentifiedObject} interface. If the object implements
+     * both of them (should not happen, but we never know), then the identifiers will have precedence.
      *
      * @param metadata The interface to wrap.
      */
     protected PropertyType(final BoundType metadata) {
         this.metadata = metadata;
+        if (metadata instanceof NilObject) {
+            final NilReason reason = ((NilObject) metadata).getNilReason();
+            if (reason != null) {
+                reference = reason.toString();
+            }
+        }
         if (metadata instanceof IdentifiedObject) {
             final IdentifierMap map = ((IdentifiedObject) metadata).getIdentifierMap();
             XLink  link = map.getSpecialized(IdentifierSpace.XLINK);
             UUID   uuid = map.getSpecialized(IdentifierSpace.UUID);
             String anyUUID = (uuid != null) ? uuid.toString() : map.get(IdentifierSpace.UUID);
             if (anyUUID != null || link != null) {
-                final Context context = Context.current();
+                final Context           context  = Context.current();
+                final ReferenceResolver resolver = Context.resolver(context);
+                final Class<BoundType>  type     = getBoundType();
                 if (uuid == null) {
                     uuid = ObjectReference.toUUID(context, anyUUID); // May still null.
                 }
-                if (uuid == null || Context.resolver(context).canSubstituteByReference(context, getBoundType(), metadata, uuid)) {
+                // Check if the user gives us the permission to use those identifiers.
+                if (uuid != null && !resolver.canSubstituteByReference(context, type, metadata, uuid)) {
+                    uuid = null;
+                }
+                if (link != null && !resolver.canSubstituteByReference(context, type, metadata, link)) {
+                    link = null;
+                }
+                if (uuid != null || link != null) {
                     reference = new ObjectReference(uuid, anyUUID, link);
-                    return;
                 }
             }
         }
-        if (metadata instanceof NilObject) {
-            final NilReason reason = ((NilObject) metadata).getNilReason();
-            if (reason != null) {
-                reference = reason.toString();
-            }
-        }
     }
 
     /**
@@ -370,7 +381,7 @@ public abstract class PropertyType<Value
     @XmlAttribute(name = "title", namespace = Namespaces.XLINK)
     public final String getTitle() {
         final XLink link = xlink(false);
-        return (link != null) ? toString(link.getTitle()) : null;
+        return (link != null) ? StringAdapter.toString(link.getTitle()) : null;
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/StringAdapter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/StringAdapter.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/StringAdapter.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/StringAdapter.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -48,28 +48,38 @@ public final class StringAdapter extends
      * If the context is {@code null} or does not specify any locale, then the choice
      * of locale is left to the {@link InternationalString#toString()} implementation.
      *
-     * @param  value The wrapper for the value, or {@code null}.
-     * @return The string representation of the given text, or {@code null}.
+     * @param  text The {@code CharSequence} to convert to a {@code String}, or {@code null}.
+     * @return The localized representation of the given text, or {@code null} if the text was null.
+     *
+     * @see org.apache.sis.xml.XML#LOCALE
      */
-    public static String toString(final GO_CharacterString value) {
-        if (value != null) {
-            final CharSequence text = value.toCharSequence();
-            if (text != null) {
-                if (text instanceof InternationalString) {
-                    final Context context = Context.current();
-                    if (context != null) {
-                        final Locale locale = context.getLocale();
-                        if (locale != null) {
-                            // While Apache SIS accepts null locale, foreigner
-                            // implementations are not guaranteed to support null.
-                            return ((InternationalString) text).toString(locale);
-                        }
-                    }
+    static String toString(final CharSequence text) {
+        if (text == null) {
+            return null;
+        }
+        if (text instanceof InternationalString) {
+            final Context context = Context.current();
+            if (context != null) {
+                final Locale locale = context.getLocale();
+                if (locale != null) {
+                    // While Apache SIS accepts null locale, foreigner
+                    // implementations are not guaranteed to support null.
+                    return ((InternationalString) text).toString(locale);
                 }
-                return text.toString();
             }
         }
-        return null;
+        return text.toString();
+    }
+
+    /**
+     * Returns the string representation of the given {@code GO_CharacterString} for the current locale.
+     * The locale is determined by the {@link org.apache.sis.xml.XML#LOCALE} property given to the marshaller.
+     *
+     * @param  value The wrapper for the value, or {@code null}.
+     * @return The string representation of the given text, or {@code null}.
+     */
+    public static String toString(final GO_CharacterString value) {
+        return (value != null) ? toString(value.toCharSequence()) : null;
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/CodeListProxy.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/CodeListProxy.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/CodeListProxy.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/CodeListProxy.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -24,7 +24,6 @@ import javax.xml.bind.annotation.XmlType
 import javax.xml.bind.annotation.XmlValue;
 import org.opengis.util.CodeList;
 import org.apache.sis.util.iso.Types;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.internal.jaxb.Context;
 
 
@@ -46,21 +45,13 @@ import org.apache.sis.internal.jaxb.Cont
 @XmlType(name = "CodeList", propOrder = { "codeSpace", "codeListValue", "codeList" })
 public final class CodeListProxy {
     /**
-     * Returns the URL to given code list in the given XML file.
-     *
-     * @param  context    The current (un)marshalling context, or {@code null} if none.
-     * @param  file       The XML file, either {@code "gmxCodelists.xml"} or {@code "ML_gmxCodelists.xml"}.
-     * @param  identifier The UML identifier of the code list.
-     * @return The URL to the given code list in the given schema.
+     * The default schema to be given to {@link Context#schema(Context, String, String)} (last argument).
      */
-    private static String schema(final Context context, final String file, final String identifier) {
-        return schema(Context.schema(context, "gmd", "http://schemas.opengis.net/iso/19139/20070417/"),
-                "resources/Codelist", file, identifier);
-    }
+    public static final String DEFAULT_SCHEMA = "http://schemas.opengis.net/iso/19139/20070417/";
 
     /**
-     * Returns the URL to a given code list in the given XML file. This method concatenates
-     * the base schema URL with the given directory, file and identifier.
+     * Returns the URL to a given code list in the given XML file.
+     * This method concatenates the base schema URL with the given file and identifier.
      * Some examples of strings returned by this method are:
      *
      * <ul>
@@ -69,23 +60,14 @@ public final class CodeListProxy {
      *   <li>{@code "http://schemas.opengis.net/iso/19139/20070417/resources/Codelist/gmxCodelists.xml#CI_OnLineFunctionCode"}</li>
      * </ul>
      *
-     * @param  schema     The schema, typically as a result of a call to
-     *                    {@link Context#schema(Context, String, String)}.
-     * @param  directory  The directory to concatenate, for example {@code "resources/uom"}
-     *                    or {@code "resources/Codelist"} (<strong>no trailing {@code '/'}</strong>).
-     * @param  file       The XML file, for example {@code "gmxUom.xml"}, {@code "gmxCodelists.xml"}
-     *                    or {@code "ML_gmxCodelists.xml"} (<strong>no trailing {@code '#'}</strong>).
+     * @param  context    The current (un)marshalling context, or {@code null} if none.
+     * @param  file       The XML file, either {@code "gmxCodelists.xml"} or {@code "ML_gmxCodelists.xml"}.
      * @param  identifier The UML identifier of the code list.
      * @return The URL to the given code list in the given schema.
      */
-    private static String schema(final String schema, final String directory, final String file, final String identifier) {
-        final StringBuilder buffer = new StringBuilder(128);
-        buffer.append(schema);
-        final int length = buffer.length();
-        if (length != 0 && buffer.charAt(length - 1) != '/') {
-            buffer.append('/');
-        }
-        return buffer.append(directory).append('/').append(file).append('#').append(identifier).toString();
+    private static String schema(final Context context, final String file, final String identifier) {
+        return Context.schema(context, "gmd", DEFAULT_SCHEMA).append("resources/Codelist/")
+                .append(file).append('#').append(identifier).toString();
     }
 
     /**
@@ -183,7 +165,7 @@ public final class CodeListProxy {
                 value = ResourceBundle.getBundle("org.opengis.metadata.CodeLists",
                         locale, CodeList.class.getClassLoader()).getString(key);
             } catch (MissingResourceException e) {
-                Logging.recoverableException(CodeListAdapter.class, "marshal", e);
+                Context.warningOccured(context, code, CodeListAdapter.class, "marshal", e, false);
             }
         }
         if (value != null) {

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/Country.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/Country.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/Country.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/Country.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -91,7 +91,7 @@ public final class Country extends GO_Ch
      * @return The country to marshal, or {@code null} if the given locale was null
      *         or if its {@link Locale#getCountry()} attribute is the empty string.
      */
-    static Country create(final Context context, final Locale locale) {
+    public static Country create(final Context context, final Locale locale) {
         if (locale != null) {
             final String codeListValue = Context.converter(context).toCountryCode(context, locale);
             if (!codeListValue.isEmpty() && Context.isFlagSet(context, Context.SUBSTITUTE_COUNTRY)) {
@@ -131,7 +131,7 @@ public final class Country extends GO_Ch
      *
      * @see LanguageCode#getLocale(Context, LanguageCode, boolean)
      */
-    static Locale getLocale(final Country value) {
+    public static Locale getLocale(final Country value) {
         if (value != null) {
             String code = null;
             if (value.proxy != null) {

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/LanguageCode.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/LanguageCode.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/LanguageCode.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/LanguageCode.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -89,7 +89,7 @@ public final class LanguageCode extends 
      * @return The language to marshal, or {@code null} if the given locale was null
      *         or if its {@link Locale#getLanguage()} attribute is the empty string.
      */
-    static LanguageCode create(final Context context, final Locale locale) {
+    public static LanguageCode create(final Context context, final Locale locale) {
         if (locale != null) {
             final String codeListValue = Context.converter(context).toLanguageCode(context, locale);
             if (!codeListValue.isEmpty() && Context.isFlagSet(context, Context.SUBSTITUTE_LANGUAGE)) {
@@ -124,6 +124,7 @@ public final class LanguageCode extends 
     /**
      * Returns the locale for the given language (which may be null), or {@code null} if none.
      *
+     * @param context The current (un)marshalling context, or {@code null} if none.
      * @param value The wrapper for this metadata value.
      * @param useCharSequence Whatever this method should fallback on the
      *        {@code gco:CharacterString} element if no value were specified for the
@@ -132,7 +133,7 @@ public final class LanguageCode extends 
      *
      * @see Country#getLocale(Country)
      */
-    static Locale getLocale(final Context context, final LanguageCode value, final boolean useCharSequence) {
+    public static Locale getLocale(final Context context, final LanguageCode value, final boolean useCharSequence) {
         if (value != null) {
             final CodeListProxy proxy = value.proxy;
             if (proxy != null) {

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/LocaleAdapter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/LocaleAdapter.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/LocaleAdapter.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gmd/LocaleAdapter.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -38,8 +38,8 @@ import org.apache.sis.internal.jaxb.Cont
  * @version 0.3
  * @module
  *
- * @see org.apache.sis.internal.jaxb.gmd.LanguageCode
- * @see org.apache.sis.internal.jaxb.gmd.PT_Locale
+ * @see LanguageCode
+ * @see org.apache.sis.internal.jaxb.code.PT_Locale
  */
 public final class LocaleAdapter extends XmlAdapter<LanguageCode, Locale> {
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -17,6 +17,11 @@
 package org.apache.sis.internal.jdk8;
 
 import java.util.Date;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicReference;
 import javax.xml.bind.DatatypeConverter;
 import org.apache.sis.util.CharSequences;
 
@@ -32,6 +37,13 @@ import org.apache.sis.util.CharSequences
  */
 public final class JDK8 {
     /**
+     * A shared Gregorian calendar to use for {@link #printDateTime(Date)}.
+     * We share a single instance instead than using {@link ThreadLocal} instances
+     * on the assumption that usages of this calendar will be relatively rare.
+     */
+    private static final AtomicReference<Calendar> CALENDAR = new AtomicReference<Calendar>();
+
+    /**
      * Do not allow instantiation of this class.
      */
     private JDK8() {
@@ -135,4 +147,29 @@ public final class JDK8 {
         }
         return null;
     }
+
+    /**
+     * Formats a date value in a string, assuming UTC timezone and US locale.
+     * This method should be used only for occasional formatting.
+     *
+     * <p>This method will be replaced by {@link java.time.format.DateTimeFormatter} on the JDK8 branch.</p>
+     *
+     * @param  date The date to format, or {@code null}.
+     * @return The formatted date, or {@code null} if the given date was null.
+     *
+     * @see DatatypeConverter#printDateTime(Calendar)
+     */
+    public static String printDateTime(final Date date) {
+        if (date == null) {
+            return null;
+        }
+        Calendar calendar = CALENDAR.getAndSet(null);
+        if (calendar == null) {
+            calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US);
+        }
+        calendar.setTime(date);
+        final String text = DatatypeConverter.printDateTime(calendar);
+        CALENDAR.set(calendar); // Recycle for future usage.
+        return text;
+    }
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -45,9 +45,10 @@ public final class TemporalUtilities ext
     /**
      * Returns a temporal factory if available.
      *
+     * @return The temporal factory.
      * @throws UnsupportedOperationException If the temporal factory is not available on the classpath.
      */
-    private static TemporalFactory getTemporalFactory() throws UnsupportedOperationException {
+    public static TemporalFactory getTemporalFactory() throws UnsupportedOperationException {
         final TemporalFactory factory = DefaultFactories.forClass(TemporalFactory.class);
         if (factory != null) {
             return factory;

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/io/IO.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/io/IO.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/io/IO.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/io/IO.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -23,7 +23,7 @@ import java.io.Writer;
 import java.io.StringWriter;
 import java.io.CharArrayWriter;
 import org.apache.sis.util.Static;
-import org.apache.sis.util.resources.Messages;
+import org.apache.sis.util.resources.Vocabulary;
 
 
 /**
@@ -143,7 +143,7 @@ public final class IO extends Static {
         if (content != null) {
             return content.toString();
         }
-        return Messages.format(Messages.Keys.UnavailableContent);
+        return Vocabulary.format(Vocabulary.Keys.UnavailableContent);
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -17,6 +17,7 @@
 package org.apache.sis.util.collection;
 
 import java.util.Arrays;
+import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.List;
@@ -38,13 +39,11 @@ import org.apache.sis.util.Workaround;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.internal.util.LocalizedParseException;
 
 import static org.apache.sis.util.Characters.NO_BREAK_SPACE;
 
-// Related to JDK7
-import org.apache.sis.internal.jdk7.JDK7;
-
 
 /**
  * A parser and formatter for {@link TreeTable} instances.
@@ -82,6 +81,16 @@ import org.apache.sis.internal.jdk7.JDK7
  * then insert the {@code "……"} string, repeat the {@code '…'} character as many time as needed
  * (may be zero), and finally insert a space</cite>".
  *
+ * {@section Safety against infinite recursivity}
+ * Some {@code TreeTable} implementations generate the nodes dynamically as wrappers around Java objects.
+ * Such Java objects may contain cyclic associations (<var>A</var> contains <var>B</var> contains <var>C</var>
+ * contains <var>A</var>), which result in a tree of infinite depth. Some examples can been found in ISO 19115
+ * metadata. This {@code TreeTableFormat} class contains a safety against such cycles. The algorithm is based
+ * on the assumption that for each node, the values and children are fully determined by the
+ * {@linkplain TreeTable.Node#getUserObject() user object}, if non-null. Consequently for each node <var>C</var>
+ * to be formatted, if the user object of that node is the same instance (in the sense of the {@code ==} operator)
+ * than the user object of a parent node <var>A</var>, then the children of the <var>C</var> node will not be formatted.
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.3 (derived from geotk-2.0)
  * @version 0.3
@@ -145,6 +154,12 @@ public class TreeTableFormat extends Tab
     private transient String treeBlank, treeLine, treeCross, treeEnd;
 
     /**
+     * The map to be given to {@link Writer#parentObjects}, created when first needed
+     * and reused for subsequent formating.
+     */
+    private transient Map<Object,Object> parentObjects;
+
+    /**
      * Creates a new tree table format.
      *
      * @param locale   The locale to use for numbers, dates and angles formatting,
@@ -555,14 +570,33 @@ public class TreeTableFormat extends Tab
         private boolean[] isLast;
 
         /**
+         * The {@linkplain TreeTable.Node#getUserObject() user object} of the parent nodes.
+         * We use this map as a safety against infinite recursivity, on the assumption that
+         * the node content and children are fully determined by the user object.
+         *
+         * <p>User objects in this map will be compared by the identity comparator rather than by the
+         * {@code equals(Object)} method because the later may be costly and could itself be vulnerable
+         * to infinite recursivity if it has not been implemented defensively.</p>
+         *
+         * <p>We check the user object instead than the node itself because the same node instance can
+         * theoretically not appear twice with a different parent.</p>
+         */
+        private final Map<Object,Object> parentObjects;
+
+        /**
          * Creates a new instance which will write in the given appendable.
+         *
+         * @param out           Where to format the tree.
+         * @param column        The columns of the tree table to format.
+         * @param parentObjects An initially empty {@link IdentityHashMap}.
          */
-        Writer(final Appendable out, final TableColumn<?>[] columns) {
+        Writer(final Appendable out, final TableColumn<?>[] columns, final Map<Object,Object> parentObjects) {
             super(columns.length >= 2 ? new TableAppender(out, "") : out);
             this.columns = columns;
             this.formats = getFormats(columns, false);
             this.values  = new Object[columns.length];
             this.isLast  = new boolean[8];
+            this.parentObjects = parentObjects;
             setTabulationExpanded(true);
             setLineSeparator(" ¶ ");
         }
@@ -634,13 +668,38 @@ public class TreeTableFormat extends Tab
             if (level >= isLast.length) {
                 isLast = Arrays.copyOf(isLast, level*2);
             }
-            final Iterator<? extends TreeTable.Node> it = node.getChildren().iterator();
-            boolean hasNext = it.hasNext();
-            while (hasNext) {
-                final TreeTable.Node child = it.next();
-                hasNext = it.hasNext();
-                isLast[level] = !hasNext; // Must be set before the call to 'format' below.
-                format(child, level+1);
+            /*
+             * Format the children only if we do not detect an infinite recursivity. Our recursivity detection
+             * algorithm assumes that the node content is fully determined by the user object. If that assumption
+             * holds, then we have an infinite recursivity if the user object of the current node is also the user
+             * object of a parent node.
+             *
+             * Note that the value stored in the 'parentObjects' map needs to be the 'userObject' because we want
+             * the map value to be null if the user object is null, in order to format the children even if many
+             * null user objects exist in the tree.
+             */
+            final Object userObject = node.getUserObject();
+            if (parentObjects.put(userObject, userObject) == null) {
+                final Iterator<? extends TreeTable.Node> it = node.getChildren().iterator();
+                boolean hasNext = it.hasNext();
+                while (hasNext) {
+                    final TreeTable.Node child = it.next();
+                    hasNext = it.hasNext();
+                    isLast[level] = !hasNext; // Must be set before the call to 'format' below.
+                    format(child, level+1);
+                }
+                parentObjects.remove(userObject);
+            } else {
+                /*
+                 * Detected a recursivity. Format "(cycle omitted)" just below the node.
+                 */
+                for (int i=0; i<level; i++) {
+                    out.append(getTreeSymbols(true, isLast[i]));
+                }
+                final Locale locale = getLocale();
+                out.append('(').append(Vocabulary.getResources(locale)
+                   .getString(Vocabulary.Keys.CycleOmitted).toLowerCase(locale))
+                   .append(')').append(lineSeparator);
             }
         }
     }
@@ -671,8 +730,15 @@ public class TreeTableFormat extends Tab
             final List<TableColumn<?>> c = tree.getColumns();
             columns = c.toArray(new TableColumn<?>[c.size()]);
         }
-        final Writer out = new Writer(toAppendTo, columns);
-        out.format(tree.getRoot(), 0);
-        out.flush();
+        if (parentObjects == null) {
+            parentObjects = new IdentityHashMap<Object,Object>();
+        }
+        try {
+            final Writer out = new Writer(toAppendTo, columns, parentObjects);
+            out.format(tree.getRoot(), 0);
+            out.flush();
+        } finally {
+            parentObjects.clear();
+        }
     }
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -52,6 +52,18 @@ import org.apache.sis.util.Classes;
  */
 public final class Logging extends Static {
     /**
+     * The threshold at which {@link #unexpectedException(Logger, String, String, Throwable, Level)} shall
+     * set the throwable in the {@link LogRecord}. For any record to be logged at a lower {@link Level},
+     * the {@link LogRecord#setThrown(Throwable)} method will not be invoked.
+     *
+     * <p>The default value is 600, which is the {@link PerformanceLevel#PERFORMANCE} value.
+     * This value is between {@link Level#FINE} (500) and {@link Level#CONFIG} (700).
+     * Consequently we will ignore the stack traces of recoverable failures, but will report
+     * stack traces that may impact performance, configuration, or correctness.</p>
+     */
+    private static final int LEVEL_THRESHOLD_FOR_STACKTRACE = 600;
+
+    /**
      * The factory for obtaining {@link Logger} instances, or {@code null} if none.
      * If {@code null} (the default), then the standard JDK logging framework will be used.
      * {@code Logging} scans the classpath for logger factories on class initialization.
@@ -383,7 +395,7 @@ public final class Logging extends Stati
         if (method != null) {
             record.setSourceMethodName(method);
         }
-        if (level.intValue() > 500) {
+        if (level.intValue() >= LEVEL_THRESHOLD_FOR_STACKTRACE) {
             record.setThrown(error);
         }
         record.setLoggerName(logger.getName());

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -34,7 +34,7 @@ import java.util.logging.LogRecord;
  * The difference between using this listener or configuring the logging {@link java.util.logging.Handler} is
  * that listeners allow to handle the warnings on a per-{@code DataStore} (or any other emitter) instance.
  *
- * @param <S> The type of the source of warnings.
+ * @param <S> The base type of objects that emit warnings (the <cite>source</cite>).
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
@@ -46,6 +46,15 @@ import java.util.logging.LogRecord;
  */
 public interface WarningListener<S> extends EventListener {
     /**
+     * Returns the type of objects that emit warnings of interest for this listener.
+     * This is typically, but not necessarily, the class having the name returned by
+     * {@link LogRecord#getSourceClassName()}, or one of its parent classes.
+     *
+     * @return The base type of objects that emit warnings (the <cite>source</cite>).
+     */
+    Class<S> getSourceClass();
+
+    /**
      * Reports the occurrence of a non-fatal error. The emitter process (often a
      * {@link org.apache.sis.storage.DataStore} in the midst of a reading process)
      * will continue following the call to this method.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -63,7 +63,7 @@ public final class Messages extends Inde
          * Property “{0}” has been discarded in favor of “{1}”, because those two properties are
          * mutually exclusive.
          */
-        public static final int DiscardedExclusiveProperty_2 = 4;
+        public static final int DiscardedExclusiveProperty_2 = 1;
 
         /**
          * Text were discarded for some locales.
@@ -74,11 +74,6 @@ public final class Messages extends Inde
          * Property “{0}” is hidden by “{1}”.
          */
         public static final int PropertyHiddenBy_2 = 3;
-
-        /**
-         * Unavailable content.
-         */
-        public static final int UnavailableContent = 1;
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] Fri Jun 21 22:02:32 2013
@@ -18,4 +18,3 @@ ChangedContainerCapacity_2      = Change
 DiscardedExclusiveProperty_2    = Property \u201c{0}\u201d has been discarded in favor of \u201c{1}\u201d, because those two properties are mutually exclusive.
 PropertyHiddenBy_2              = Property \u201c{0}\u201d is hidden by \u201c{1}\u201d.
 LocalesDiscarded                = Text were discarded for some locales.
-UnavailableContent              = Unavailable content.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] Fri Jun 21 22:02:32 2013
@@ -18,4 +18,3 @@ ChangedContainerCapacity_2      = Change
 DiscardedExclusiveProperty_2    = La propri\u00e9t\u00e9 \u201c{0}\u201d a \u00e9t\u00e9 \u00e9cart\u00e9e en faveur de \u201c{1}\u201d, parce que ces deux propri\u00e9t\u00e9s sont mutuellement exclusives.
 PropertyHiddenBy_2              = La propri\u00e9t\u00e9 \u201c{0}\u201d est masqu\u00e9e par \u201c{1}\u201d.
 LocalesDiscarded                = Des textes ont \u00e9t\u00e9 ignor\u00e9s pour certaines langues.
-UnavailableContent              = Contenu non-disponible.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -110,6 +110,11 @@ public final class Vocabulary extends In
         public static final int CurrentDirectory = 33;
 
         /**
+         * Cycle omitted
+         */
+        public static final int CycleOmitted = 54;
+
+        /**
          * Daylight time
          */
         public static final int DaylightTime = 24;
@@ -290,6 +295,11 @@ public final class Vocabulary extends In
         public static final int Type = 1;
 
         /**
+         * Unavailable content.
+         */
+        public static final int UnavailableContent = 53;
+
+        /**
          * Untitled
          */
         public static final int Untitled = 37;

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] Fri Jun 21 22:02:32 2013
@@ -25,6 +25,7 @@ Code_1             = {0} code
 Commands           = Commands
 CurrentDateTime    = Current date and time
 CurrentDirectory   = Current directory
+CycleOmitted       = Cycle omitted
 DaylightTime       = Daylight time
 Destination        = Destination
 Dimensions         = Dimensions
@@ -62,6 +63,7 @@ TemporaryFiles     = Temporary files
 Timezone           = Timezone
 Type               = Type
 Untitled           = Untitled
+UnavailableContent = Unavailable content.
 UserHome           = User home directory
 Value              = Value
 Variables          = Variables

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] Fri Jun 21 22:02:32 2013
@@ -25,6 +25,7 @@ Code_1             = Code {0}
 Commands           = Commandes
 CurrentDateTime    = Date et heure courantes
 CurrentDirectory   = R\u00e9pertoire courant
+CycleOmitted       = Cycle omit
 DaylightTime       = Heure normale
 Destination        = Destination
 Dimensions         = Dimensions
@@ -62,6 +63,7 @@ TemporaryFiles     = Fichiers temporaire
 Timezone           = Fuseau horaire
 Type               = Type
 Untitled           = Sans titre
+UnavailableContent = Contenu non-disponible.
 UserHome           = R\u00e9pertoire de l'utilisateur
 Value              = Valeur
 Variables          = Variables

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/MarshalContext.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/MarshalContext.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/MarshalContext.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/MarshalContext.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -19,6 +19,7 @@ package org.apache.sis.xml;
 import java.util.Locale;
 import java.util.TimeZone;
 import org.apache.sis.util.Version;
+import org.opengis.util.InternationalString;
 
 
 /**
@@ -37,29 +38,8 @@ public abstract class MarshalContext {
     }
 
     /**
-     * Returns the schema version of the XML document being (un)marshalled.
-     * The {@code prefix} argument can be any of the following values (case-sensitive):
-     *
-     * <table class="sis">
-     *   <tr>
-     *     <th>Prefix</th>
-     *     <th>Standard</th>
-     *     <th>Typical values</th>
-     *   </tr>
-     *   <tr>
-     *     <td>gml</td> <td>Geographic Markup Language</td> <td>{@code 3.0}, {@code 3.2}</td>
-     *   </tr>
-     * </table>
-     *
-     * @param  prefix One of the above-cited prefix.
-     * @return The version for the given schema, or {@code null} if unknown.
-     */
-    public abstract Version getVersion(final String prefix);
-
-    /**
-     * Returns the locale to use for (un)marshalling, or {@code null} if no locale were explicitly
-     * specified. The locale returned by this method can be used for choosing a language in an
-     * {@link org.opengis.util.InternationalString}.
+     * Returns the locale to use for (un)marshalling, or {@code null} if no locale were explicitly specified.
+     * The locale returned by this method can be used for choosing a language in an {@link InternationalString}.
      *
      * <p>This locale may vary in different fragments of the same XML document.
      * In particular children of {@link org.opengis.metadata.Metadata} inherit the locale
@@ -82,8 +62,7 @@ public abstract class MarshalContext {
     public abstract Locale getLocale();
 
     /**
-     * Returns the timezone to use for (un)marshalling, or {@code null} if none were explicitely
-     * specified.
+     * Returns the timezone to use for (un)marshalling, or {@code null} if none were explicitely specified.
      *
      * {@section Handling of <code>null</code> timezone}
      * A {@code null} value means that the timezone is unspecified. Callers are encouraged
@@ -92,4 +71,24 @@ public abstract class MarshalContext {
      * @return The timezone for the XML fragment being (un)marshalled, or {@code null} if unspecified.
      */
     public abstract TimeZone getTimeZone();
+
+    /**
+     * Returns the schema version of the XML document being (un)marshalled.
+     * The {@code prefix} argument can be any of the following values (case-sensitive):
+     *
+     * <table class="sis">
+     *   <tr>
+     *     <th>Prefix</th>
+     *     <th>Standard</th>
+     *     <th>Typical values</th>
+     *   </tr>
+     *   <tr>
+     *     <td>gml</td> <td>Geographic Markup Language</td> <td>{@code 3.0}, {@code 3.2}</td>
+     *   </tr>
+     * </table>
+     *
+     * @param  prefix One of the above-cited prefix.
+     * @return The version for the given schema, or {@code null} if unknown.
+     */
+    public abstract Version getVersion(final String prefix);
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -29,8 +29,10 @@ import javax.xml.bind.PropertyException;
 import javax.xml.bind.ValidationEventHandler;
 import javax.xml.bind.annotation.adapters.XmlAdapter;
 import org.apache.sis.util.Version;
+import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.logging.WarningListener;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.jaxb.Context;
 
@@ -87,22 +89,23 @@ abstract class Pooled {
     private final Map<Object,Object> initialProperties;
 
     /**
-     * The object converters to use during (un)marshalling.
-     * Can be set by the {@link XML#CONVERTER} property.
+     * Bit masks for various boolean attributes. This include whatever the language codes
+     * or the country codes should be substituted by a simpler character string elements.
+     * Those bits are determined by the {@link XML#STRING_SUBSTITUTES} property.
      */
-    private ValueConverter converter;
+    private int bitMasks;
 
     /**
-     * The reference resolver to use during unmarshalling.
-     * Can be set by the {@link XML#RESOLVER} property.
+     * An optional locale for {@link org.opengis.util.InternationalString} and
+     * {@link org.opengis.util.CodeList}. Can be set by the {@link XML#LOCALE} property.
      */
-    private ReferenceResolver resolver;
+    private Locale locale;
 
     /**
-     * The GML version to be marshalled or unmarshalled, or {@code null} if unspecified.
-     * If null, then the latest version is assumed.
+     * The timezone, or {@code null} if unspecified.
+     *  Can be set by the {@link XML#TIMEZONE} property.
      */
-    private Version gmlVersion;
+    private TimeZone timezone;
 
     /**
      * The base URL of ISO 19139 (or other standards) schemas. It shall be an unmodifiable
@@ -112,23 +115,27 @@ abstract class Pooled {
     private Map<String,String> schemas;
 
     /**
-     * An optional locale for {@link org.opengis.util.InternationalString} and
-     * {@link org.opengis.util.CodeList}. Can be set by the {@link XML#LOCALE} property.
+     * The GML version to be marshalled or unmarshalled, or {@code null} if unspecified.
+     * If null, then the latest version is assumed.
      */
-    private Locale locale;
+    private Version gmlVersion;
 
     /**
-     * The timezone, or {@code null} if unspecified.
-     *  Can be set by the {@link XML#TIMEZONE} property.
+     * The reference resolver to use during unmarshalling.
+     * Can be set by the {@link XML#RESOLVER} property.
      */
-    private TimeZone timezone;
+    private ReferenceResolver resolver;
 
     /**
-     * Bit masks for various boolean attributes. This include whatever the language codes
-     * or the country codes should be substituted by a simpler character string elements.
-     * Those bits are determined by the {@link XML#STRING_SUBSTITUTES} property.
+     * The object converters to use during (un)marshalling.
+     * Can be set by the {@link XML#CONVERTER} property.
      */
-    private int bitMasks;
+    private ValueConverter converter;
+
+    /**
+     * The object to inform about warnings, or {@code null} if none.
+     */
+    private WarningListener<?> warningListener;
 
     /**
      * The {@link System#nanoTime()} value of the last call to {@link #reset()}.
@@ -170,14 +177,15 @@ abstract class Pooled {
             reset(entry.getKey(), entry.getValue());
         }
         initialProperties.clear();
-        converter  = null;
-        resolver   = null;
-        gmlVersion = null;
-        schemas    = null;
-        locale     = null;
-        timezone   = null;
-        bitMasks   = initialBitMasks();
-        resetTime  = System.nanoTime();
+        bitMasks        = initialBitMasks();
+        locale          = null;
+        timezone        = null;
+        schemas         = null;
+        gmlVersion      = null;
+        resolver        = null;
+        converter       = null;
+        warningListener = null;
+        resetTime       = System.nanoTime();
     }
 
     /**
@@ -241,12 +249,12 @@ abstract class Pooled {
     public final void setProperty(String name, final Object value) throws PropertyException {
         try {
             /* switch (name) */ {
-                if (name.equals(XML.CONVERTER)) {
-                    converter = (ValueConverter) value;
+                if (name.equals(XML.LOCALE)) {
+                    locale = (Locale) value;
                     return;
                 }
-                else if (name.equals(XML.RESOLVER)) {
-                    resolver = (ReferenceResolver) value;
+                else if (name.equals(XML.TIMEZONE)) {
+                    timezone = (TimeZone) value;
                     return;
                 }
                 else if (name.equals(XML.SCHEMAS)) {
@@ -273,19 +281,18 @@ abstract class Pooled {
                     gmlVersion = (value instanceof CharSequence) ? new Version(value.toString()) : (Version) value;
                     return;
                 }
-                else if (name.equals(XML.LOCALE)) {
-                    locale = (Locale) value;
+                else if (name.equals(XML.RESOLVER)) {
+                    resolver = (ReferenceResolver) value;
                     return;
                 }
-                else if (name.equals(XML.TIMEZONE)) {
-                    timezone = (TimeZone) value;
+                else if (name.equals(XML.CONVERTER)) {
+                    converter = (ValueConverter) value;
                     return;
                 }
                 else if (name.equals(XML.STRING_SUBSTITUTES)) {
                     int mask = initialBitMasks();
-                    final CharSequence[] substitutes = CharSequences.split((CharSequence) value, ',');
-                    if (substitutes != null) {
-                        for (final CharSequence substitute : substitutes) {
+                    if (value != null) {
+                        for (final CharSequence substitute : (CharSequence[]) value) {
                             if (CharSequences.equalsIgnoreCase(substitute, "language")) {
                                 mask |= Context.SUBSTITUTE_LANGUAGE;
                             } else if (CharSequences.equalsIgnoreCase(substitute, "country")) {
@@ -296,6 +303,10 @@ abstract class Pooled {
                     bitMasks = mask;
                     return;
                 }
+                else if (name.equals(XML.WARNING_LISTENER)) {
+                    warningListener = (WarningListener<?>) value;
+                    return;
+                }
             }
         } catch (ClassCastException e) {
             throw new PropertyException(Errors.format(
@@ -316,22 +327,19 @@ abstract class Pooled {
      */
     public final Object getProperty(final String name) throws PropertyException {
         /*switch (name)*/ {
-            if (name.equals(XML.CONVERTER))   return converter;
-            if (name.equals(XML.RESOLVER))    return resolver;
-            if (name.equals(XML.SCHEMAS))     return schemas;
-            if (name.equals(XML.GML_VERSION)) return gmlVersion;
-            if (name.equals(XML.LOCALE))      return locale;
-            if (name.equals(XML.TIMEZONE))    return timezone;
+            if (name.equals(XML.LOCALE))           return locale;
+            if (name.equals(XML.TIMEZONE))         return timezone;
+            if (name.equals(XML.SCHEMAS))          return schemas;
+            if (name.equals(XML.GML_VERSION))      return gmlVersion;
+            if (name.equals(XML.RESOLVER))         return resolver;
+            if (name.equals(XML.CONVERTER))        return converter;
+            if (name.equals(XML.WARNING_LISTENER)) return warningListener;
             if (name.equals(XML.STRING_SUBSTITUTES)) {
-                final StringBuilder buffer = new StringBuilder();
-                if ((bitMasks & Context.SUBSTITUTE_LANGUAGE) != 0) buffer.append("language,");
-                if ((bitMasks & Context.SUBSTITUTE_COUNTRY)  != 0) buffer.append("country,");
-                final int length = buffer.length();
-                if (length != 0) {
-                    buffer.setLength(length - 1); // Remove the last coma.
-                    return buffer.toString();
-                }
-                return null;
+                int n = 0;
+                final String[] substitutes = new String[2];
+                if ((bitMasks & Context.SUBSTITUTE_LANGUAGE) != 0) substitutes[n++] = "language";
+                if ((bitMasks & Context.SUBSTITUTE_COUNTRY)  != 0) substitutes[n++] = "country";
+                return (n != 0) ? ArraysExt.resize(substitutes, n) : null;
             }
             else {
                 return getStandardProperty(convertPropertyKey(name));
@@ -428,6 +436,6 @@ abstract class Pooled {
      * @see Context#finish();
      */
     final Context begin() {
-        return new Context(converter, resolver, gmlVersion, schemas, locale, timezone, bitMasks);
+        return new Context(bitMasks, locale, timezone, schemas, gmlVersion, resolver, converter, warningListener);
     }
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java?rev=1495611&r1=1495610&r2=1495611&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java [UTF-8] Fri Jun 21 22:02:32 2013
@@ -181,30 +181,28 @@ public class ReferenceResolver {
      *   <th>As {@code <gco:CharacterString>}</th>
      *   <th>As {@code <gmx:Anchor>}</th>
      * </tr><tr>
-     * <td>{@preformat xml
-     *   <gmd:country>
-     *     <gco:CharacterString>France</gco:CharacterString>
-     *   </gmd:country>
-     * }</td>
-     * <td>{@preformat xml
-     *   <gmd:country>
-     *     <gmx:Anchor xlink:href="SDN:C320:2:FR">France</gmx:Anchor>
-     *   </gmd:country>
-     * }</td>
-     * </tr>
+     * <td>
+     *   <pre> &lt;gmd:country&gt;
+     *     &lt;gco:CharacterString&gt;France&lt;/gco:CharacterString&gt;
+     * &lt;/gmd:country&gt;</pre>
+     * </td><td>
+     *   <pre> &lt;gmd:country&gt;
+     *     &lt;gmx:Anchor xlink:href="SDN:C320:2:FR"&gt;France&lt;/gmx:Anchor&gt;
+     * &lt;/gmd:country&gt;</pre>
+     * </td></tr>
      * </table>
      *
      * Subclasses can override this method if they can provide a mapping from some text
      * values to anchors.
      *
      * @param  context Context (GML version, locale, <i>etc.</i>) of the (un)marshalling process.
-     * @param  object  The object for which an anchor is requested. Often same than {@code text},
-     *                 but can also be the {@link java.net.URI} or {@link java.util.Locale} instance
-     *                 for which {@code text} is a string representation.
-     * @param  text    The textual representation of the object for which to get the anchor.
+     * @param  value   The value for which an anchor is requested. Often the same instance than {@code text},
+     *                 but can also be the {@link java.net.URI} or {@link java.util.Locale} instance for which
+     *                 {@code text} is a string representation.
+     * @param  text    The textual representation of the value for which to get the anchor.
      * @return The anchor for the given text, or {@code null} if none.
      */
-    public XLink anchor(final MarshalContext context, final Object object, final CharSequence text) {
+    public XLink anchor(final MarshalContext context, final Object value, final CharSequence text) {
         return (text instanceof Anchor) ? (Anchor) text : null;
     }
 }



Mime
View raw message