sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1774297 - in /sis/branches/JDK8: core/sis-utility/src/main/java/org/apache/sis/util/ storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/ storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ storage/sis-xmlstore/src/t...
Date Wed, 14 Dec 2016 17:36:39 GMT
Author: desruisseaux
Date: Wed Dec 14 17:36:39 2016
New Revision: 1774297

URL: http://svn.apache.org/viewvc?rev=1774297&view=rev
Log:
Add JAXB annotation for metadata, but do not use them yet.
Add email type as defined by GPX specification.
Replace <keywords> string by a list.
Rename Constants as Attributes.

Added:
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Attributes.java
      - copied, changed from r1774296, sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Constants.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Email.java   (with props)
Removed:
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Constants.java
Modified:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Version.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Copyright.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXReader.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter100.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter110.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Link.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Metadata.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Person.java
    sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXReaderTest.java
    sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXWriterTest.java
    sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata100.xml
    sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata110.xml

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Version.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Version.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Version.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Version.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -96,7 +96,7 @@ public class Version implements CharSequ
     /**
      * Creates a new version object from the supplied string.
      *
-     * @param version The version as a string.
+     * @param version  the version as a string.
      */
     public Version(final String version) {
         ArgumentChecks.ensureNonNull("version", version);
@@ -114,8 +114,8 @@ public class Version implements CharSequ
      *   <li>Other elements (if any) will be appended to the {@link #toString() string value}.</li>
      * </ul>
      *
-     * @param  components The major number, optionally followed by minor, revision or other numbers.
-     * @return A new or existing instance of {@code Version} for the given numbers.
+     * @param  components  the major number, optionally followed by minor, revision or other numbers.
+     * @return a new or existing instance of {@code Version} for the given numbers.
      *
      * @since 0.4
      */
@@ -156,7 +156,7 @@ public class Version implements CharSequ
      * Returns the major version number. This method returns an {@link Integer} if possible,
      * or a {@link String} otherwise.
      *
-     * @return The major version number.
+     * @return the major version number.
      */
     public Comparable<?> getMajor() {
         return getComponent(0);
@@ -167,7 +167,7 @@ public class Version implements CharSequ
      * or a {@link String} otherwise. If there is no minor version number, then this method
      * returns {@code null}.
      *
-     * @return The minor version number, or {@code null} if none.
+     * @return the minor version number, or {@code null} if none.
      */
     public Comparable<?> getMinor() {
         return getComponent(1);
@@ -178,7 +178,7 @@ public class Version implements CharSequ
      * or a {@link String} otherwise. If there is no revision number, then this method
      * returns {@code null}.
      *
-     * @return The revision number, or {@code null} if none.
+     * @return the revision number, or {@code null} if none.
      */
     public Comparable<?> getRevision() {
         return getComponent(2);
@@ -193,8 +193,8 @@ public class Version implements CharSequ
      * or a {@link String} otherwise. If there is no component at the specified index,
      * then this method returns {@code null}.</p>
      *
-     * @param  index The index of the component to fetch.
-     * @return The value at the specified index, or {@code null} if none.
+     * @param  index  the index of the component to fetch.
+     * @return the value at the specified index, or {@code null} if none.
      * @throws IndexOutOfBoundsException if {@code index} is negative.
      */
     final synchronized Comparable<?> getComponent(final int index) {
@@ -245,10 +245,10 @@ public class Version implements CharSequ
      * The comparisons are performed as {@link Integer} object if possible, or as {@link String}
      * otherwise.
      *
-     * @param  other The other version object to compare with.
-     * @param  limit The maximum number of components to compare.
-     * @return A negative value if this version is lower than the supplied version, a positive
-     *         value if it is higher, or 0 if they are equal.
+     * @param  other  the other version object to compare with.
+     * @param  limit  the maximum number of components to compare.
+     * @return a negative value if this version is lower than the supplied version,
+     *         a positive value if it is higher, or 0 if they are equal.
      */
     public int compareTo(final Version other, final int limit) {
         ArgumentChecks.ensureNonNull ("other", other);
@@ -295,8 +295,8 @@ public class Version implements CharSequ
      * Compares this version with an other version object. This method performs the same
      * comparison than {@link #compareTo(Version, int)} with no limit.
      *
-     * @param  other The other version object to compare with.
-     * @return A negative value if this version is lower than the supplied version,
+     * @param  other  the other version object to compare with.
+     * @return a negative value if this version is lower than the supplied version,
      *         a positive value if it is higher, or 0 if they are equal.
      */
     @Override
@@ -308,7 +308,7 @@ public class Version implements CharSequ
      * Compare this version string with the specified object for equality. Two version are
      * considered equal if <code>{@linkplain #compareTo(Object) compareTo}(other) == 0</code>.
      *
-     * @param other The object to compare with this version for equality.
+     * @param other  the object to compare with this version for equality.
      */
     @Override
     public boolean equals(final Object other) {

Copied: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Attributes.java (from r1774296, sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Constants.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Attributes.java?p2=sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Attributes.java&p1=sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Constants.java&r1=1774296&r2=1774297&rev=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Constants.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Attributes.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -21,58 +21,30 @@ import org.apache.sis.util.Static;
 
 /**
  * GPX attribute names in XML files.
- *
- * @todo may be refactored.
+ * Contrarily to {@link Tags}, attributes in GPX files have no namespace.
+ * Unless otherwise noticed by a "(v1.0)" or "(v1.1)" text in the javadoc,
+ * attributes in this class apply to all supported GPX versions.
  *
  * @author  Johann Sorel (Geomatys)
  * @since   0.8
  * @version 0.8
  * @module
  */
-final class Constants extends Static {
-    /*
-     * Main GPX XML tags.
-     */
-    /** Used in versions 1.0 and 1.1. */
-    public static final String ATT_GPX_VERSION = "version";
-    /** Used in versions 1.0 and 1.1. */
-    public static final String ATT_GPX_CREATOR = "creator";
-
-    /*
-     * Copyright tags.
-     */
-    /** Used in version 1.1. */
-    public static final String ATT_COPYRIGHT_AUTHOR = "author";
-
-    /*
-     * Bounds tags.
-     */
-    /** Used in versions 1.0 and 1.1. */
-    public static final String ATT_BOUNDS_MINLAT = "minlat";
-    /** Used in versions 1.0 and 1.1. */
-    public static final String ATT_BOUNDS_MINLON = "minlon";
-    /** Used in versions 1.0 and 1.1. */
-    public static final String ATT_BOUNDS_MAXLAT = "maxlat";
-    /** Used in versions 1.0 and 1.1. */
-    public static final String ATT_BOUNDS_MAXLON = "maxlon";
-
-    /*
-     * Link tags.
-     */
-    /** Used in version 1.1. */
-    public static final String ATT_LINK_HREF = "href";
-
-    /*
-     * WPT tags.
-     */
-    /** Used in versions 1.0 and 1.1. */
-    public static final String ATT_WPT_LAT = "lat";
-    /** Used in versions 1.0 and 1.1. */
-    public static final String ATT_WPT_LON = "lon";
+final class Attributes extends Static {
+    /** A main GPX attribute.           */ static final String VERSION   = "version";
+    /** A main GPX attribute.           */ static final String CREATOR   = "creator";
+    /** A copyright attribute (v1.1+).  */ static final String AUTHOR    = "author";
+    /** A bounds attribute.             */ static final String MIN_X     = "minlon";
+    /** A bounds attribute.             */ static final String MAX_X     = "maxlon";
+    /** A bounds attribute.             */ static final String MIN_Y     = "minlat";
+    /** A bounds attribute.             */ static final String MAX_Y     = "maxlat";
+    /** A link attribute (v1.1+).       */ static final String HREF      = "href";
+    /** A way point attribute.          */ static final String LATITUDE  = "lat";
+    /** A way point attribute.          */ static final String LONGITUDE = "lon";
 
     /**
      * Do not allow instantiation of this class.
      */
-    private Constants() {
+    private Attributes() {
     }
 }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Copyright.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Copyright.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Copyright.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Copyright.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -16,11 +16,14 @@
  */
 package org.apache.sis.internal.gpx;
 
+import java.net.URI;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Objects;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
 import org.apache.sis.util.iso.SimpleInternationalString;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
@@ -72,6 +75,7 @@ public final class Copyright implements
      * @see #getParties()
      * @see #getName()
      */
+    @XmlAttribute(name = Attributes.AUTHOR, required = true)
     public String author;
 
     /**
@@ -80,6 +84,7 @@ public final class Copyright implements
      *
      * @see #getDate()
      */
+    @XmlElement(name = Tags.YEAR)
     public Integer year;
 
     /**
@@ -88,7 +93,8 @@ public final class Copyright implements
      *
      * @see #getOnlineResources()
      */
-    public OnlineResource license;
+    @XmlElement(name = Tags.LICENSE)
+    public URI license;
 
     /**
      * Creates an initially empty instance.
@@ -464,7 +470,7 @@ public final class Copyright implements
      */
     @Override
     public Collection<OnlineResource> getOnlineResources() {
-        return (license != null) ? Collections.singleton(license) : Collections.emptySet();
+        return (license != null) ? Collections.singleton(new Link(license)) : Collections.emptySet();
     }
 
     /**
@@ -517,8 +523,8 @@ public final class Copyright implements
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("Copyright");
-        if (year   != null) sb.append(' ').append(year);
-        if (author != null) sb.append(' ').append(author);
+        if (year    != null) sb.append(' ').append(year);
+        if (author  != null) sb.append(' ').append(author);
         if (license != null) {
             sb.append(System.lineSeparator()).append(license);
         }

Added: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Email.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Email.java?rev=1774297&view=auto
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Email.java (added)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Email.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.gpx;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+
+/**
+ * An email address broken into two parts (id and domain) to help prevent email harvesting.
+ * This class also implements its own converter for getting the {@link String} representation
+ * at unmarshalling time.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+final class Email extends XmlAdapter<Email, String> {
+    /**
+     * The half before {@code @} in email address (for example "john.smith").
+     */
+    @XmlAttribute
+    String id;
+
+    /**
+     * The half after {@code @} in email address (for example "hotmail.com").
+     */
+    @XmlAttribute
+    String domain;
+
+    /**
+     * Invoked by JAXB at (un)marshalling time.
+     */
+    public Email() {
+    }
+
+    /**
+     * Returns the complete email address.
+     */
+    @Override
+    public String toString() {
+        return id + '@' + domain;
+    }
+
+    /**
+     * Invoked at reading time for creating the full email address from its components.
+     */
+    @Override
+    public String unmarshal(final Email address) {
+        return address.toString();
+    }
+
+    /**
+     * Invoked at writing time for splitting an email address into two components.
+     */
+    @Override
+    public Email marshal(final String address) {
+        final Email r = new Email();
+        final int s = address.indexOf('@');
+        r.id = address.substring(0, Math.max(0, s));
+        r.domain = address.substring(s+1);
+        return r;
+    }
+}

Propchange: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Email.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Email.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXReader.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXReader.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXReader.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXReader.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -17,9 +17,11 @@
 package org.apache.sis.internal.gpx;
 
 import java.util.List;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.io.IOException;
 import java.io.EOFException;
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -68,6 +70,18 @@ public class GPXReader extends StaxStrea
     private final Types types;
 
     /**
+     * Version of the GPX file, or {@code null} if unspecified.
+     * Can be {@link GPXStore#V1_0} or {@link GPXStore#V1_1}.
+     */
+    private Version version;
+
+    /**
+     * Convenience flag set to {@code true} if the {@link #version} field is {@link GPXStore#V1_0},
+     * or {@code false} if the version is {@link GPXStore#V1_1}.
+     */
+    private boolean isLegacy;
+
+    /**
      * The metadata (ISO 19115 compatible), or {@code null} if none.
      */
     private Metadata metadata;
@@ -78,66 +92,80 @@ public class GPXReader extends StaxStrea
      */
     private Feature current;
 
-    private int wayPointInc = 0;
-    private int routeInc = 0;
-    private int trackInc = 0;
-    private Version version;
-    private boolean isRevision;
-    private String baseNamespace = Tags.NAMESPACE_V11;
+    /**
+     * Identifier of the last "way point" feature instance created.
+     * We use sequential numbers starting from 1.
+     */
+    private int wayPointId;
 
     /**
-     * {@inheritDoc }
+     * Identifier of the last "route" feature instance created.
+     * We use sequential numbers starting from 1.
+     */
+    private int routeId;
+
+    /**
+     * Identifier of the last "track" feature instance created.
+     * We use sequential numbers starting from 1.
+     */
+    private int trackId;
+
+    /**
+     * Creates a new GPX reader from the given file, URL, stream or reader object.
+     * See {@linkplain StaxStreamReader#StaxStreamReader super-class constructor}
+     * for more information about constructor arguments.
      *
-     * @param input input object
-     * @throws XMLStreamException if input is not a valid XML stream
+     * @param  owner      the data store for which this reader is created.
+     * @param  input      value of {@code storage.getStorage()}.
+     * @param  connector  information about the storage (URL, stream, <i>etc</i>), or {@code null} if unknown.
+     * @throws DataStoreException if the input type is not recognized.
+     * @throws XMLStreamException if an error occurred while opening the XML file.
+     * @throws EOFException if the file seems to be truncated.
      */
-    public GPXReader(final GPXStore owner, final Object input, final StorageConnector storage)
+    public GPXReader(final GPXStore owner, final Object input, final StorageConnector connector)
             throws DataStoreException, XMLStreamException, EOFException
     {
-        super(owner, input, storage);
+        super(owner, input, connector);
         types = Types.DEFAULT;
+        /*
+         * Read metadata immediately. We are especially interrested in the "bounds" tag,
+         * for creating the envelope.
+         */
         final XMLStreamReader reader = getReader();
-
-        // Search for the bound tag to generate the envelope
-searchLoop:
-        while(reader.hasNext()) {
+readMD: while (reader.hasNext()) {
             switch (reader.next()) {
                 case START_ELEMENT: {
-                    final String typeName = reader.getLocalName();
-                    if (Tags.GPX.equalsIgnoreCase(typeName)) {
-
-                        String str = "1.1";     // Consider 1.1 by default
-                        for (int i=0,n=reader.getAttributeCount(); i<n;i++) {
-                            if (Constants.ATT_GPX_VERSION.equalsIgnoreCase(reader.getAttributeLocalName(i))) {
-                                str = reader.getAttributeValue(i);
+                    if (isGPX(reader)) {
+                        switch (reader.getLocalName()) {
+                            case Tags.GPX: {
+                                String str = "1.1";     // Consider 1.1 by default
+                                final int n=reader.getAttributeCount();
+                                for (int i=0; i<n; i++) {
+                                    if (Attributes.VERSION.equals(reader.getAttributeLocalName(i))) {
+                                        str = reader.getAttributeValue(i);
+                                    }
+                                }
+                                version = new Version(str);
+                                isLegacy = GPXStore.V1_0.equals(version);
+                                if (isLegacy) {
+                                    // we wont found a metadata tag, must read the tags here.
+                                    metadata = parseMetadata100();
+                                    break readMD;
+                                } else if (!GPXStore.V1_1.equals(version)) {
+                                    throw new DataStoreException("Unsupported version: " + version);
+                                }
+                                break;
+                            }
+                            case Tags.METADATA: {
+                                metadata = parseMetadata110();
+                                break readMD;
+                            }
+                            case Tags.WAY_POINT:
+                            case Tags.TRACKS:
+                            case Tags.ROUTES: {
+                                break readMD;
                             }
                         }
-
-                        try {
-                            this.version = new Version(str);
-                        } catch (NumberFormatException ex) {
-                            throw new XMLStreamException(ex);
-                        }
-                        isRevision = GPXStore.V1_1.equals(version);
-                        if (isRevision) {
-                            baseNamespace = Tags.NAMESPACE_V11;
-                        } else if (GPXStore.V1_0.equals(version)) {
-                            baseNamespace = Tags.NAMESPACE_V10;
-                            //we wont found a metadata tag, must read the tags here.
-                            metadata = parseMetadata100();
-                            break searchLoop;
-                        } else {
-                            throw new DataStoreException("Unsupported version: " + version);
-                        }
-
-                    } else if (Tags.METADATA.equalsIgnoreCase(typeName)) {
-                        metadata = parseMetadata110();
-                        break searchLoop;
-                    } else if (Tags.WAY_POINT.equalsIgnoreCase(typeName)
-                            || Tags.TRACKS.equalsIgnoreCase(typeName)
-                            || Tags.ROUTES.equalsIgnoreCase(typeName)) {
-                        //there is no metadata tag
-                        break searchLoop;
                     }
                 }
             }
@@ -145,6 +173,14 @@ searchLoop:
     }
 
     /**
+     * Returns {@code true} if the current element of the given reader is in the GPX namespace or has no namespace.
+     */
+    private static boolean isGPX(final XMLStreamReader reader) {
+        final String ns = reader.getNamespaceURI();
+        return (ns == null) || ns.startsWith(Tags.NAMESPACE);
+    }
+
+    /**
      * Get GPX file version.
      * This method will return a result only if called only after the input has been set.
      *
@@ -172,9 +208,9 @@ searchLoop:
         super.close();
         metadata = null;
         current = null;
-        wayPointInc = 0;
-        routeInc = 0;
-        trackInc = 0;
+        wayPointId = 0;
+        routeId = 0;
+        trackId = 0;
     }
 
     /**
@@ -223,13 +259,13 @@ searchLoop:
             if (type == START_ELEMENT) {
                 final String localName = reader.getLocalName();
                 if (Tags.WAY_POINT.equalsIgnoreCase(localName)) {
-                    current = parseWayPoint(wayPointInc++);
+                    current = parseWayPoint(++wayPointId);
                     break;
                 } else if (Tags.ROUTES.equalsIgnoreCase(localName)) {
-                    current = parseRoute(routeInc++);
+                    current = parseRoute(++routeId);
                     break;
                 } else if (Tags.TRACKS.equalsIgnoreCase(localName)) {
-                    current = parseTrack(trackInc++);
+                    current = parseTrack(++trackId);
                     break;
                 }
             }
@@ -243,43 +279,43 @@ searchLoop:
     private Metadata parseMetadata100() throws XMLStreamException, EOFException {
         final XMLStreamReader reader = getReader();
         final Metadata metadata = new Metadata();
-
-searchLoop:
-        while (reader.hasNext()) {
+readMD: while (reader.hasNext()) {
             switch (reader.next()) {
                 case START_ELEMENT: {
-                    final String localName = reader.getLocalName();
-                    if (Tags.NAME.equalsIgnoreCase(localName)) {
-                        metadata.name = reader.getElementText();
-                    } else if (Tags.DESCRIPTION.equalsIgnoreCase(localName)) {
-                        metadata.description = reader.getElementText();
-                    } else if (Tags.AUTHOR.equalsIgnoreCase(localName)) {
-                        if (metadata.author == null) metadata.author = new Person();
-                        metadata.author.name = reader.getElementText();
-                    } else if (Tags.EMAIL.equalsIgnoreCase(localName)) {
-                        if (metadata.author == null) metadata.author = new Person();
-                        metadata.author.email = reader.getElementText();
-                    } else if (Tags.URL.equalsIgnoreCase(localName)) {
-                        try {
-                            metadata.links.add(new Link(reader.getElementText()));
-                        } catch (URISyntaxException ex) {
-                            throw new XMLStreamException(ex);
+                    switch (reader.getLocalName()) {
+                        case Tags.NAME:        metadata.name        = reader.getElementText(); break;
+                        case Tags.DESCRIPTION: metadata.description = reader.getElementText(); break;
+                        case Tags.AUTHOR: {
+                            if (metadata.author == null) metadata.author = new Person();
+                            metadata.author.name = reader.getElementText();
+                            break;
                         }
-                    } else if (Tags.URL_NAME.equalsIgnoreCase(localName)) {
-                        //reader.getElementText();
-                    } else if (Tags.TIME.equalsIgnoreCase(localName)) {
-                        metadata.time = parseTime(reader.getElementText());
-                    } else if (Tags.KEYWORDS.equalsIgnoreCase(localName)) {
-                        metadata.keywords = reader.getElementText();
-                    } else if (Tags.BOUNDS.equalsIgnoreCase(localName)) {
-                        metadata.bounds = parseBound();
-                    } else if (Tags.WAY_POINT.equalsIgnoreCase(localName)
-                            || Tags.TRACKS.equalsIgnoreCase(localName)
-                            || Tags.ROUTES.equalsIgnoreCase(localName)) {
-                        //there is no more metadata tags
-                        break searchLoop;
+                        case Tags.EMAIL: {
+                            if (metadata.author == null) metadata.author = new Person();
+                            metadata.author.email = reader.getElementText();
+                            break;
+                        }
+                        case Tags.URL: {
+                            try {
+                                metadata.links.add(new Link(new URI(reader.getElementText())));
+                            } catch (URISyntaxException ex) {
+                                throw new XMLStreamException(ex);
+                            }
+                            break;
+                        }
+                        case Tags.URL_NAME: {
+                            //reader.getElementText();
+                            break;
+                        }
+                        case Tags.TIME:     metadata.time     = parseTime(reader.getElementText()); break;
+                        case Tags.KEYWORDS: metadata.keywords = Arrays.asList(reader.getElementText().split(" ")); break;
+                        case Tags.BOUNDS:   metadata.bounds   = parseBound(); break;
+                        case Tags.WAY_POINT:
+                        case Tags.TRACKS:
+                        case Tags.ROUTES:
+                            //there is no more metadata tags
+                            break readMD;
                     }
-                    break;
                 }
             }
         }
@@ -293,34 +329,26 @@ searchLoop:
     private Metadata parseMetadata110() throws XMLStreamException, EOFException {
         final XMLStreamReader reader = getReader();
         final Metadata metadata = new Metadata();
-
         while (reader.hasNext()) {
             switch (reader.next()) {
                 case START_ELEMENT: {
-                    final String localName = reader.getLocalName();
-                    if (Tags.NAME.equalsIgnoreCase(localName)) {
-                        metadata.name = reader.getElementText();
-                    } else if (Tags.DESCRIPTION.equalsIgnoreCase(localName)) {
-                        metadata.description = reader.getElementText();
-                    } else if (Tags.AUTHOR.equalsIgnoreCase(localName)) {
-                        metadata.author = parsePerson();
-                    } else if (Tags.COPYRIGHT.equalsIgnoreCase(localName)) {
-                        metadata.copyright = parseCopyright();
-                    } else if (Tags.LINK.equalsIgnoreCase(localName)) {
-                        metadata.links.add(parseLink());
-                    } else if (Tags.TIME.equalsIgnoreCase(localName)) {
-                        metadata.time = parseTime(reader.getElementText());
-                    } else if (Tags.KEYWORDS.equalsIgnoreCase(localName)) {
-                        metadata.keywords = reader.getElementText();
-                    } else if (Tags.BOUNDS.equalsIgnoreCase(localName)) {
-                        metadata.bounds = parseBound();
+                    switch (reader.getLocalName()) {
+                        case Tags.NAME: metadata.name = reader.getElementText(); break;
+                        case Tags.DESCRIPTION: metadata.description = reader.getElementText(); break;
+                        case Tags.AUTHOR: metadata.author = parsePerson(); break;
+                        case Tags.COPYRIGHT: metadata.copyright = parseCopyright(); break;
+                        case Tags.LINK: metadata.links.add(parseLink()); break;
+                        case Tags.TIME: metadata.time = parseTime(reader.getElementText()); break;
+                        case Tags.KEYWORDS: metadata.keywords = Arrays.asList(reader.getElementText().split(" ")); break;
+                        case Tags.BOUNDS: metadata.bounds = parseBound(); break;
                     }
                     break;
                 }
                 case END_ELEMENT: {
-                    if (Tags.METADATA.equalsIgnoreCase(reader.getLocalName())) {
-                        // End of the metadata element
-                        return metadata;
+                    switch (reader.getLocalName()) {
+                        case Tags.METADATA:
+                            // End of the metadata element
+                            return metadata;
                     }
                     break;
                 }
@@ -336,7 +364,7 @@ searchLoop:
     private Copyright parseCopyright() throws XMLStreamException {
         final XMLStreamReader reader = getReader();
         final Copyright copyright = new Copyright();
-        copyright.author = reader.getAttributeValue(null, Constants.ATT_COPYRIGHT_AUTHOR);
+        copyright.author = reader.getAttributeValue(null, Attributes.AUTHOR);
 
         while (reader.hasNext()) {
             switch (reader.next()) {
@@ -346,7 +374,7 @@ searchLoop:
                         copyright.year = Integer.valueOf(reader.getElementText());
                     } else if (Tags.LICENSE.equalsIgnoreCase(localName)) {
                         try {
-                            copyright.license = new Link(reader.getElementText());
+                            copyright.license = new URI(reader.getElementText());
                         } catch (URISyntaxException ex) {
                             throw new XMLStreamException(ex);
                         }
@@ -370,7 +398,7 @@ searchLoop:
      */
     private Link parseLink() throws XMLStreamException {
         final XMLStreamReader reader = getReader();
-        String text = reader.getAttributeValue(null, Constants.ATT_LINK_HREF);
+        String text = reader.getAttributeValue(null, Attributes.HREF);
         String mime = null;
 
         while (reader.hasNext()) {
@@ -388,7 +416,7 @@ searchLoop:
                     if (Tags.LINK.equalsIgnoreCase(reader.getLocalName())) {
                         try {
                             // End of the link element
-                            return new Link(text);
+                            return new Link(new URI(text));
                         } catch (URISyntaxException ex) {
                             throw new XMLStreamException(ex);
                         }
@@ -439,10 +467,10 @@ searchLoop:
      */
     private GeographicBoundingBox parseBound() throws XMLStreamException, EOFException {
         final XMLStreamReader reader = getReader();
-        final String xmin = reader.getAttributeValue(null, Constants.ATT_BOUNDS_MINLON);
-        final String xmax = reader.getAttributeValue(null, Constants.ATT_BOUNDS_MAXLON);
-        final String ymin = reader.getAttributeValue(null, Constants.ATT_BOUNDS_MINLAT);
-        final String ymax = reader.getAttributeValue(null, Constants.ATT_BOUNDS_MAXLAT);
+        final String xmin = reader.getAttributeValue(null, Attributes.MIN_X);
+        final String xmax = reader.getAttributeValue(null, Attributes.MAX_X);
+        final String ymin = reader.getAttributeValue(null, Attributes.MIN_Y);
+        final String ymax = reader.getAttributeValue(null, Attributes.MAX_Y);
 
         if (xmin == null || xmax == null || ymin == null || ymax == null) {
             throw new XMLStreamException("Error in xml file, metadata bounds not defined correctly");
@@ -472,8 +500,8 @@ searchLoop:
 
         List<Link> links = null;
 
-        final String lat = reader.getAttributeValue(null, Constants.ATT_WPT_LAT);
-        final String lon = reader.getAttributeValue(null, Constants.ATT_WPT_LON);
+        final String lat = reader.getAttributeValue(null, Attributes.LATITUDE);
+        final String lon = reader.getAttributeValue(null, Attributes.LONGITUDE);
 
         if (lat == null || lon == null) {
             throw new XMLStreamException("Error in xml file, way point lat/lon not defined correctly");
@@ -522,11 +550,11 @@ searchLoop:
                         feature.setPropertyValue(Tags.AGE_OF_GPS_DATA, Double.valueOf(reader.getElementText()));
                     } else if (Tags.DGPS_ID.equalsIgnoreCase(localName)) {
                         feature.setPropertyValue(Tags.DGPS_ID, Integer.valueOf(reader.getElementText()));
-                    } else if (!isRevision && Tags.URL.equalsIgnoreCase(localName)) {
+                    } else if (isLegacy && Tags.URL.equalsIgnoreCase(localName)) {
                         // GPX 1.0 only
                         if (links == null) links = new ArrayList<>();
                         try {
-                            links.add(new Link(reader.getElementText()));
+                            links.add(new Link(new URI(reader.getElementText())));
                         } catch (URISyntaxException ex) {
                             throw new XMLStreamException(ex);
                         }
@@ -565,7 +593,7 @@ searchLoop:
                     final String localName = reader.getLocalName();
                     if (Tags.ROUTE_POINTS.equalsIgnoreCase(localName)) {
                         if (wayPoints == null) wayPoints = new ArrayList<>();
-                        wayPoints.add(parseWayPoint(ptInc++));
+                        wayPoints.add(parseWayPoint(++ptInc));
                     } else if (Tags.NAME.equalsIgnoreCase(localName)) {
                         feature.setPropertyValue(Tags.NAME, reader.getElementText());
                     } else if (Tags.COMMENT.equalsIgnoreCase(localName)) {
@@ -581,11 +609,11 @@ searchLoop:
                         feature.setPropertyValue(Tags.NUMBER, Integer.valueOf(reader.getElementText()));
                     } else if (Tags.TYPE.equalsIgnoreCase(localName)) {
                         feature.setPropertyValue(Tags.TYPE, reader.getElementText());
-                    } else if (!isRevision && Tags.URL.equalsIgnoreCase(localName)) {
+                    } else if (isLegacy && Tags.URL.equalsIgnoreCase(localName)) {
                         //GPX 1.0 only
                         if (links == null) links = new ArrayList<>();
                         try {
-                            links.add(new Link(reader.getElementText()));
+                            links.add(new Link(new URI(reader.getElementText())));
                         } catch (URISyntaxException ex) {
                             throw new XMLStreamException(ex);
                         }
@@ -623,7 +651,7 @@ searchLoop:
                     final String localName = reader.getLocalName();
                     if (Tags.TRACK_POINTS.equalsIgnoreCase(localName)) {
                         if (wayPoints == null) wayPoints = new ArrayList<>();
-                        wayPoints.add(parseWayPoint(ptInc++));
+                        wayPoints.add(parseWayPoint(++ptInc));
                     }
                     break;
                 }
@@ -674,11 +702,11 @@ searchLoop:
                         feature.setPropertyValue(Tags.NUMBER, Integer.valueOf(reader.getElementText()));
                     } else if (Tags.TYPE.equalsIgnoreCase(localName)) {
                         feature.setPropertyValue(Tags.TYPE, reader.getElementText());
-                    } else if (!isRevision && Tags.URL.equalsIgnoreCase(localName)) {
+                    } else if (isLegacy && Tags.URL.equalsIgnoreCase(localName)) {
                         // GPX 1.0 only
                         if (links == null) links = new ArrayList<>();
                         try {
-                            links.add(new Link(reader.getElementText()));
+                            links.add(new Link(new URI(reader.getElementText())));
                         } catch (URISyntaxException ex) {
                             throw new XMLStreamException(ex);
                         }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter100.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter100.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter100.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter100.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -27,6 +27,8 @@ import java.time.format.DateTimeFormatte
 import java.time.temporal.Temporal;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import org.opengis.metadata.extent.GeographicBoundingBox;
@@ -119,8 +121,8 @@ public class GPXWriter100 extends StaxSt
         final XMLStreamWriter writer = getWriter();
         writer.setDefaultNamespace(namespace);
         writer.writeStartElement(namespace, Tags.GPX);
-        writer.writeAttribute(Constants.ATT_GPX_VERSION, getVersion());
-        writer.writeAttribute(Constants.ATT_GPX_CREATOR, creator);
+        writer.writeAttribute(Attributes.VERSION, getVersion());
+        writer.writeAttribute(Attributes.CREATOR, creator);
         writer.writeDefaultNamespace(namespace);
         writer.flush();
     }
@@ -229,12 +231,17 @@ public class GPXWriter100 extends StaxSt
             writeSimpleTag(namespace, Tags.TIME, toString(metadata.time));
         }
 
-        writeSimpleTag(namespace, Tags.KEYWORDS, metadata.keywords);
+        writeSimpleTag(namespace, Tags.KEYWORDS, toSpaceSeparatedList(metadata.keywords));
         writeBounds(metadata.bounds);
         final XMLStreamWriter writer = getWriter();
         writer.flush();
     }
 
+    static String toSpaceSeparatedList(final List<String> keywords) {
+        if (keywords == null) return null;
+        return keywords.stream().collect(Collectors.joining(" "));
+    }
+
     /**
      * Write a way point.
      *
@@ -248,8 +255,8 @@ public class GPXWriter100 extends StaxSt
         writer.writeStartElement(namespace, tagName);
 
         final Point pt = (Point) feature.getProperty("@geometry").getValue();
-        writer.writeAttribute(Constants.ATT_WPT_LAT, Double.toString(pt.getY()));
-        writer.writeAttribute(Constants.ATT_WPT_LON, Double.toString(pt.getX()));
+        writer.writeAttribute(Attributes.LATITUDE, Double.toString(pt.getY()));
+        writer.writeAttribute(Attributes.LONGITUDE, Double.toString(pt.getX()));
 
         writeProperty(Tags.ELEVATION,       feature.getProperty(Tags.ELEVATION));
         writeProperty(Tags.TIME,            feature.getProperty(Tags.TIME));
@@ -369,7 +376,7 @@ public class GPXWriter100 extends StaxSt
 
         final XMLStreamWriter writer = getWriter();
         writer.writeStartElement(namespace, Tags.LINK);
-        writer.writeAttribute(Constants.ATT_LINK_HREF, link.uri.toASCIIString());
+        writer.writeAttribute(Attributes.HREF, link.uri.toASCIIString());
         writer.writeEndElement();
     }
 
@@ -385,10 +392,10 @@ public class GPXWriter100 extends StaxSt
         final XMLStreamWriter writer = getWriter();
         writer.writeStartElement(namespace, Tags.BOUNDS);
 
-        writer.writeAttribute(Constants.ATT_BOUNDS_MINLAT, Double.toString(env.getSouthBoundLatitude()));
-        writer.writeAttribute(Constants.ATT_BOUNDS_MINLON, Double.toString(env.getWestBoundLongitude()));
-        writer.writeAttribute(Constants.ATT_BOUNDS_MAXLAT, Double.toString(env.getNorthBoundLatitude()));
-        writer.writeAttribute(Constants.ATT_BOUNDS_MAXLON, Double.toString(env.getEastBoundLongitude()));
+        writer.writeAttribute(Attributes.MIN_Y, Double.toString(env.getSouthBoundLatitude()));
+        writer.writeAttribute(Attributes.MIN_X, Double.toString(env.getWestBoundLongitude()));
+        writer.writeAttribute(Attributes.MAX_Y, Double.toString(env.getNorthBoundLatitude()));
+        writer.writeAttribute(Attributes.MAX_X, Double.toString(env.getEastBoundLongitude()));
 
         writer.writeEndElement();
     }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter110.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter110.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter110.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter110.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -75,7 +75,7 @@ public class GPXWriter110 extends GPXWri
             writeSimpleTag(namespace, Tags.TIME, toString(d));
         }
 
-        writeSimpleTag(namespace, Tags.KEYWORDS, metadata.keywords);
+        writeSimpleTag(namespace, Tags.KEYWORDS, toSpaceSeparatedList(metadata.keywords));
         writeBounds(metadata.bounds);
 
         writer.writeEndElement();
@@ -126,7 +126,7 @@ public class GPXWriter110 extends GPXWri
 
         final XMLStreamWriter writer = getWriter();
         writer.writeStartElement(namespace, Tags.LINK);
-        writer.writeAttribute(Constants.ATT_LINK_HREF, link.uri.toASCIIString());
+        writer.writeAttribute(Attributes.HREF, link.uri.toASCIIString());
         writer.writeEndElement();
     }
 
@@ -143,7 +143,7 @@ public class GPXWriter110 extends GPXWri
         writer.writeStartElement(namespace, Tags.COPYRIGHT);
         final String author = copyRight.author;
         if (author != null) {
-            writer.writeAttribute(Constants.ATT_COPYRIGHT_AUTHOR, author);
+            writer.writeAttribute(Attributes.AUTHOR, author);
         }
         writeSimpleTag(namespace, Tags.YEAR, copyRight.year);
         writeSimpleTag(namespace, Tags.LICENSE, copyRight.license);

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Link.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Link.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Link.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Link.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -19,17 +19,24 @@ package org.apache.sis.internal.gpx;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Objects;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.OnLineFunction;
 import org.opengis.metadata.citation.OnlineResource;
+import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.internal.jaxb.Context;
 
 
 /**
- * Link to a resource in a GPX file.
+ * A link to an external resource (Web page, digital photo, video clip, <i>etc</i>) with additional information.
  * This element provides 3 properties:
  *
  * <ul>
  *   <li>The {@linkplain #uri}, which is the only mandatory property.</li>
+ *   <li>The {@linkplain #text} to show for the link.</li>
+ *   <li>The MIME {@linkplain #type}.</li>
  * </ul>
  *
  * Those properties can be read or modified directly. All methods defined in this class are bridges to
@@ -43,11 +50,28 @@ import org.opengis.metadata.citation.Onl
  */
 public final class Link implements OnlineResource {
     /**
-     * The link value.
+     * URL of hyper-link.
+     *
+     * @see #getLinkage()
      */
+    @XmlAttribute(name = Attributes.HREF, required = true)
     public URI uri;
 
     /**
+     * Text of hyper-link.
+     *
+     * @see #getName()
+     */
+    @XmlElement(name = Tags.TEXT)
+    public String text;
+
+    /**
+     * MIME type of content (for example "image/jpeg").
+     */
+    @XmlElement(name = Tags.TYPE)
+    public String type;
+
+    /**
      * Creates an initially empty instance.
      * Callers should set at least the {@link #uri} field after construction.
      */
@@ -58,16 +82,45 @@ public final class Link implements Onlin
      * Creates a new instance initialized to the given URI.
      *
      * @param  uri  the URI.
-     * @throws URISyntaxException if the given URI is invalid.
      */
-    public Link(final String uri) throws URISyntaxException {
-        this.uri = new URI(uri);
+    public Link(final URI uri) {
+        this.uri = uri;
     }
 
     /**
-     * Returns the link value.
+     * Invoked by JAXB after unmarshalling. If the {@linkplain #uri} is not set but the {@link #text} looks
+     * like a URI, uses that text. The intend is to handle link that should have been defined like below:
+     *
+     * {@preformat xml
+     *   <link href="http://some.site.org">
+     *   </link>
+     * }
+     *
+     * but instead has erroneously been defined like below:
      *
-     * @return {@link #uri}.
+     * {@preformat xml
+     *   <link>
+     *     <text>http://some.site.org</text>
+     *   </link>
+     * }
+     *
+     * If we fail to convert the text to an URI, we will leave the object state as-is.
+     */
+    final void afterUnmarshal(Unmarshaller um, Object parent) {
+        if (uri == null && text != null) {
+            final Context context = Context.current();
+            try {
+                Context.converter(context).toURI(context, text);
+            } catch (URISyntaxException e) {
+                Context.warningOccured(context, Link.class, "afterUnmarshal", e, true);
+            }
+        }
+    }
+
+    /**
+     * ISO 19115 metadata property determined by the {@link #uri} field.
+     *
+     * @return location for on-line access using a URL address or similar scheme.
      */
     @Override
     public URI getLinkage() {
@@ -95,13 +148,13 @@ public final class Link implements Onlin
     }
 
     /**
-     * ISO 19115 metadata property not specified by GPX.
+     * ISO 19115 metadata property determined by the {@link #text} field.
      *
      * @return name of the online resource.
      */
     @Override
     public InternationalString getName() {
-        return null;
+        return (text != null) ? new SimpleInternationalString(text) : null;
     }
 
     /**
@@ -144,7 +197,9 @@ public final class Link implements Onlin
     public boolean equals(Object obj) {
         if (obj instanceof Link) {
             final Link that = (Link) obj;
-            return Objects.equals(this.uri, that.uri);
+            return Objects.equals(this.uri,  that.uri)  &&
+                   Objects.equals(this.text, that.text) &&
+                   Objects.equals(this.type, that.type);
         }
         return false;
     }
@@ -156,7 +211,7 @@ public final class Link implements Onlin
      */
     @Override
     public int hashCode() {
-        return Objects.hashCode(uri);
+        return Objects.hash(uri, text, type);
     }
 
     /**

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Metadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Metadata.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Metadata.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Metadata.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -18,6 +18,7 @@ package org.apache.sis.internal.gpx;
 
 import java.time.Instant;
 import java.time.temporal.Temporal;
+import java.util.AbstractList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -26,6 +27,7 @@ import java.util.List;
 import java.util.Objects;
 import java.io.IOException;
 import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlList;
 
 import org.opengis.metadata.citation.CitationDate;
 import org.opengis.metadata.citation.DateType;
@@ -76,50 +78,67 @@ import org.apache.sis.util.iso.SimpleInt
 public final class Metadata extends SimpleMetadata {
     /**
      * The name of the GPX file.
+     *
+     * @see #getTitle()
      */
     @XmlElement(name = Tags.NAME)
     public String name;
 
     /**
      * A description of the contents of the GPX file.
+     *
+     * @see #getAbstract()
      */
     @XmlElement(name = Tags.DESCRIPTION)
     public String description;
 
     /**
      * The person or organization who created the GPX file.
+     *
+     * @see #getPointOfContacts()
      */
     @XmlElement(name = Tags.AUTHOR)
     public Person author;
 
     /**
      * Copyright and license information governing use of the file.
+     *
+     * @see #getResourceConstraints()
      */
     @XmlElement(name = Tags.COPYRIGHT)
     public Copyright copyright;
 
     /**
      * URLs associated with the location described in the file.
+     *
+     * @see #getOnlineResources()
      */
     @XmlElement(name = Tags.LINK)
     public final List<Link> links = new ArrayList<>();
 
     /**
      * The creation date of the file.
+     *
+     * @see #getDates()
      */
-    @XmlElement
+    @XmlElement(name = Tags.TIME)
     public Temporal time;
 
     /**
-     * Keywords associated with the file.
+     * Keywords associated with the file, or {@code null} if unspecified.
      * Search engines or databases can use this information to classify the data.
+     *
+     * @see #getDescriptiveKeywords()
      */
-    @XmlElement
-    public String keywords;
+    @XmlList
+    @XmlElement(name = Tags.KEYWORDS)
+    public List<String> keywords;
 
     /**
      * Minimum and maximum coordinates which describe the extent of the coordinates in the file.
      * The GPX 1.1 specification restricts the coordinate reference system to WGS84.
+     *
+     * @see #getExtents()
      */
     @XmlElement
     public GeographicBoundingBox bounds;
@@ -160,7 +179,22 @@ public final class Metadata extends Simp
      */
     @Override
     public Collection<Keywords> getDescriptiveKeywords() {
-        return (keywords != null) ? Collections.singleton(new DefaultKeywords(keywords)) : super.getDescriptiveKeywords();
+        if (keywords != null) {
+            return new KW(keywords);
+        }
+        return super.getDescriptiveKeywords();
+    }
+
+    /**
+     * The list to be returned by {@link #getDescriptiveKeywords()}.
+     * Each keywords is created when first needed.
+     */
+    private static final class KW extends AbstractList<Keywords> {
+        private final List<String> keywords;
+
+        KW(final List<String> keywords)      {this.keywords = keywords;}
+        @Override public int      size()     {return keywords.size();}
+        @Override public Keywords get(int i) {return new DefaultKeywords(keywords.get(i));}
     }
 
     /**
@@ -216,22 +250,14 @@ public final class Metadata extends Simp
     }
 
     /**
-     * ISO 19115 metadata property determined by the {@link #bounds} field.
+     * ISO 19115 metadata property determined by the {@link #links} field.
      * This is part of the information returned by {@link #getCitation()}.
      *
      * @return online references to the cited resource.
      */
     @Override
     public Collection<OnlineResource> getOnlineResources() {
-        final int size = links.size();
-        if (size != 0) {
-            final List<OnlineResource> resources = new ArrayList<>(size);
-            for (final Link link : links) {
-                resources.add(link);
-            }
-            return resources;
-        }
-        return super.getOnlineResources();
+        return Collections.unmodifiableList(links);
     }
 
     /**
@@ -285,14 +311,10 @@ public final class Metadata extends Simp
         append(table, "Description", description);
         append(table, "Author",      author);
         append(table, "Copyright",   copyright);
-        String label = "Link(s)";
-        for (final Link link : links) {
-            append(table, label, link);
-            label = null;
-        }
-        append(table, "Time",     time);
-        append(table, "Keywords", keywords);
-        append(table, "Bounds",   bounds);
+        append(table, "Link(s)",     links, System.lineSeparator());
+        append(table, "Time",        time);
+        append(table, "Keywords",    keywords, " ");
+        append(table, "Bounds",      bounds);
         table.appendHorizontalSeparator();
         try {
             table.flush();
@@ -306,16 +328,33 @@ public final class Metadata extends Simp
      * Appends a row to the given table if the given value is non-null.
      *
      * @param  table  the table where to append a row.
-     * @param  label  the label, or {@code null} if none.
+     * @param  label  the label.
      * @param  value  the value, or {@code null} if none.
      */
     private static void append(final TableAppender table, final String label, final Object value) {
         if (value != null) {
-            if (label != null) {
-                table.append(label).append(':');
-            }
-            table.nextColumn();
+            table.append(label).append(':').nextColumn();
             table.append(value.toString()).nextLine();
         }
     }
+
+    /**
+     * Appends a multi-values to the given table if the given list is non-null.
+     *
+     * @param  table      the table where to append a row.
+     * @param  label      the label.
+     * @param  values     the values, or {@code null} if none.
+     * @param  separator  the separator to insert between each value.
+     */
+    private static void append(final TableAppender table, final String label, final List<?> values, final String separator) {
+        if (values != null) {
+            table.append(label).append(':').nextColumn();
+            final int n = values.size();
+            for (int i=0; i<n; i++) {
+                if (i != 0) table.append(separator);
+                table.append(values.get(i).toString());
+            }
+            table.nextLine();
+        }
+    }
 }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Person.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Person.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Person.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Person.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -19,6 +19,8 @@ package org.apache.sis.internal.gpx;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Objects;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 import org.apache.sis.util.iso.SimpleInternationalString;
 import org.opengis.metadata.citation.Address;
 import org.opengis.metadata.citation.Contact;
@@ -61,6 +63,7 @@ public final class Person implements Res
      *
      * @see #getName()
      */
+    @XmlElement(name = Tags.NAME)
     public String name;
 
     /**
@@ -68,6 +71,8 @@ public final class Person implements Res
      *
      * @see #getElectronicMailAddresses()
      */
+    @XmlElement(name = Tags.EMAIL)
+    @XmlJavaTypeAdapter(Email.class)
     public String email;
 
     /**
@@ -75,6 +80,7 @@ public final class Person implements Res
      *
      * @see #getOnlineResources()
      */
+    @XmlElement(name = Tags.LINK)
     public Link link;
 
     /**

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXReaderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXReaderTest.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXReaderTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXReaderTest.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -70,7 +70,7 @@ public class GPXReaderTest extends TestC
             assertEquals("sample", data.name);
             assertEquals("sample gpx test file", data.description);
             assertEquals(parseTime("2010-03-01"), data.time);
-            assertEquals("sample,metadata", data.keywords);
+            assertArrayEquals(new String[] {"sample", "metadata"}, data.keywords.toArray());
             assertEquals(new DefaultGeographicBoundingBox(-20, 30, 10, 40), data.bounds);
 
             assertEquals("Jean-Pierre", data.author.name);
@@ -97,7 +97,7 @@ public class GPXReaderTest extends TestC
             assertEquals("sample", data.name);
             assertEquals("sample gpx test file", data.description);
             assertEquals(parseTime("2010-03-01"), data.time);
-            assertEquals("sample,metadata", data.keywords);
+            assertArrayEquals(new String[] {"sample", "metadata"}, data.keywords.toArray());
             assertEquals(new DefaultGeographicBoundingBox(-20, 30, 10, 40), data.bounds);
 
             assertEquals("Jean-Pierre", data.author.name);
@@ -107,7 +107,7 @@ public class GPXReaderTest extends TestC
             assertEquals("gnu", data.copyright.author);
             assertEquals(2010, data.copyright.year.intValue());
             assertEquals("http://www.gnu.org/licenses/lgpl-3.0-standalone.html",
-                         data.copyright.license.getLinkage().toString());
+                         data.copyright.license.toString());
 
             assertEquals(3, data.links.size());
             assertEquals("http://first-adress.org", data.links.get(0).toString());
@@ -208,7 +208,7 @@ public class GPXReaderTest extends TestC
             assertEquals("http://route-adress1.org", links.get(0).toString());
 
             List<Feature> points = new ArrayList<>((Collection<Feature>) f.getPropertyValue("rtept"));
-            assertEquals(3,points.size());
+            assertEquals(3, points.size());
             checkPoint(points.get(0), 0, false);
             checkPoint(points.get(1), 1, false);
             checkPoint(points.get(2), 2, false);
@@ -453,7 +453,7 @@ public class GPXReaderTest extends TestC
 
     private void checkPoint(final Feature f, final int num, final boolean v11) throws Exception {
         if (num == 0) {
-            assertEquals(0,                     f.getPropertyValue("@identifier"));
+            assertEquals(1,                     f.getPropertyValue("@identifier"));
             assertEquals(15.0,                  ((Point)f.getPropertyValue("@geometry")).getX(), DELTA);
             assertEquals(10.0,                  ((Point)f.getPropertyValue("@geometry")).getY(), DELTA);
             assertEquals(140.0,                 f.getPropertyValue("ele"));
@@ -492,7 +492,7 @@ public class GPXReaderTest extends TestC
             assertEquals(bbox.getMaximum(1), 10.0d, DELTA);
 
         } else if (num == 1) {
-            assertEquals(1,                     f.getPropertyValue("@identifier"));
+            assertEquals(2,                     f.getPropertyValue("@identifier"));
             assertEquals(25.0,                  ((Point)f.getPropertyValue("@geometry")).getX(), DELTA);
             assertEquals(20.0,                  ((Point)f.getPropertyValue("@geometry")).getY(), DELTA);
             assertEquals(null,                  f.getPropertyValue("ele"));
@@ -523,7 +523,7 @@ public class GPXReaderTest extends TestC
             assertEquals(bbox.getMaximum(1), 20.0d, DELTA);
 
         } else if (num == 2) {
-            assertEquals(2,                     f.getPropertyValue("@identifier"));
+            assertEquals(3,                     f.getPropertyValue("@identifier"));
             assertEquals(35.0,                  ((Point) f.getPropertyValue("@geometry")).getX(), DELTA);
             assertEquals(30.0,                  ((Point) f.getPropertyValue("@geometry")).getY(), DELTA);
             assertEquals(150.0,                 f.getPropertyValue("ele"));

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXWriterTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXWriterTest.java?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXWriterTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXWriterTest.java [UTF-8] Wed Dec 14 17:36:39 2016
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.gpx;
 
+import java.net.URI;
 import java.io.File;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -74,12 +75,12 @@ public final strictfp class GPXWriterTes
         final Person person = new Person();
         person.name = "Jean-Pierre";
         person.email = "jean-pierre@test.com";
-        person.link = new Link("http://son-site.com");
+        person.link = new Link(new URI("http://son-site.com"));
 
         final Copyright copyright = new Copyright();
         copyright.author = "GNU";
         copyright.year = 2010;
-        copyright.license = new Link("http://gnu.org");
+        copyright.license = new URI("http://gnu.org");
 
         final GeographicBoundingBox bounds = new DefaultGeographicBoundingBox(-10, 20, -30, 40);
 
@@ -88,9 +89,9 @@ public final strictfp class GPXWriterTes
         metaData.description = "description";
         metaData.author = person;
         metaData.copyright = copyright;
-        metaData.links.addAll(Arrays.asList(new Link("http://adress1.org"), new Link("http://adress2.org")));
+        metaData.links.addAll(Arrays.asList(new Link(new URI("http://adress1.org")), new Link(new URI("http://adress2.org"))));
         metaData.time = Instant.now();
-        metaData.keywords = "test,sample";
+        metaData.keywords = Arrays.asList("test", "sample");
         metaData.bounds = bounds;
 
         writer.writeStartDocument();
@@ -121,7 +122,7 @@ public final strictfp class GPXWriterTes
 
         //way points -----------------------------------------------------------
         Feature point1 = types.wayPoint.newInstance();
-        point1.setPropertyValue("@identifier", 0);
+        point1.setPropertyValue("@identifier", 1);
         point1.setPropertyValue("@geometry", new Point(-10, 10));
         point1.setPropertyValue("ele", 15.6);
         point1.setPropertyValue("time", LocalDate.now());
@@ -131,7 +132,7 @@ public final strictfp class GPXWriterTes
         point1.setPropertyValue("cmt", "fdrt");
         point1.setPropertyValue("desc", "ffe");
         point1.setPropertyValue("src", "aaz");
-        point1.setPropertyValue("link", Collections.singletonList(new Link("http://test.com")));
+        point1.setPropertyValue("link", Collections.singletonList(new Link(new URI("http://test.com"))));
         point1.setPropertyValue("sym", "fdsg");
         point1.setPropertyValue("type", "klj");
         point1.setPropertyValue("fix", "yy");
@@ -142,7 +143,7 @@ public final strictfp class GPXWriterTes
         point1.setPropertyValue("ageofdgpsdata", 78.9);
         point1.setPropertyValue("dgpsid", 6);
         Feature point2 = types.wayPoint.newInstance();
-        point2.setPropertyValue("@identifier", 1);
+        point2.setPropertyValue("@identifier", 2);
         point2.setPropertyValue("@geometry", new Point(-15, 15));
         point2.setPropertyValue("ele", 15.6);
         point2.setPropertyValue("time", LocalDate.now());
@@ -152,7 +153,7 @@ public final strictfp class GPXWriterTes
         point2.setPropertyValue("cmt", "fdrt");
         point2.setPropertyValue("desc", "ffe");
         point2.setPropertyValue("src", "aaz");
-        point2.setPropertyValue("link", Collections.singletonList(new Link("http://test.com")));
+        point2.setPropertyValue("link", Collections.singletonList(new Link(new URI("http://test.com"))));
         point2.setPropertyValue("sym", "fdsg");
         point2.setPropertyValue("type", "klj");
         point2.setPropertyValue("fix", "yy");
@@ -163,7 +164,7 @@ public final strictfp class GPXWriterTes
         point2.setPropertyValue("ageofdgpsdata", 78.9);
         point2.setPropertyValue("dgpsid", 6);
         Feature point3 = types.wayPoint.newInstance();
-        point3.setPropertyValue("@identifier", 2);
+        point3.setPropertyValue("@identifier", 3);
         point3.setPropertyValue("@geometry", new Point(-20, 20));
         point3.setPropertyValue("ele", 15.6);
         point3.setPropertyValue("time", LocalDate.now());
@@ -173,7 +174,7 @@ public final strictfp class GPXWriterTes
         point3.setPropertyValue("cmt", "fdrt");
         point3.setPropertyValue("desc", "ffe");
         point3.setPropertyValue("src", "aaz");
-        point3.setPropertyValue("link", Collections.singletonList(new Link("http://test.com")));
+        point3.setPropertyValue("link", Collections.singletonList(new Link(new URI("http://test.com"))));
         point3.setPropertyValue("sym", "fdsg");
         point3.setPropertyValue("type", "klj");
         point3.setPropertyValue("fix", "yy");
@@ -191,22 +192,22 @@ public final strictfp class GPXWriterTes
 
         //routes ---------------------------------------------------------------
         final Feature route1 = types.route.newInstance();
-        route1.setPropertyValue("@identifier", 0);
+        route1.setPropertyValue("@identifier", 1);
         route1.setPropertyValue("name", "tt");
         route1.setPropertyValue("cmt", "cc");
         route1.setPropertyValue("desc", "des");
         route1.setPropertyValue("src", "src");
-        route1.setPropertyValue("link", Collections.singletonList(new Link("http://test.com")));
+        route1.setPropertyValue("link", Collections.singletonList(new Link(new URI("http://test.com"))));
         route1.setPropertyValue("number", 15);
         route1.setPropertyValue("type", "test");
         route1.setPropertyValue("rtept", wayPoints);
         final Feature route2 = types.route.newInstance();
-        route2.setPropertyValue("@identifier", 1);
+        route2.setPropertyValue("@identifier", 2);
         route2.setPropertyValue("name", "tt2");
         route2.setPropertyValue("cmt", "cc2");
         route2.setPropertyValue("desc", "des2");
         route2.setPropertyValue("src", "src2");
-        route2.setPropertyValue("link", Collections.singletonList(new Link("http://test2.com")));
+        route2.setPropertyValue("link", Collections.singletonList(new Link(new URI("http://test2.com"))));
         route2.setPropertyValue("number", 15);
         route2.setPropertyValue("type", "test2");
         route2.setPropertyValue("rtept", wayPoints);
@@ -218,32 +219,32 @@ public final strictfp class GPXWriterTes
         //tracks ---------------------------------------------------------------
         final List<Feature> segments = new ArrayList<>();
         final Feature seg1 = types.trackSegment.newInstance();
-        seg1.setPropertyValue("@identifier", 0);
+        seg1.setPropertyValue("@identifier", 1);
         seg1.setPropertyValue("trkpt", wayPoints);
         final Feature seg2 = types.trackSegment.newInstance();
-        seg2.setPropertyValue("@identifier", 1);
+        seg2.setPropertyValue("@identifier", 2);
         seg2.setPropertyValue("trkpt", wayPoints);
         final Feature seg3 = types.trackSegment.newInstance();
-        seg3.setPropertyValue("@identifier", 2);
+        seg3.setPropertyValue("@identifier", 3);
         seg3.setPropertyValue("trkpt", wayPoints);
 
         final Feature track1 = types.track.newInstance();
-        track1.setPropertyValue("@identifier", 0);
+        track1.setPropertyValue("@identifier", 1);
         track1.setPropertyValue("name", "tc");
         track1.setPropertyValue("cmt", "cc");
         track1.setPropertyValue("desc", "des");
         track1.setPropertyValue("src", "src");
-        track1.setPropertyValue("link", Collections.singletonList(new Link("http://test4.com")));
+        track1.setPropertyValue("link", Collections.singletonList(new Link(new URI("http://test4.com"))));
         track1.setPropertyValue("number", 15);
         track1.setPropertyValue("type", "test");
         track1.setPropertyValue("trkseg", segments);
         final Feature track2 = types.track.newInstance();
-        track2.setPropertyValue("@identifier", 1);
+        track2.setPropertyValue("@identifier", 2);
         track2.setPropertyValue("name", "tc2");
         track2.setPropertyValue("cmt", "cc2");
         track2.setPropertyValue("desc", "des2");
         track2.setPropertyValue("src", "src2");
-        track2.setPropertyValue("link", Collections.singletonList(new Link("http://test5.com")));
+        track2.setPropertyValue("link", Collections.singletonList(new Link(new URI("http://test5.com"))));
         track2.setPropertyValue("number", 15);
         track2.setPropertyValue("type", "test2");
         track2.setPropertyValue("trkseg", segments);

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata100.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata100.xml?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata100.xml (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata100.xml Wed Dec 14 17:36:39 2016
@@ -17,7 +17,7 @@
     <urlname>first</urlname>
 
     <time>2010-03-01</time>
-    <keywords>sample,metadata</keywords>
+    <keywords>sample metadata</keywords>
     <bounds minlat="10" minlon="-20" maxlat="40" maxlon="30"/>
 
 </gpx>

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata110.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata110.xml?rev=1774297&r1=1774296&r2=1774297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata110.xml (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/test/resources/org/apache/sis/gpx/sample_metadata110.xml Wed Dec 14 17:36:39 2016
@@ -29,7 +29,7 @@
             <type>website</type>
         </link>
         <time>2010-03-01</time>
-        <keywords>sample,metadata</keywords>
+        <keywords>sample metadata</keywords>
         <bounds minlat="10" minlon="-20" maxlat="40" maxlon="30"/>
         <extensions/>
     </metadata>



Mime
View raw message