Author: desruisseaux
Date: Wed Oct 7 22:05:28 2015
New Revision: 1707407
URL: http://svn.apache.org/viewvc?rev=1707407&view=rev
Log:
Initial support of xlink:href referencing gml:id (SIS-173).
Added:
sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/DuplicatedParameters.xml
- copied, changed from r1707309, sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/ParameterMarshallingTest.java
sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1707407&r1=1707406&r2=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
[UTF-8] Wed Oct 7 22:05:28 2015
@@ -473,76 +473,6 @@ public class AbstractIdentifiedObject ex
}
/**
- * The {@code gml:id}, which is mandatory. The current implementation searches for the
first identifier,
- * regardless its authority. If no identifier is found, then the name is used.
- * If no name is found (which should not occur for valid objects), then this method returns
{@code null}.
- *
- * <p>If an identifier has been found, this method returns the concatenation of
the following elements
- * separated by hyphens:</p>
- * <ul>
- * <li>The code space in lower case, retaining only characters that are valid
for Unicode identifiers.</li>
- * <li>The object type as defined in OGC's URN (see {@link org.apache.sis.internal.util.DefinitionURI})</li>
- * <li>The object code, retaining only characters that are valid for Unicode
identifiers.</li>
- * </ul>
- *
- * Example: {@code "epsg-crs-4326"}.
- *
- * <p>The returned ID needs to be unique only in the XML document being marshalled.
- * Consecutive invocations of this method do not need to return the same value,
- * since it may depends on the marshalling context.</p>
- */
- @XmlID
- @XmlSchemaType(name = "ID")
- @XmlAttribute(name = "id", namespace = Namespaces.GML, required = true)
- @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
- final String getID() {
- final Context context = Context.current();
- String candidate = Context.getExistingID(context, this);
- if (candidate == null) {
- final StringBuilder id = new StringBuilder();
- /*
- * We will iterate over the identifiers first. Only after the iteration is over,
- * if we found no suitable ID, then we will use the primary name as a last resort.
- */
- if (identifiers != null) {
- for (final Identifier identifier : identifiers) {
- if (appendUnicodeIdentifier(id, '-', identifier.getCodeSpace(), ":",
true) | // Really |, not ||
- appendUnicodeIdentifier(id, '-', NameMeaning.toObjectType(getClass()),
":", false) |
- appendUnicodeIdentifier(id, '-', identifier.getCode(), ":", true))
- {
- /*
- * Check for ID uniqueness. If the ID is rejected, then we just need
to clear
- * the buffer and let the iteration continue the search for another
ID.
- */
- candidate = id.toString();
- if (Context.isAvailableID(context, this, candidate)) {
- return candidate;
- }
- }
- id.setLength(0); // Clear the buffer for another try.
- }
- }
- /*
- * In last ressort, append code without codespace since the name are often verbose.
- * If that name is also used, append a number until we find a free ID.
- */
- if (name != null && appendUnicodeIdentifier(id, '-', name.getCode(),
":", false)) {
- candidate = id.toString();
- if (!Context.isAvailableID(context, this, candidate)) {
- final int s = id.append('-').length();
- int n = 0;
- do {
- if (++n == 100) return null; // Arbitrary limit.
- candidate = id.append(n).toString();
- id.setLength(s);
- } while (!Context.isAvailableID(context, this, candidate));
- }
- }
- }
- return candidate;
- }
-
- /**
* Returns the primary name by which this object is identified.
*
* @return The primary name.
@@ -965,6 +895,90 @@ public class AbstractIdentifiedObject ex
}
/**
+ * The {@code gml:id}, which is mandatory. The current implementation searches for the
first identifier,
+ * regardless its authority. If no identifier is found, then the name is used.
+ * If no name is found (which should not occur for valid objects), then this method returns
{@code null}.
+ *
+ * <p>If an identifier has been found, this method returns the concatenation of
the following elements
+ * separated by hyphens:</p>
+ * <ul>
+ * <li>The code space in lower case, retaining only characters that are valid
for Unicode identifiers.</li>
+ * <li>The object type as defined in OGC's URN (see {@link org.apache.sis.internal.util.DefinitionURI})</li>
+ * <li>The object code, retaining only characters that are valid for Unicode
identifiers.</li>
+ * </ul>
+ *
+ * Example: {@code "epsg-crs-4326"}.
+ *
+ * <p>The returned ID needs to be unique only in the XML document being marshalled.
+ * Consecutive invocations of this method do not need to return the same value,
+ * since it may depends on the marshalling context.</p>
+ */
+ @XmlID
+ @XmlSchemaType(name = "ID")
+ @XmlAttribute(name = "id", namespace = Namespaces.GML, required = true)
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ final String getID() {
+ final Context context = Context.current();
+ String candidate = Context.getObjectID(context, this);
+ if (candidate == null) {
+ final StringBuilder id = new StringBuilder();
+ /*
+ * We will iterate over the identifiers first. Only after the iteration is over,
+ * if we found no suitable ID, then we will use the primary name as a last resort.
+ */
+ if (identifiers != null) {
+ for (final Identifier identifier : identifiers) {
+ if (appendUnicodeIdentifier(id, '-', identifier.getCodeSpace(), ":",
true) | // Really |, not ||
+ appendUnicodeIdentifier(id, '-', NameMeaning.toObjectType(getClass()),
":", false) |
+ appendUnicodeIdentifier(id, '-', identifier.getCode(), ":", true))
+ {
+ /*
+ * Check for ID uniqueness. If the ID is rejected, then we just need
to clear
+ * the buffer and let the iteration continue the search for another
ID.
+ */
+ candidate = id.toString();
+ if (Context.setObjectForID(context, this, candidate)) {
+ return candidate;
+ }
+ }
+ id.setLength(0); // Clear the buffer for another try.
+ }
+ }
+ /*
+ * In last ressort, append code without codespace since the name are often verbose.
+ * If that name is also used, append a number until we find a free ID.
+ */
+ if (name != null && appendUnicodeIdentifier(id, '-', name.getCode(),
":", false)) {
+ candidate = id.toString();
+ if (!Context.setObjectForID(context, this, candidate)) {
+ final int s = id.append('-').length();
+ int n = 0;
+ do {
+ if (++n == 100) return null; // Arbitrary limit.
+ candidate = id.append(n).toString();
+ id.setLength(s);
+ } while (!Context.setObjectForID(context, this, candidate));
+ }
+ }
+ }
+ return candidate;
+ }
+
+ /**
+ * Invoked by JAXB at unmarhalling time for specifying the value of the {@code gml:id}
attribute.
+ * That GML identifier is not actually stored in this {@code AbstractIdentifiedObject}
+ * since we rather generate it dynamically from the ISO 19111 identifiers. But we still
+ * need to declare that identifier to our unmarshaller context, in case it is referenced
+ * from elsewhere in the XML document.
+ */
+ private void setID(final String id) {
+ final Context context = Context.current();
+ if (!Context.setObjectForID(context, this, id)) {
+ Context.warningOccured(context, getClass(), "setID", Errors.class, Errors.Keys.DuplicatedIdentifier_1,
id);
+ }
+ }
+
+ /**
* Returns a single element from the {@code Set<Identifier>} collection, or {@code
null} if none.
* We have to define this method because ISO 19111 defines the {@code identifiers} property
as a collection
* while GML 3.2 defines it as a singleton.
Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/ParameterMarshallingTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/ParameterMarshallingTest.java?rev=1707407&r1=1707406&r2=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/ParameterMarshallingTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/ParameterMarshallingTest.java
[UTF-8] Wed Oct 7 22:05:28 2015
@@ -50,7 +50,7 @@ import java.util.Objects;
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.6
- * @version 0.6
+ * @version 0.7
* @module
*/
@DependsOn({
@@ -361,23 +361,48 @@ public final strictfp class ParameterMar
}
/**
- * Tests (un)marshalling of a parameter value group.
+ * Tests marshalling of a parameter value group.
*
- * @throws JAXBException if an error occurred during marshalling or unmarshalling.
+ * @throws JAXBException if an error occurred during marshalling.
*/
@Test
@DependsOnMethod("testDescriptorGroup")
- public void testValueGroup() throws JAXBException {
- // Test marshalling.
+ public void testValueGroupMmarshalling() throws JAXBException {
assertMarshalEqualsFile("ParameterValueGroup.xml",
ParameterFormatTest.createMercatorParameters().createValue(),
- "xmlns:*", "xsi:schemaLocation", "gml:id");
+ "xmlns:*", "xsi:schemaLocation");
+ }
- // Test unmarshalling.
- final DefaultParameterValueGroup group = unmarshalFile(
- DefaultParameterValueGroup.class, "ParameterValueGroup.xml");
+ /**
+ * Tests unmarshalling of a parameter value group. The XML file use {@code xlink:href}
attributes
+ * for avoiding to repeat the full definition of descriptors.
+ *
+ * @throws JAXBException if an error occurred during unmarshalling.
+ */
+ @Test
+ @DependsOnMethod("testDescriptorGroup")
+ public void testValueGroupUnmarshalling() throws JAXBException {
+ testValueGroupUnmarshalling("ParameterValueGroup.xml");
+ }
- // Verify the ParameterValues properties.
+ /**
+ * Tests unmarshalling of a parameter value group. The XML file does not use {@code xlink:href}
attributes;
+ * descriptor definitions are repeated. The intend of this test is to test Apache SIS
capability to replace
+ * duplicates instances by unique instances.
+ *
+ * @throws JAXBException if an error occurred during unmarshalling.
+ */
+ @Test
+ @DependsOnMethod("testValueGroupUnmarshalling")
+ public void testDuplicatedParametersUnmarshalling() throws JAXBException {
+ testValueGroupUnmarshalling("DuplicatedParameters.xml");
+ }
+
+ /**
+ * Tests unmarshalling of the given file.
+ */
+ private void testValueGroupUnmarshalling(final String file) throws JAXBException {
+ final DefaultParameterValueGroup group = unmarshalFile(DefaultParameterValueGroup.class,
file);
verifyDescriptorGroup(group.getDescriptor());
final Iterator<GeneralParameterValue> it = group.values().iterator();
final Iterator<GeneralParameterDescriptor> itd = group.getDescriptor().descriptors().iterator();
Copied: sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/DuplicatedParameters.xml
(from r1707309, sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/DuplicatedParameters.xml?p2=sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/DuplicatedParameters.xml&p1=sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml&r1=1707309&r2=1707407&rev=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml
(original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/DuplicatedParameters.xml
Wed Oct 7 22:05:28 2015
@@ -66,11 +66,13 @@
<gml:name codeSpace="OGC">Mercator_1SP</gml:name>
<gml:minimumOccurs>0</gml:minimumOccurs>
- <!-- All parameters below this point are duplication of the parameters already defined
above.
- For now we do not yet use the XML references, but this is something that we will
need to
- support in a future SIS version. See https://issues.apache.org/jira/browse/SIS-173
issue.
- In the meantime, we have to change the gml:id attribute values used below for
avoiding
- collisions with the gml:id attribute used in above parameters. -->
+ <!-- Parameters below this point are duplication of the parameters already defined
above.
+ We could use xlink:href attribute for avoiding duplication (this is the purpose
of
+ the ParameterValueGroup.xml test file), but in this file we intentionally keep
the
+ duplication in order to test Apache SIS capability to replace duplicated instances
+ by unique instances. Note that we have to change the gml:id attribute values used
+ below for avoiding collisions with the gml:id attribute used in above parameters.
-->
+
<gml:parameter>
<gml:OperationParameter gml:id="epsg-8801">
<gml:identifier codeSpace="IOGP">urn:ogc:def:parameter:EPSG::8801</gml:identifier>
Modified: sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml?rev=1707407&r1=1707406&r2=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml
(original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/resources/org/apache/sis/parameter/ParameterValueGroup.xml
Wed Oct 7 22:05:28 2015
@@ -19,6 +19,7 @@
-->
<gml:ParameterValueGroup xsi:schemaLocation = "http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/coordinateOperations.xsd"
+ xmlns:xlink = "http://www.w3.org/1999/xlink"
xmlns:gml = "http://www.opengis.net/gml/3.2"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance">
@@ -66,33 +67,13 @@
<gml:name codeSpace="OGC">Mercator_1SP</gml:name>
<gml:minimumOccurs>0</gml:minimumOccurs>
- <!-- All parameters below this point are duplication of the parameters already defined
above.
- For now we do not yet use the XML references, but this is something that we will
need to
- support in a future SIS version. See https://issues.apache.org/jira/browse/SIS-173
issue.
- In the meantime, we have to change the gml:id attribute values used below for
avoiding
- collisions with the gml:id attribute used in above parameters. -->
- <gml:parameter>
- <gml:OperationParameter gml:id="epsg-8801">
- <gml:identifier codeSpace="IOGP">urn:ogc:def:parameter:EPSG::8801</gml:identifier>
- <gml:name codeSpace="EPSG">Latitude of natural origin</gml:name>
- <gml:name codeSpace="OGC">latitude_of_origin</gml:name>
- <gml:remarks>This parameter is shown for completeness, but should never have
a value different than 0 for this projection.</gml:remarks>
- </gml:OperationParameter>
- </gml:parameter>
- <gml:parameter>
- <gml:OperationParameter gml:id="epsg-8802">
- <gml:identifier codeSpace="IOGP">urn:ogc:def:parameter:EPSG::8802</gml:identifier>
- <gml:name codeSpace="EPSG">Longitude of natural origin</gml:name>
- <gml:name codeSpace="OGC">central_meridian</gml:name>
- </gml:OperationParameter>
- </gml:parameter>
- <gml:parameter>
- <gml:OperationParameter gml:id="epsg-8805">
- <gml:identifier codeSpace="IOGP">urn:ogc:def:parameter:EPSG::8805</gml:identifier>
- <gml:name codeSpace="EPSG">Scale factor at natural origin</gml:name>
- <gml:name codeSpace="OGC">scale_factor</gml:name>
- </gml:OperationParameter>
- </gml:parameter>
+ <!-- The first 3 parameters below this point are duplication of the parameters already
defined above.
+ The xlink:href attribute allows to reference existing instances instead than repeating
them.
+ See https://issues.apache.org/jira/browse/SIS-173 -->
+
+ <gml:parameter xlink:href="#epsg-parameter-8801"/>
+ <gml:parameter xlink:href="#epsg-parameter-8802"/>
+ <gml:parameter xlink:href="#epsg-parameter-8805"/>
<gml:parameter>
<gml:OperationParameter gml:id="epsg-parameter-8806">
<gml:identifier codeSpace="IOGP">urn:ogc:def:parameter:EPSG::8806</gml:identifier>
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java?rev=1707407&r1=1707406&r2=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java
[UTF-8] Wed Oct 7 22:05:28 2015
@@ -465,11 +465,25 @@ public final class Context extends Marsh
*
* @since 0.7
*/
- public static String getExistingID(final Context context, final Object object) {
+ 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 id The identifier for which to get the object.
+ * @return The object associated to the given identifier, or {@code null} if none.
+ *
+ * @since 0.7
+ */
+ 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
@@ -482,7 +496,7 @@ public final class Context extends Marsh
*
* @since 0.7
*/
- public static boolean isAvailableID(final Context context, final Object object, final
String id) {
+ public static boolean setObjectForID(final Context context, final Object object, final
String id) {
if (context != null) {
final Object existing = context.identifiers.putIfAbsent(id, object);
if (existing == null) {
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java?rev=1707407&r1=1707406&r2=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
[UTF-8] Wed Oct 7 22:05:28 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
@@ -173,7 +173,6 @@ public abstract class PropertyType<Value
* @param value The interface to wrap.
*/
protected PropertyType(final BoundType value) {
- metadata = value;
/*
* Do not invoke NilReason.forObject(metadata) in order to avoid unnecessary synchronization.
* Subclasses will use the PropertyType(BoundType, boolean) constructor instead when
a check
@@ -183,9 +182,15 @@ public abstract class PropertyType<Value
final NilReason reason = ((NilObject) value).getNilReason();
if (reason != null) {
reference = reason.toString();
- metadata = null;
+ return;
}
}
+ metadata = value; // Non-null only after we verified that not a NilObject.
+
+ @SuppressWarnings("OverridableMethodCallDuringObjectConstruction")
+ final Class<BoundType> type = getBoundType();
+ final Context context = Context.current();
+ final ReferenceResolver resolver = Context.resolver(context);
if (value instanceof IdentifiedObject) {
/*
* Get the identifiers as full UUID or XLink objects. We do not use the more
permissive methods
@@ -208,13 +213,9 @@ public abstract class PropertyType<Value
* org.apache.sis.internal.jaxb.ModifiableIdentifierMap.put(Citation, String).
*/
final IdentifierMap map = ((IdentifiedObject) value).getIdentifierMap();
- XLink link = map.getSpecialized(IdentifierSpace.XLINK);
- UUID uuid = map.getSpecialized(IdentifierSpace.UUID);
+ XLink link = map.getSpecialized(IdentifierSpace.XLINK);
+ UUID uuid = map.getSpecialized(IdentifierSpace.UUID);
if (uuid != null || link != null) {
- @SuppressWarnings("OverridableMethodCallDuringObjectConstruction")
- final Class<BoundType> 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
@@ -240,6 +241,21 @@ public abstract class PropertyType<Value
}
}
}
+ /*
+ * If the object to marshall has not been replaced by a user-specified {@code uuidref}
or {@code xlink},
+ * verify if we already marshalled that object previously with a {@code gml:id} attribute.
+ */
+ if (metadata != null) {
+ final String id = Context.getObjectID(context, metadata);
+ 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);
+ metadata = null; // Clear only after success.
+ } catch (URISyntaxException e) {
+ Context.warningOccured(context, getClass(), "<init>", e, true);
+ }
+ }
}
/**
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java?rev=1707407&r1=1707406&r2=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java
[UTF-8] Wed Oct 7 22:05:28 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.
*
- * <p>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.</p>
+ * <p>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.</p>
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.4
+ * @version 0.7
* @module
*/
public class ReferenceResolver {
@@ -90,8 +92,8 @@ 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 <T> The compile-time type of the {@code type} argument.
* @param context Context (GML version, locale, <i>etc.</i>) of the (un)marshalling
process.
@@ -108,8 +110,15 @@ 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:
+ *
+ * <ul>
+ * <li>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.</li>
+ * <li>Otherwise returns {@code null}.</li>
+ * </ul>
*
* @param <T> The compile-time type of the {@code type} argument.
* @param context Context (GML version, locale, <i>etc.</i>) of the (un)marshalling
process.
@@ -120,14 +129,21 @@ public class ReferenceResolver {
public <T> T resolve(final MarshalContext context, final Class<T> 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 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.
*
* <p>The default implementation returns {@code true} in the following cases:</p>
@@ -144,7 +160,7 @@ public class ReferenceResolver {
* @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 <T> boolean canSubstituteByReference(final MarshalContext context, final
Class<T> type, final T object, final UUID uuid) {
return (object instanceof NilObject) || (object instanceof Emptiable && ((Emptiable)
object).isEmpty());
@@ -152,9 +168,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.
*
* <p>The default implementation returns {@code true} in the following cases:</p>
@@ -172,13 +188,42 @@ public class ReferenceResolver {
* @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 <T> boolean canSubstituteByReference(final MarshalContext context, final
Class<T> type, final T object, final XLink link) {
return (object instanceof NilObject) || (object instanceof Emptiable && ((Emptiable)
object).isEmpty());
}
/**
+ * 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 all the following
+ * conditions are meet:
+ *
+ * <ul>
+ * <li>The given object has already been marshalled in the same XML document.</li>
+ * <li>The marshalled object had a {@code gml:id} attribute.</li>
+ * <li>Other {@code canSubstituteByReference(context, type, object, …)} methods
returned {@code false}.</li>
+ * </ul>
+ *
+ * The default implementation unconditionally returns {@code true}.
+ * Subclasses can override this method if they want to filter wish objects to declare
by reference.
+ *
+ * @param <T> The compile-time type of the {@code type} argument.
+ * @param context Context (GML version, locale, <i>etc.</i>) of the (un)marshalling
process.
+ * @param type The type of object to be marshalled as an <strong>interface</strong>.
+ * This is usually a <a href="http://www.geoapi.org">GeoAPI</a>
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 <T> boolean canSubstituteByReference(final MarshalContext context, final
Class<T> type, final T object, final String id) {
+ return true;
+ }
+
+ /**
* Returns the {@code <gmx:Anchor>} to use for the given text, or {@code null}
if none.
* Anchors can appear in ISO 19139 documents where we would normally expect a character
* sequence. For example:
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java?rev=1707407&r1=1707406&r2=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java [UTF-8] Wed
Oct 7 22:05:28 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}.
*
* <p>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:</p>
+ * without any concrete definition, then the default behavior is as below:</p>
+ *
+ * <ul>
+ * <li>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.</li>
+ * <li>Otherwise an empty element containing only the values of the above-cited
attributes is created.</li>
+ * </ul>
+ *
+ * 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/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java?rev=1707407&r1=1707406&r2=1707407&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/package-info.java
[UTF-8] Wed Oct 7 22:05:28 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;
|