sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1707407 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/ sis-referencing/src/test/java/org/apache/sis/parameter/ sis-referencing/src/test/resources/org/apache/sis/parameter/ sis-utility/src/main/java/...
Date Wed, 07 Oct 2015 22:05:28 GMT
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;




Mime
View raw message