Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -17,8 +17,12 @@ package org.apache.sis.internal.jaxb; import java.util.Map; +import java.util.Deque; +import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.Locale; import java.util.TimeZone; +import java.util.LinkedList; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.LogRecord; @@ -32,21 +36,25 @@ import org.apache.sis.util.resources.Ind import org.apache.sis.internal.jaxb.gco.PropertyType; import org.apache.sis.internal.system.Semaphores; import org.apache.sis.internal.system.Loggers; +import org.apache.sis.xml.IdentifierSpace; import org.apache.sis.xml.MarshalContext; import org.apache.sis.xml.ValueConverter; import org.apache.sis.xml.ReferenceResolver; +// Branch-dependent imports. +import org.apache.sis.internal.jdk8.JDK8; + /** * Thread-local status of a marshalling or unmarshalling processes. * All non-static methods in this class except {@link #finish()} are implementation of public API. * All static methods are internal API. Those methods expect a {@code Context} instance as their first argument. - * They should be though as if they were normal member methods, except that they accept {@code null} instance + * They can be though as if they were normal member methods, except that they accept {@code null} instance * if no (un)marshalling is in progress. * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.6 + * @version 0.7 * @module */ public final class Context extends MarshalContext { @@ -98,8 +106,6 @@ public final class Context extends Marsh /** * The logger to use for warnings that are specific to XML. - * - * @see org.apache.sis.metadata.iso.ISOMetadata#LOGGER */ public static final Logger LOGGER = Logging.getLogger(Loggers.XML); @@ -109,9 +115,9 @@ public final class Context extends Marsh private int bitMasks; /** - * The locale to use for marshalling, or {@code null} if no locale were explicitly specified. + * The locale to use for marshalling, or an empty queue if no locale were explicitly specified. */ - private Locale locale; + private final Deque locales; /** * The timezone, or {@code null} if unspecified. @@ -142,6 +148,24 @@ public final class Context extends Marsh private final ValueConverter converter; /** + * The objects associated to XML identifiers. At marhalling time, this is used for avoiding duplicated identifiers + * in the same XML document. At unmarshalling time, this is used for getting a previous object from its identifier. + * + * @since 0.7 + */ + private final Map identifiers; + + /** + * The identifiers used for marshalled objects. This is the converse of {@link #identifiers}, used in order to + * identify which {@code gml:id} to use for the given object. The {@code gml:id} to use are not necessarily the + * same than the one associated to {@link IdentifierSpace#ID} if the identifier was already used for another + * object in the same XML document. + * + * @since 0.7 + */ + private final Map identifiedObjects; + + /** * The object to inform about warnings, or {@code null} if none. */ private final WarningListener warningListener; @@ -160,7 +184,7 @@ public final class Context extends Marsh /** * The context which was previously used. This form a linked list allowing to push properties - * (e.g. {@link #push(Locale)}) and pull back the context to its previous state once finished. + * and pull back the context to its previous state once finished. */ private final Context previous; @@ -193,50 +217,25 @@ public final class Context extends Marsh final ReferenceResolver resolver, final ValueConverter converter, final WarningListener warningListener) { - 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; + this.bitMasks = bitMasks; + this.locales = new LinkedList(); + 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; + this.identifiers = new HashMap(); + this.identifiedObjects = new IdentityHashMap(); if ((bitMasks & MARSHALLING) != 0) { if (!Semaphores.queryAndSet(Semaphores.NULL_COLLECTION)) { this.bitMasks |= CLEAR_SEMAPHORE; } } - previous = current(); - CURRENT.set(this); - } - - /** - * Inherits all configuration from the previous context, if any. - * - * @param previous The context from which to inherit the configuration, or {@code null}. - * - * @see #push(Locale) - */ - @SuppressWarnings("ThisEscapedInObjectConstruction") - private Context(final Context previous) { - if (previous != null) { - bitMasks = previous.bitMasks; - locale = previous.locale; - timezone = previous.timezone; - schemas = previous.schemas; - versionGML = previous.versionGML; - resolver = previous.resolver; - converter = previous.converter; - warningListener = previous.warningListener; - } else { - timezone = null; - schemas = null; - versionGML = null; - resolver = null; - converter = null; - warningListener = null; + if (locale != null) { + locales.add(locale); } - this.previous = previous; + previous = CURRENT.get(); CURRENT.set(this); } @@ -247,7 +246,7 @@ public final class Context extends Marsh */ @Override public final Locale getLocale() { - return locale; + return locales.peekLast(); } /** @@ -275,12 +274,18 @@ public final class Context extends Marsh return null; } - /* - * ---- END OF PUBLIC API -------------------------------------------------------------- - * - * Following are internal API. They are provided as static methods with a Context - * argument rather than normal member methods in order to accept null context. - */ + + + + //////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// END OF PUBLIC (non-internal) API. //////// + //////// //////// + //////// Following are internal API. They are provided as static methods //////// + //////// with a Context argument rather than normal member methods //////// + //////// in order to accept null context. //////// + //////// //////// + //////////////////////////////////////////////////////////////////////////////////////// /** * Returns the context of the XML (un)marshalling currently progressing in the current thread, @@ -293,6 +298,45 @@ public final class Context extends Marsh } /** + * Sets the locale to the given value. The old locales are remembered and will + * be restored by the next call to {@link #pull()}. This method can be invoked + * when marshalling object that need to marshall their children in a different + * locale, like below: + * + * {@preformat java + * private void beforeMarshal(Marshaller marshaller) { + * Context.push(language); + * } + * + * private void afterMarshal(Marshaller marshaller) { + * Context.pull(); + * } + * } + * + * @param locale The locale to set, or {@code null}. + */ + public static void push(Locale locale) { + final Context current = current(); + if (current != null) { + if (locale == null) { + locale = current.getLocale(); + } + current.locales.addLast(locale); + } + } + + /** + * Restores the locale which was used prior the call to {@link #push(Locale)}. + * It is not necessary to invoke this method in a {@code finally} block. + */ + public static void pull() { + final Context current = current(); + if (current != null) { + current.locales.removeLast(); + } + } + + /** * Returns {@code true} if the given flag is set. * * @param context The current context, or {@code null} if none. @@ -305,6 +349,30 @@ public final class Context extends Marsh } /** + * 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. + * + *
API 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; + } + + /** * 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 '/'}. @@ -389,24 +457,55 @@ public final class Context extends Marsh } /** - * 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. + * If a {@code gml:id} value has already been used for the given object in the current XML document, + * returns that identifier. Otherwise returns {@code null}. * - *
API 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 object The object for which to get the {@code gml:id}. + * @return The identifier used in the current XML document for the given object, or {@code null} if none. + * + * @since 0.7 + */ + public static String getObjectID(final Context context, final Object object) { + return (context != null) ? context.identifiedObjects.get(object) : null; + } + + /** + * Returns the object for the given {@code gml:id}, or {@code null} if none. + * This association is valid only for the current XML document. * * @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. + * @param id The identifier for which to get the object. + * @return The object associated to the given identifier, or {@code null} if none. * - * @see #getVersion(String) + * @since 0.7 */ - public static boolean isGMLVersion(final Context context, final Version version) { + public static Object getObjectForID(final Context context, final String id) { + return (context != null) ? context.identifiers.get(id) : null; + } + + /** + * Returns {@code true} if the given identifier is available, or {@code false} if it is used by another object. + * If this method returns {@code true}, then the given identifier is associated to the given object for future + * invocation of {@code Context} method. If this method returns {@code false}, then the caller is responsible + * for computing an other identifier candidate. + * + * @param context The current context, or {@code null} if none. + * @param object The object for which to assign the {@code gml:id}. + * @param id The identifier to assign to the given object. + * @return {@code true} if the given identifier can be used. + * + * @since 0.7 + */ + public static boolean setObjectForID(final Context context, final Object object, final String id) { if (context != null) { - final Version versionGML = context.versionGML; - if (versionGML != null) { - return versionGML.compareTo(version) >= 0; + final Object existing = JDK8.putIfAbsent(context.identifiers, id, object); + if (existing == null) { + if (context.identifiedObjects.put(object, id) != null) { + throw new AssertionError(id); // Caller forgot to invoke getExistingID(context, object). + } + } else if (existing != object) { + return false; } } return true; @@ -544,44 +643,6 @@ public final class Context extends Marsh } /** - * Sets the locale to the given value. The old locales are remembered and will - * be restored by the next call to {@link #pull()}. This method can be invoked - * when marshalling object that need to marshall their children in a different - * locale, like below: - * - * {@preformat java - * private void beforeMarshal(Marshaller marshaller) { - * Context.push(language); - * } - * - * private void afterMarshal(Marshaller marshaller) { - * Context.pull(); - * } - * } - * - * @param locale The locale to set, or {@code null}. - */ - public static void push(final Locale locale) { - final Context context = new Context(current()); - if (locale != null) { - context.locale = locale; - } - } - - /** - * Restores the locale (or any other setting) which was used prior the call - * to {@link #push(Locale)}. It is not necessary to invoke this method in a - * {@code finally} block if the parent {@code Context} is itself - * disposed in a {@code finally} block. - */ - public static void pull() { - final Context current = current(); - if (current != null) { - current.finish(); - } - } - - /** * Invoked in a {@code finally} block when a unmarshalling process is finished. */ public final void finish() { Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -106,7 +106,7 @@ import org.apache.sis.util.iso.SimpleInt * @author Cédric Briançon (Geomatys) * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.4 + * @version 0.7 * @module * * @see XmlAdapter @@ -131,9 +131,8 @@ public abstract class PropertyType - *
  • {@link ObjectReference} defines the {@code idref}, {@code uuidref}, {@code xlink:href}, - * {@code xlink:role}, {@code xlink:arcrole}, {@code xlink:title}, {@code xlink:show} and - * {@code xlink:actuate} attributes.
  • + *
  • {@link ObjectReference} defines the {@code uuidref}, {@code xlink:href}, {@code xlink:role}, + * {@code xlink:arcrole}, {@code xlink:title}, {@code xlink:show} and {@code xlink:actuate} attributes.
  • *
  • {@link String} defines the {@code nilReason} attribute.
  • * * @@ -174,7 +173,6 @@ public abstract class PropertyType type = getBoundType(); + final Context context = Context.current(); + final ReferenceResolver resolver = Context.resolver(context); + final String id = Context.getObjectID(context, value); + if (id != null && resolver.canSubstituteByReference(context, type, value, id)) try { + final XLink link = new XLink(); + link.setHRef(new URI(null, null, id)); + reference = new ObjectReference(null, link); + return; + } catch (URISyntaxException e) { + Context.warningOccured(context, getClass(), "", e, true); + } + metadata = value; // Non-null only after we verified that not a NilObject or xlink:href="#foo". if (value instanceof IdentifiedObject) { /* * Get the identifiers as full UUID or XLink objects. We do not use the more permissive methods @@ -209,13 +226,9 @@ public abstract class PropertyType type = getBoundType(); - final Context context = Context.current(); - final ReferenceResolver resolver = Context.resolver(context); /* * Check if the user gives us the permission to use reference to those identifiers. * If not, forget them in order to avoid marshalling the identifiers twice (see the @@ -350,8 +363,8 @@ public abstract class PropertyType * * @see GML identifiers - * @see org.apache.sis.internal.jaxb.gco.ObjectReference#getUUIDREF() + * @see org.apache.sis.internal.jaxb.gco.PropertyType#getUUIDREF() */ @XmlID @XmlAttribute(namespace = Namespaces.GML, required = true) @@ -94,7 +94,7 @@ public abstract class GMLAdapter { } /** - * Assign the {@link #id} value (if non-null) to the given object. This method + * Assigns the {@link #id} value (if non-null) to the given object. This method * is typically invoked at unmarshalling time in order to assign the ID of this * temporary wrapper to the "real" GeoAPI implementation instance. * Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -34,7 +34,7 @@ * * @author Cédric Briançon (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module */ package org.apache.sis.internal.jaxb; Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -64,7 +64,10 @@ public final class CollectionsExt extend /** * Returns the first element of the given iterable, or {@code null} if none. - * This method is null-safe. Note that the first element may be null. + * This method does not emit warning if more than one element is found. + * Consequently, this method should be used only when multi-occurrence is not ambiguous. + * + *

    This method is null-safe. Note however that the first element may be null.

    * * @param The type of elements contained in the iterable. * @param collection The iterable from which to get the first element, or {@code null}. Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -187,6 +187,7 @@ abstract class RecordDefinition { // Int /** * Read-only access to the map of member indices. */ + @SuppressWarnings("ReturnOfCollectionOrArrayField") final Map memberIndices() { return memberIndices; } @@ -195,7 +196,8 @@ abstract class RecordDefinition { // Int * Returns the number of elements in records. */ final int size() { - return members.length; + // 'members' should not be null, but let be safe. + return (members != null) ? members.length : 0; } /** @@ -241,7 +243,7 @@ abstract class RecordDefinition { // Int final String toString(final String head, final Object values) { final StringBuilder buffer = new StringBuilder(250); final String lineSeparator = JDK7.lineSeparator(); - final String[] names = new String[members.length]; + final String[] names = new String[size()]; int width = 0; buffer.append(head).append("[“").append(getRecordType().getTypeName()).append("”] {").append(lineSeparator); for (int i=0; iAny other JAR registering a {@link org.apache.sis.util.logging.LoggerFactory} implementation. * * - *
    Apache SIS logger constants
    - * Some static final {@code Logger} constant defined in Apache SIS are: - *
      - *
    • {@link org.apache.sis.metadata.iso.ISOMetadata#LOGGER} for the {@code org.apache.sis.metadata.iso.*} packages
    • - *
    - * *
    Note for SIS developers
    * All SIS code should fetch their logger through a call to our custom * {@link org.apache.sis.util.logging.Logging#getLogger(String)} method instead than Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -16,6 +16,7 @@ */ package org.apache.sis.xml; +import java.net.URI; import java.util.UUID; import java.lang.reflect.Proxy; import org.opengis.metadata.Identifier; @@ -23,6 +24,7 @@ import org.apache.sis.util.Emptiable; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.LenientComparable; import org.apache.sis.internal.jaxb.gmx.Anchor; +import org.apache.sis.internal.jaxb.Context; import static org.apache.sis.util.ArgumentChecks.*; @@ -33,13 +35,13 @@ import static org.apache.sis.util.Argume * to an existing instance instead than writing the full object definition. * At unmarshalling time, this class replaces (if possible) a reference by the full object definition. * - *

    Subclasses can override the methods defined in this class in order to search in their - * own catalog. See the {@link XML#RESOLVER} javadoc for an example of registering a custom - * {@code ReferenceResolver} to a unmarshaller.

    + *

    Subclasses can override the methods defined in this class in order to search in their own catalog. + * See the {@link XML#RESOLVER} javadoc for an example of registering a custom {@code ReferenceResolver} + * to a unmarshaller.

    * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.4 + * @version 0.7 * @module */ public class ReferenceResolver { @@ -72,8 +74,7 @@ public class ReferenceResolver { * * @param The compile-time type of the {@code type} argument. * @param context Context (GML version, locale, etc.) of the (un)marshalling process. - * @param type The type of object to be unmarshalled as an interface. - * This is usually a GeoAPI interface. + * @param type The type of object to be unmarshalled, often as a GeoAPI interface. * @param identifiers An arbitrary amount of identifiers. For each identifier, the * {@linkplain Identifier#getAuthority() authority} is typically (but not * necessarily) one of the constants defined in {@link IdentifierSpace}. @@ -90,13 +91,12 @@ public class ReferenceResolver { } /** - * Returns an object of the given type for the given {@code uuid} attribute, or {@code null} - * if none. The default implementation returns {@code null} in all cases. + * Returns an object of the given type for the given {@code uuid} attribute, or {@code null} if none. + * The default implementation returns {@code null} in all cases. * * @param The compile-time type of the {@code type} argument. * @param context Context (GML version, locale, etc.) of the (un)marshalling process. - * @param type The type of object to be unmarshalled as an interface. - * This is usually a GeoAPI interface. + * @param type The type of object to be unmarshalled, often as a GeoAPI interface. * @param uuid The {@code uuid} attributes. * @return An object of the given type for the given {@code uuid} attribute, or {@code null} if none. */ @@ -108,26 +108,77 @@ public class ReferenceResolver { } /** - * Returns an object of the given type for the given {@code xlink} attribute, or {@code null} - * if none. The default implementation returns {@code null} in all cases. + * Returns an object of the given type for the given {@code xlink} attribute, or {@code null} if none. + * The default implementation performs the following lookups: + * + *
      + *
    • If the {@link XLink#getHRef() xlink:href} attribute is an {@linkplain URI#getFragment() URI fragment} + * of the form {@code "#foo"} and if an object of class {@code type} with the {@code gml:id="foo"} attribute + * has previously been seen in the same XML document, then that object is returned.
    • + *
    • Otherwise returns {@code null}.
    • + *
    * * @param The compile-time type of the {@code type} argument. * @param context Context (GML version, locale, etc.) of the (un)marshalling process. - * @param type The type of object to be unmarshalled, often as an interface. + * @param type The type of object to be unmarshalled, often as a GeoAPI interface. * @param link The {@code xlink} attributes. * @return An object of the given type for the given {@code xlink} attribute, or {@code null} if none. */ public T resolve(final MarshalContext context, final Class type, final XLink link) { ensureNonNull("type", type); ensureNonNull("xlink", link); + final URI href = link.getHRef(); + if (href != null && href.toString().startsWith("#")) { + final Object object = Context.getObjectForID(Context.current(), href.getFragment()); + if (type.isInstance(object)) { + return type.cast(object); + } + } return null; } /** - * Returns {@code true} if the marshaller can use a reference to the given metadata - * instead than writing the full element. This method is invoked when a metadata to - * be marshalled has a UUID identifier. Because those metadata may be defined externally, - * SIS can not know if the metadata shall be fully marshalled or not. + * Returns {@code true} if the marshaller can use a {@code xlink:href="#id"} reference to the given object + * instead than writing the full XML element. This method is invoked by the marshaller when: + * + *
      + *
    • The given object has already been marshalled in the same XML document.
    • + *
    • The marshalled object had a {@code gml:id} attribute + *
        + *
      • either specified explicitely by + * {@linkplain IdentifierMap#put IdentifierMap.put}({@linkplain IdentifierSpace#ID}, id)
      • + *
      • or inferred automatically by the marshalled object + * (e.g. {@link org.apache.sis.referencing.AbstractIdentifiedObject}).
      • + *
      + *
    • + *
    + * + * Note that if this method returns {@code true}, then the use of {@code xlink:href="#id"} will have + * precedence over {@linkplain #canSubstituteByReference(MarshalContext, Class, Object, UUID) UUID} + * and {@linkplain #canSubstituteByReference(MarshalContext, Class, Object, XLink) XLink alternatives}. + * + *

    The default implementation unconditionally returns {@code true}. + * Subclasses can override this method if they want to filter which objects to declare by reference.

    + * + * @param The compile-time type of the {@code type} argument. + * @param context Context (GML version, locale, etc.) of the (un)marshalling process. + * @param type The type of object to be unmarshalled, often as a GeoAPI interface. + * @param object The object to be marshalled. + * @param id The {@code gml:id} value of the object to be marshalled. + * @return {@code true} if the marshaller can use the {@code xlink:href="#id"} attribute + * instead than marshalling the given object. + * + * @since 0.7 + */ + public boolean canSubstituteByReference(final MarshalContext context, final Class type, final T object, final String id) { + return true; + } + + /** + * Returns {@code true} if the marshaller can use a reference to the given object + * instead than writing the full XML element. This method is invoked when an object to + * be marshalled has a UUID identifier. Because those object may be defined externally, + * SIS can not know if the object shall be fully marshalled or not. * Such information needs to be provided by the application. * *

    The default implementation returns {@code true} in the following cases:

    @@ -140,11 +191,11 @@ public class ReferenceResolver { * * @param The compile-time type of the {@code type} argument. * @param context Context (GML version, locale, etc.) of the (un)marshalling process. - * @param type The type of object to be unmarshalled, often as an interface. + * @param type The type of object to be unmarshalled, often as a GeoAPI interface. * @param object The object to be marshalled. * @param uuid The unique identifier of the object to be marshalled. * @return {@code true} if the marshaller can use the {@code uuidref} attribute - * instead than marshalling the given metadata. + * instead than marshalling the given object. */ public boolean canSubstituteByReference(final MarshalContext context, final Class type, final T object, final UUID uuid) { return (object instanceof NilObject) || (object instanceof Emptiable && ((Emptiable) object).isEmpty()); @@ -152,9 +203,9 @@ public class ReferenceResolver { /** * Returns {@code true} if the marshaller can use a {@code xlink:href} reference to the given - * metadata instead than writing the full element. This method is invoked when a metadata to be - * marshalled has a {@link XLink} identifier. Because those metadata may be defined externally, - * SIS can not know if the metadata shall be fully marshalled or not. + * object instead than writing the full XML element. This method is invoked when an object to be + * marshalled has a {@link XLink} identifier. Because those object may be defined externally, + * SIS can not know if the object shall be fully marshalled or not. * Such information needs to be provided by the application. * *

    The default implementation returns {@code true} in the following cases:

    @@ -167,12 +218,11 @@ public class ReferenceResolver { * * @param The compile-time type of the {@code type} argument. * @param context Context (GML version, locale, etc.) of the (un)marshalling process. - * @param type The type of object to be marshalled as an interface. - * This is usually a GeoAPI interface. + * @param type The type of object to be unmarshalled, often as a GeoAPI interface. * @param object The object to be marshalled. * @param link The reference of the object to be marshalled. * @return {@code true} if the marshaller can use the {@code xlink:href} attribute - * instead than marshalling the given metadata. + * instead than marshalling the given object. */ public boolean canSubstituteByReference(final MarshalContext context, final Class type, final T object, final XLink link) { return (object instanceof NilObject) || (object instanceof Emptiable && ((Emptiable) object).isEmpty()); Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XLink.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XLink.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XLink.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XLink.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -416,12 +416,7 @@ public class XLink implements Serializab } /** - * Returns a URN to an external resources, or to an other part of a XML document, or an - * identifier. - * - *
    Note: - * This serves a role similar to {@code idref}. The {@code idref} attribute allows an XML element - * to refer to another XML element that has a corresponding {@code id} attribute.
    + * Returns a URN to an external resources, or to an other part of a XML document, or an identifier. * * @return A URN to a resources, or {@code null} if none. * Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -171,16 +171,21 @@ public final class XML extends Static { public static final String GML_VERSION = "org.apache.sis.gml.version"; /** - * Allows client code to replace {@code xlink} or {@code uuidref} attributes by the actual - * object to use. The value for this property shall be an instance of {@link ReferenceResolver}. + * Allows client code to replace {@code xlink} or {@code uuidref} attributes by the actual objects to use. + * The value for this property shall be an instance of {@link ReferenceResolver}. * *

    If a property in a XML document is defined only by {@code xlink} or {@code uuidref} attributes, - * without any concrete definition, then the default behavior is to create an empty element which - * contain only the values of the above-cited attributes. This is usually not the right behavior, - * since we should use the reference ({@code href} or {@code uuidref} attributes) for fetching - * the appropriate object. However doing so require some application knowledge, for example a - * catalog where to perform the search, which is left to users. Users can define their search - * algorithm by subclassing {@link ReferenceResolver} and configure a unmarshaller as below:

    + * without any concrete definition, then the default behavior is as below:

    + * + *
      + *
    • If the reference is of the form {@code xlink:href="#foo"} and an object with the {@code gml:id="foo"} + * attribute was previously found in the same XML document, then that object will be used.
    • + *
    • Otherwise an empty element containing only the values of the above-cited attributes is created.
    • + *
    + * + * Applications can sometime do better by using some domain-specific knowledge, for example by searching in a + * database. Users can define their search algorithm by subclassing {@link ReferenceResolver} and configuring + * a unmarshaller as below: * * {@preformat java * ReferenceResolver myResolver = ...; Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -59,7 +59,7 @@ * @author Guilhem Legal (Geomatys) * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.4 + * @version 0.7 * @module */ package org.apache.sis.xml; Modified: sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/gco/StringAdapterTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/gco/StringAdapterTest.java?rev=1707577&r1=1707576&r2=1707577&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/gco/StringAdapterTest.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/gco/StringAdapterTest.java [UTF-8] Thu Oct 8 16:10:05 2015 @@ -32,7 +32,7 @@ import static org.junit.Assert.*; * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module */ public final strictfp class StringAdapterTest extends TestCase { @@ -48,7 +48,7 @@ public final strictfp class StringAdapte /** * Tests {@link StringAdapter#toString(CharSequence)} for an {@link InternationalString} - * having loalization in different languages. + * having localizations in different languages. */ @Test @DependsOnMethod("testToUnlocalizedString") @@ -57,25 +57,16 @@ public final strictfp class StringAdapte i18n.add(Locale.ENGLISH, "A word"); i18n.add(Locale.FRENCH, "Un mot"); i18n.add(Locale.JAPANESE, "言葉"); - Context.push(Locale.JAPANESE); + final Context context = new Context(0, Locale.ENGLISH, null, null, null, null, null, null); try { - assertEquals("言葉", StringAdapter.toString(i18n)); - Context.push(Locale.FRENCH); - try { - assertEquals("Un mot", StringAdapter.toString(i18n)); - Context.push(Locale.ENGLISH); - try { - assertEquals("A word", StringAdapter.toString(i18n)); - } finally { - Context.pull(); - } - assertEquals("Un mot", StringAdapter.toString(i18n)); - } finally { - Context.pull(); - } - assertEquals("言葉", StringAdapter.toString(i18n)); + Context.push(Locale.JAPANESE); assertEquals("言葉", StringAdapter.toString(i18n)); + Context.push(Locale.FRENCH); assertEquals("Un mot", StringAdapter.toString(i18n)); + Context.push(Locale.ENGLISH); assertEquals("A word", StringAdapter.toString(i18n)); + Context.pull(); assertEquals("Un mot", StringAdapter.toString(i18n)); + Context.pull(); assertEquals("言葉", StringAdapter.toString(i18n)); + Context.pull(); assertEquals("A word", StringAdapter.toString(i18n)); } finally { - Context.pull(); + context.finish(); } } }