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();
}
}
}