sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1803070 [4/20] - in /sis/branches/JDK9: ./ application/sis-console/src/main/java/org/apache/sis/console/ core/sis-build-helper/ core/sis-build-helper/src/main/java/org/apache/sis/internal/book/ core/sis-build-helper/src/main/java/org/apach...
Date Wed, 26 Jul 2017 16:14:14 GMT
Modified: sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -18,6 +18,7 @@ package org.apache.sis.feature;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.HashMap;
@@ -47,23 +48,34 @@ import org.opengis.feature.PropertyType;
 })
 public final strictfp class FeatureFormatTest extends TestCase {
     /**
+     * Creates the formatter instance to be used for the tests.
+     */
+    private static FeatureFormat create() {
+        final FeatureFormat format = new FeatureFormat(Locale.US, null);
+        format.setAllowedColumns(EnumSet.of(FeatureFormat.Column.NAME,            FeatureFormat.Column.TYPE,
+                                            FeatureFormat.Column.CARDINALITY,     FeatureFormat.Column.VALUE,
+                                            FeatureFormat.Column.CHARACTERISTICS, FeatureFormat.Column.REMARKS));
+        return format;
+    }
+
+    /**
      * Tests the formatting of a {@link DefaultFeatureType}.
      */
     @Test
     public void testFeatureType() {
         final DefaultFeatureType feature = DefaultFeatureTypeTest.worldMetropolis();
-        final FeatureFormat format = new FeatureFormat(Locale.US, null);
+        final FeatureFormat format = create();
         final String text = format.format(feature);
         assertMultilinesEquals("World metropolis ⇾ Metropolis, University city\n" +
                 "┌──────────────┬─────────────────────┬─────────────┬───────────────┬────────────────────────────┐\n" +
                 "│ Name         │ Type                │ Cardinality │ Default value │ Characteristics            │\n" +
                 "├──────────────┼─────────────────────┼─────────────┼───────────────┼────────────────────────────┤\n" +
-                "│ city         │ String              │ [1 … 1]     │ Utopia        │                            │\n" +
-                "│ population   │ Integer             │ [1 … 1]     │               │                            │\n" +
-                "│ region       │ InternationalString │ [1 … 1]     │               │                            │\n" +
-                "│ isGlobal     │ Boolean             │ [1 … 1]     │               │                            │\n" +
-                "│ universities │ String              │ [0 … ∞]     │               │                            │\n" +
-                "│ temperature  │ Float               │ [1 … 1]     │               │ accuracy = 0.1, units = °C │\n" +
+                "│ city         │ String              │     [1 … 1] │ Utopia        │                            │\n" +
+                "│ population   │ Integer             │     [1 … 1] │               │                            │\n" +
+                "│ region       │ InternationalString │     [1 … 1] │               │                            │\n" +
+                "│ isGlobal     │ Boolean             │     [1 … 1] │               │                            │\n" +
+                "│ universities │ String              │     [0 … ∞] │               │                            │\n" +
+                "│ temperature  │ Float               │     [1 … 1] │               │ accuracy = 0.1, units = °C │\n" +
                 "└──────────────┴─────────────────────┴─────────────┴───────────────┴────────────────────────────┘\n", text);
     }
 
@@ -80,17 +92,17 @@ public final strictfp class FeatureForma
                 FeatureOperations.compound(name("anotherId"), ":", "<", ">", city, feature.getProperty("population")),
                 AbstractOperationTest.foundCity());
 
-        final FeatureFormat format = new FeatureFormat(Locale.US, null);
+        final FeatureFormat format = create();
         final String text = format.format(feature);
         assertMultilinesEquals("Identified city ⇾ City\n" +
                 "┌────────────┬─────────┬─────────────┬─────────────────────┐\n" +
                 "│ Name       │ Type    │ Cardinality │ Default value       │\n" +
                 "├────────────┼─────────┼─────────────┼─────────────────────┤\n" +
-                "│ city       │ String  │ [1 … 1]     │ Utopia              │\n" +
-                "│ population │ Integer │ [1 … 1]     │                     │\n" +
-                "│ someId     │ String  │ [1 … 1]     │ = city              │\n" +
-                "│ anotherId  │ String  │ [1 … 1]     │ = <city:population> │\n" +
-                "│ new city   │ String  │ [1 … 1]     │ = create(founder)   │\n" +
+                "│ city       │ String  │     [1 … 1] │ Utopia              │\n" +
+                "│ population │ Integer │     [1 … 1] │                     │\n" +
+                "│ someId     │ String  │     [1 … 1] │ = city              │\n" +
+                "│ anotherId  │ String  │     [1 … 1] │ = <city:population> │\n" +
+                "│ new city   │ String  │     [1 … 1] │ = create(founder)   │\n" +
                 "└────────────┴─────────┴─────────────┴─────────────────────┘\n", text);
     }
 
@@ -114,15 +126,15 @@ public final strictfp class FeatureForma
         feature = new DefaultFeatureType(name("City for human"), false, new DefaultFeatureType[] {feature},
                 new DefaultAttributeType<>(properties, String.class, 0, 2, null));
 
-        final FeatureFormat format = new FeatureFormat(Locale.US, null);
+        final FeatureFormat format = create();
         final String text = format.format(feature);
         assertMultilinesEquals("City for human ⇾ City\n" +
                 "┌────────────┬─────────┬─────────────┬───────────────┬─────────────┐\n" +
                 "│ Name       │ Type    │ Cardinality │ Default value │ Remarks     │\n" +
                 "├────────────┼─────────┼─────────────┼───────────────┼─────────────┤\n" +
-                "│ city       │ String  │ [1 … 1]     │ Utopia        │             │\n" +
-                "│ population │ Integer │ [1 … 1]     │               │             │\n" +
-                "│ highway    │ String  │ [0 … 2]     │               │ Deprecated¹ │\n" +
+                "│ city       │ String  │     [1 … 1] │ Utopia        │             │\n" +
+                "│ population │ Integer │     [1 … 1] │               │             │\n" +
+                "│ highway    │ String  │     [0 … 2] │               │ Deprecated¹ │\n" +
                 "└────────────┴─────────┴─────────────┴───────────────┴─────────────┘\n" +
                 "¹ Replaced by pedestrian areas.\n", text);
     }
@@ -140,19 +152,20 @@ public final strictfp class FeatureForma
         feature.setPropertyValue("city", "Tokyo");
         feature.setPropertyValue("population", 13185502);                               // In 2011.
         feature.setPropertyValue("universities", Arrays.asList("Waseda", "Keio"));
+        feature.setPropertyValue("temperature", Float.NaN);
 
-        final FeatureFormat format = new FeatureFormat(Locale.US, null);
+        final FeatureFormat format = create();
         final String text = format.format(feature);
         assertMultilinesEquals("World metropolis\n" +
                 "┌──────────────┬─────────────────────┬─────────────┬──────────────┬─────────────────┐\n" +
                 "│ Name         │ Type                │ Cardinality │ Value        │ Characteristics │\n" +
                 "├──────────────┼─────────────────────┼─────────────┼──────────────┼─────────────────┤\n" +
-                "│ city         │ String              │ [1 … 1]     │ Tokyo        │                 │\n" +
-                "│ population   │ Integer             │ [1 … 1]     │ 13,185,502   │                 │\n" +
-                "│ region       │ InternationalString │ [1 … 1]     │              │                 │\n" +
-                "│ isGlobal     │ Boolean             │ [1 … 1]     │              │                 │\n" +
-                "│ universities │ String              │ [0 … ∞]     │ Waseda, Keio │                 │\n" +
-                "│ temperature  │ Float               │ [1 … 1]     │              │ accuracy, units │\n" +
+                "│ city         │ String              │ 1 ∈ [1 … 1] │ Tokyo        │                 │\n" +
+                "│ population   │ Integer             │ 1 ∈ [1 … 1] │ 13,185,502   │                 │\n" +
+                "│ region       │ InternationalString │ 0 ∉ [1 … 1] │              │                 │\n" +
+                "│ isGlobal     │ Boolean             │ 0 ∉ [1 … 1] │              │                 │\n" +
+                "│ universities │ String              │ 2 ∈ [0 … ∞] │ Waseda, Keio │                 │\n" +
+                "│ temperature  │ Float               │ 1 ∈ [1 … 1] │ NaN          │ accuracy, units │\n" +
                 "└──────────────┴─────────────────────┴─────────────┴──────────────┴─────────────────┘\n", text);
     }
 
@@ -174,15 +187,15 @@ public final strictfp class FeatureForma
         feature.setPropertyValue("population", 143174);                     // December 31th, 2011
         feature.setPropertyValue("twin town", twinTown);
 
-        final FeatureFormat format = new FeatureFormat(Locale.US, null);
+        final FeatureFormat format = create();
         final String text = format.format(feature);
         assertMultilinesEquals("Twin town\n" +
                 "┌────────────┬─────────┬─────────────┬───────────┐\n" +
                 "│ Name       │ Type    │ Cardinality │ Value     │\n" +
                 "├────────────┼─────────┼─────────────┼───────────┤\n" +
-                "│ city       │ String  │ [1 … 1]     │ Paderborn │\n" +
-                "│ population │ Integer │ [1 … 1]     │ 143,174   │\n" +
-                "│ twin town  │ City    │ [0 … 1]     │ Le Mans   │\n" +
+                "│ city       │ String  │ 1 ∈ [1 … 1] │ Paderborn │\n" +
+                "│ population │ Integer │ 1 ∈ [1 … 1] │ 143,174   │\n" +
+                "│ twin town  │ City    │ 1 ∈ [0 … 1] │ Le Mans   │\n" +
                 "└────────────┴─────────┴─────────────┴───────────┘\n", text);
     }
 }

Modified: sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureMemoryBenchmark.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureMemoryBenchmark.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureMemoryBenchmark.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureMemoryBenchmark.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -21,6 +21,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Random;
 import org.opengis.feature.Feature;
+import org.apache.sis.internal.util.StandardDateFormat;
 
 import static java.util.Collections.singletonMap;
 
@@ -179,7 +180,7 @@ public final class FeatureMemoryBenchmar
                 long time = System.nanoTime();
                 b.run();
                 time = System.nanoTime() - time;
-                System.console().printf("Ellapsed time: %f%n", time / 1E+9);
+                System.console().printf("Ellapsed time: %f%n", time / (float) StandardDateFormat.NANOS_PER_SECOND);
                 return;
             }
         }

Modified: sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -17,10 +17,14 @@
 package org.apache.sis.feature.builder;
 
 import java.util.Iterator;
+import java.util.Collections;
 import com.esri.core.geometry.Geometry;
 import com.esri.core.geometry.Point;
+import org.apache.sis.feature.AbstractOperation;
+import org.opengis.geometry.Envelope;
 import org.apache.sis.internal.feature.AttributeConvention;
 import org.apache.sis.feature.DefaultFeatureTypeTest;
+import org.apache.sis.feature.FeatureOperations;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
@@ -33,7 +37,9 @@ import static org.junit.Assert.*;
 // Branch-dependent imports
 import org.opengis.feature.AttributeType;
 import org.opengis.feature.FeatureType;
+import org.opengis.feature.IdentifiedType;
 import org.opengis.feature.PropertyType;
+import org.opengis.feature.Operation;
 
 
 /**
@@ -288,4 +294,56 @@ public final strictfp class FeatureTypeB
         assertNotSame("Should return a new FeatureType since we changed an attribute.",
                       city, builder.build());
     }
+
+    /**
+     * Tests overriding the "sis:envelope" property. This may happen when the user wants to specify
+     * envelope himself instead than relying on the automatically computed value.
+     */
+    @Test
+    public void testEnvelopeOverride() {
+        FeatureTypeBuilder builder = new FeatureTypeBuilder().setName("CoverageRecord").setAbstract(true);
+        builder.addAttribute(Geometry.class).setName(AttributeConvention.GEOMETRY_PROPERTY).addRole(AttributeRole.DEFAULT_GEOMETRY);
+        final FeatureType parentType = builder.build();
+
+        builder = new FeatureTypeBuilder().setName("Record").setSuperTypes(parentType);
+        builder.addAttribute(Envelope.class).setName(AttributeConvention.ENVELOPE_PROPERTY);
+        final FeatureType childType = builder.build();
+
+        final Iterator<? extends PropertyType> it = childType.getProperties(true).iterator();
+        assertPropertyEquals("sis:envelope", Envelope.class, it.next());
+        assertPropertyEquals("sis:geometry", Geometry.class, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * Tests overriding an attribute by an operation.
+     * This is the converse of {@link #testEnvelopeOverride()}.
+     */
+    @Test
+    public void testOverrideByOperation() {
+        FeatureTypeBuilder builder = new FeatureTypeBuilder().setName("Parent").setAbstract(true);
+        final AttributeType<Integer> pa = builder.addAttribute(Integer.class).setName("A").build();
+        builder.addAttribute(Integer.class).setName("B");
+        final FeatureType parentType = builder.build();
+
+        builder = new FeatureTypeBuilder().setName("Child").setSuperTypes(parentType);
+        builder.addProperty(FeatureOperations.link(Collections.singletonMap(AbstractOperation.NAME_KEY, "B"), pa));
+        final FeatureType childType = builder.build();
+
+        final Iterator<? extends PropertyType> it = childType.getProperties(true).iterator();
+        assertPropertyEquals("A", Integer.class, it.next());
+        assertPropertyEquals("B", Integer.class, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * Verifies that the given property is an attribute with the given name and value class.
+     */
+    private static void assertPropertyEquals(final String name, final Class<?> valueClass, IdentifiedType property) {
+        assertEquals("name", name, property.getName().toString());
+        if (property instanceof Operation) {
+            property = ((Operation) property).getResult();
+        }
+        assertEquals("valueClass", valueClass, ((AttributeType<?>) property).getValueClass());
+    }
 }

Modified: sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/internal/feature/AttributeConventionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/internal/feature/AttributeConventionTest.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/internal/feature/AttributeConventionTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-feature/src/test/java/org/apache/sis/internal/feature/AttributeConventionTest.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -22,6 +22,8 @@ import com.esri.core.geometry.Point;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.feature.DefaultAttributeType;
+import org.apache.sis.feature.DefaultFeatureType;
+import org.apache.sis.feature.FeatureOperations;
 import org.apache.sis.util.iso.Names;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -30,6 +32,7 @@ import static org.junit.Assert.*;
 
 // Branch-dependent imports
 import org.opengis.feature.Property;
+import org.opengis.feature.Operation;
 import org.opengis.feature.IdentifiedType;
 
 
@@ -88,6 +91,14 @@ public final strictfp class AttributeCon
         type = new DefaultAttributeType<>(properties, Point.class, 1, 1, null, characteristic);
         assertTrue("characterizedByCRS", AttributeConvention.characterizedByCRS(type));
         assertEquals(HardCodedCRS.WGS84, AttributeConvention.getCRSCharacteristic(type.newInstance()));
+        assertEquals(HardCodedCRS.WGS84, AttributeConvention.getCRSCharacteristic(null, type));
+        /*
+         * Test again AttributeConvention.getCRSCharacteristic(…, PropertyType), but following link.
+         */
+        final Operation link = FeatureOperations.link(Collections.singletonMap(DefaultAttributeType.NAME_KEY, "geom"), type);
+        final DefaultFeatureType feat = new DefaultFeatureType(Collections.singletonMap(DefaultAttributeType.NAME_KEY, "feat"), false, null, type, link);
+        assertEquals(HardCodedCRS.WGS84, AttributeConvention.getCRSCharacteristic(feat, link));
+        assertNull(                      AttributeConvention.getCRSCharacteristic(null, link));
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-metadata/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/pom.xml?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/pom.xml (original)
+++ sis/branches/JDK9/core/sis-metadata/pom.xml Wed Jul 26 16:14:09 2017
@@ -146,6 +146,11 @@ Implementations of metadata derived from
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -247,6 +247,21 @@ public final class AxisDirections extend
     }
 
     /**
+     * Returns {@code true} if the specified direction is cardinal direction.
+     * Cardinal directions are {@code NORTH}, {@code SOUTH}, {@code EAST} and {@code WEST}.
+     *
+     * @param  dir  the direction to test, or {@code null}.
+     * @return {@code true} if the given direction is a cardinal direction.
+     *
+     * @since 0.8
+     */
+    public static boolean isCardinal(final AxisDirection dir) {
+        if (dir == null) return false;
+        final int n  = dir.ordinal() - NORTH.ordinal();
+        return n >= 0 && n < COMPASS_COUNT && (n & 3) == 0;
+    }
+
+    /**
      * Returns {@code true} if the specified direction is an inter-cardinal direction.
      * Inter-cardinal directions are {@code NORTH_EAST}, {@code SOUTH_SOUTH_EAST}, etc.
      *

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/NameToIdentifier.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/NameToIdentifier.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/NameToIdentifier.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/NameToIdentifier.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -41,7 +41,7 @@ import static org.apache.sis.util.Charac
  * Current version does not yet work with URN or HTTP syntax.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.4
  * @module
  */
@@ -207,6 +207,32 @@ public final class NameToIdentifier impl
     }
 
     /**
+     * Returns {@code true} if the given identifier to search matches one of the object identifiers.
+     *
+     * @param  identifiers  the identifiers to compare against {@code toSearch}.
+     * @param  toSearch     the identifier to check for equality.
+     * @return {@code true} if the identifier to search is found in the given set of identifiers.
+     *
+     * @since 0.8
+     */
+    public static boolean isHeuristicMatchForIdentifier(final Iterable<? extends Identifier> identifiers, final String toSearch) {
+        if (toSearch != null && identifiers != null) {
+            for (int s = toSearch.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR); s >= 0;
+                     s = toSearch.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR, s))
+            {
+                final String codespace = toSearch.substring(0, s).trim();
+                final String code = toSearch.substring(++s).trim();
+                for (final Identifier id : identifiers) {
+                    if (codespace.equalsIgnoreCase(id.getCodeSpace()) && code.equalsIgnoreCase(id.getCode())) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns {@code true} if the given {@linkplain org.apache.sis.referencing.AbstractIdentifiedObject#getName()
      * primary name} or one of the given aliases matches the given name. The comparison ignores case, some Latin
      * diacritical signs and any characters that are not letters or digits.
@@ -222,28 +248,30 @@ public final class NameToIdentifier impl
     public static boolean isHeuristicMatchForName(final Identifier name, final Collection<GenericName> aliases,
             CharSequence toSearch, final Simplifier simplifier)
     {
-        toSearch = simplifier.apply(toSearch);
-        if (name != null) {                                                                 // Paranoiac check.
-            final CharSequence code = simplifier.apply(name.getCode());
-            if (code != null) {                                                             // Paranoiac check.
-                if (CharSequences.equalsFiltered(toSearch, code, LETTERS_AND_DIGITS, true)) {
-                    return true;
-                }
+        if (toSearch != null) {
+            CharSequence code = (name != null) ? name.getCode() : null;
+            if (toSearch.equals(code)) {
+                return true;                                                    // Optimization for a common case.
             }
-        }
-        if (aliases != null) {
-            for (final GenericName alias : aliases) {
-                if (alias != null) {                                                        // Paranoiac check.
-                    final CharSequence tip = simplifier.apply(alias.tip().toString());
-                    if (CharSequences.equalsFiltered(toSearch, tip, LETTERS_AND_DIGITS, true)) {
-                        return true;
+            toSearch = simplifier.apply(toSearch);
+            code     = simplifier.apply(code);
+            if (CharSequences.equalsFiltered(toSearch, code, LETTERS_AND_DIGITS, true)) {
+                return true;
+            }
+            if (aliases != null) {
+                for (final GenericName alias : aliases) {
+                    if (alias != null) {                                                        // Paranoiac check.
+                        final CharSequence tip = simplifier.apply(alias.tip().toString());
+                        if (CharSequences.equalsFiltered(toSearch, tip, LETTERS_AND_DIGITS, true)) {
+                            return true;
+                        }
+                        /*
+                         * Note: a previous version compared also the scoped names. We removed that part,
+                         * because experience has shown that this method is used only for the "code" part
+                         * of an object name. If we really want to compare scoped name, it would probably
+                         * be better to take a GenericName argument instead than String.
+                         */
                     }
-                    /*
-                     * Note: a previous version compared also the scoped names. We removed that part,
-                     * because experience has shown that this method is used only for the "code" part
-                     * of an object name. If we really want to compare scoped name, it would probably
-                     * be better to take a GenericName argument instead than String.
-                     */
                 }
             }
         }

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/RecordSchemaSIS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/RecordSchemaSIS.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/RecordSchemaSIS.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/RecordSchemaSIS.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -30,13 +30,9 @@ import org.apache.sis.util.iso.DefaultRe
  * @since   0.7
  * @module
  */
+@SuppressWarnings("serial")  // serialVersionUID not needed because of writeReplace().
 final class RecordSchemaSIS extends DefaultRecordSchema implements Serializable {
     /**
-     * For cross-version compatibility.
-     */
-    private static final long serialVersionUID = 2708181165532467516L;
-
-    /**
      * The schema used in SIS for creating records.
      */
     static final DefaultRecordSchema INSTANCE = new RecordSchemaSIS();
@@ -51,7 +47,7 @@ final class RecordSchemaSIS extends Defa
     /**
      * On serialization, returns a proxy which will be resolved as {@link #INSTANCE} on deserialization.
      */
-    Object writeReplace() throws ObjectStreamException {
+    protected Object writeReplace() throws ObjectStreamException {
         return new Proxy();
     }
 

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -25,7 +25,6 @@ import javax.measure.Unit;
 import javax.measure.quantity.Length;
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
-import org.opengis.metadata.Identifier;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.crs.CRSFactory;
@@ -40,6 +39,7 @@ import org.opengis.referencing.cs.Cartes
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.cs.EllipsoidalCS;
+import org.opengis.referencing.datum.DatumFactory;
 import org.opengis.referencing.datum.PrimeMeridian;
 import org.opengis.referencing.datum.VerticalDatum;
 import org.opengis.referencing.operation.MathTransform;
@@ -59,7 +59,6 @@ import org.apache.sis.internal.system.Op
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.io.wkt.FormattableObject;
 import org.apache.sis.util.ArraysExt;
-import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.util.Deprecable;
 
 
@@ -131,12 +130,9 @@ public class ReferencingServices extends
     public static final String CS_FACTORY = "csFactory";
 
     /**
-     * The separator character between an identifier and its namespace in the argument given to
-     * {@link #getOperationMethod(Iterable, String)}. For example this is the separator in {@code "EPSG:9807"}.
-     *
-     * This is defined as a constant for now, but we may make it configurable in a future version.
+     * The key for specifying a {@link DatumFactory} instance to use for geodetic object constructions.
      */
-    private static final char IDENTIFIER_SEPARATOR = DefaultNameSpace.DEFAULT_SEPARATOR;
+    public static final String DATUM_FACTORY = "datumFactory";
 
     /**
      * The services, fetched when first needed.
@@ -645,33 +641,6 @@ public class ReferencingServices extends
     }
 
     /**
-     * Returns {@code true} if the name or an identifier of the given method matches the given {@code identifier}.
-     *
-     * @param  method      the method to test for a match.
-     * @param  identifier  the name or identifier of the operation method to search.
-     * @return {@code true} if the given method is a match for the given identifier.
-     *
-     * @since 0.6
-     */
-    private boolean matches(final OperationMethod method, final String identifier) {
-        if (isHeuristicMatchForName(method, identifier)) {
-            return true;
-        }
-        for (int s = identifier.indexOf(IDENTIFIER_SEPARATOR); s >= 0;
-                 s = identifier.indexOf(IDENTIFIER_SEPARATOR, s))
-        {
-            final String codespace = identifier.substring(0, s).trim();
-            final String code = identifier.substring(++s).trim();
-            for (final Identifier id : method.getIdentifiers()) {
-                if (codespace.equalsIgnoreCase(id.getCodeSpace()) && code.equalsIgnoreCase(id.getCode())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
      * Returns the operation method for the specified name or identifier. The given argument shall be either a
      * method name (e.g. <cite>"Transverse Mercator"</cite>) or one of its identifiers (e.g. {@code "EPSG:9807"}).
      *
@@ -687,7 +656,9 @@ public class ReferencingServices extends
     public final OperationMethod getOperationMethod(final Iterable<? extends OperationMethod> methods, final String identifier) {
         OperationMethod fallback = null;
         for (final OperationMethod method : methods) {
-            if (matches(method, identifier)) {
+            if (isHeuristicMatchForName(method, identifier) ||
+                    NameToIdentifier.isHeuristicMatchForIdentifier(method.getIdentifiers(), identifier))
+            {
                 /*
                  * Stop the iteration at the first non-deprecated method.
                  * If we find only deprecated methods, take the first one.

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ServicesForUtility.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ServicesForUtility.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ServicesForUtility.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ServicesForUtility.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -174,7 +174,7 @@ public final class ServicesForUtility ex
                 alternateTitle = key;
                 break;
             }
-            case "Proj4": {
+            case Constants.PROJ4: {
                 title = "Proj.4";
                 break;
             }

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Dialect.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Dialect.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Dialect.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Dialect.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -26,38 +26,38 @@ import org.apache.sis.util.CharSequences
  * that can not (to our knowledge) be inferred from the {@link DatabaseMetaData}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
-enum Dialect {
+public enum Dialect {
     /**
      * The database is presumed to use ANSI SQL syntax.
      */
-    ANSI(null),
+    ANSI(null, false),
 
     /**
      * The database uses Derby syntax. This is ANSI, with some constraints that PostgreSQL does not have
      * (for example column with {@code UNIQUE} constraint must explicitly be specified as {@code NOT NULL}).
      */
-    DERBY("derby"),
+    DERBY("derby", false),
 
     /**
      * The database uses HSQL syntax. This is ANSI, but does not allow {@code INSERT} statements inserting many lines.
      * It also have a {@code SHUTDOWN} command which is specific to HSQLDB.
      */
-    HSQL("hsqldb"),
+    HSQL("hsqldb", false),
 
     /**
      * The database uses PostgreSQL syntax. This is ANSI, but provided an a separated
      * enumeration value because it allows a few additional commands like {@code VACUUM}.
      */
-    POSTGRESQL("postgresql"),
+    POSTGRESQL("postgresql", true),
 
     /**
      * The database uses Oracle syntax. This is ANSI, but without {@code "AS"} keyword.
      */
-    ORACLE("oracle");
+    ORACLE("oracle", false);
 
     /**
      * The protocol in JDBC URL, or {@code null} if unknown.
@@ -66,10 +66,24 @@ enum Dialect {
     private final String protocol;
 
     /**
+     * Whether this dialect support table inheritance.
+     */
+    public final boolean isTableInheritanceSupported;
+
+    /**
+     * {@code true} if child tables inherit the index of their parent tables.
+     * This feature is not yet supported in PostgreSQL.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-358">SIS-358</a>
+     */
+    public final boolean isIndexInheritanceSupported = false;
+
+    /**
      * Creates a new enumeration value for a SQL dialect for the given protocol.
      */
-    private Dialect(final String protocol) {
+    private Dialect(final String protocol, final boolean isTableInheritanceSupported) {
         this.protocol = protocol;
+        this.isTableInheritanceSupported = isTableInheritanceSupported;
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -389,7 +389,10 @@ public abstract class Initializer {
                     if (home != null) {
                         final Path file = Paths.get(home).resolveSibling("db/lib/derby.jar");
                         if (Files.isRegularFile(file)) {
-                            javadbLoader = loader = new URLClassLoader(new URL[] {file.toUri().toURL()});
+                            javadbLoader = loader = new URLClassLoader(new URL[] {
+                                file.toUri().toURL(),
+                                file.resolveSibling("derbynet.jar").toUri().toURL()
+                            });
                         }
                     }
                 }

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/SQLBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/SQLBuilder.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/SQLBuilder.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/SQLBuilder.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -34,7 +34,7 @@ public class SQLBuilder {
     /**
      * The database dialect. This is used for a few database-dependent syntax.
      */
-    private final Dialect dialect;
+    public final Dialect dialect;
 
     /**
      * The characters used for quoting identifiers, or an empty string if none.

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -197,8 +197,8 @@ abstract class AbstractParser implements
     }
 
     /**
-     * Creates the object from a string. This method is for implementation of {@code createFromWKT(String)}
-     * method is SIS factories only.
+     * Creates the object from a string and log the warnings if any.
+     * This method is for implementation of {@code createFromWKT(String)} method is SIS factories only.
      *
      * @param  text  coordinate system encoded in Well-Known Text format (version 1 or 2).
      * @return the result of parsing the given text.

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Parser.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Parser.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Parser.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Parser.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -37,6 +37,12 @@ import org.opengis.util.FactoryException
  *   <li>{@link org.apache.sis.geometry.Envelopes#fromWKT(CharSequence)}</li>
  * </ul>
  *
+ * Non-fatal anomalies found in Well Known Texts are reported in a {@linkplain java.util.logging.Logger logger}
+ * named {@code "org.apache.sis.io.wkt"}. Warnings may be for unknown or unsupported WKT elements, inconsistent
+ * unit definitions (unit symbol, scale factor or EPSG code), unparsable axis abbreviations, <i>etc.</i>
+ * However this parser does not verify if the overall parsed object matches the EPSG (or other authority) definition.
+ * For such verification, see {@link org.apache.sis.referencing.CRS#fromWKT(String)}.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.6
  * @since   0.6
@@ -48,6 +54,12 @@ public interface Parser {
      * Objects returned by this method are typically (but not necessarily)
      * {@linkplain org.apache.sis.referencing.crs.AbstractCRS Coordinate Reference Systems} or
      * {@linkplain org.apache.sis.referencing.operation.transform.AbstractMathTransform Math Transforms}.
+     * If the given text contains non-fatal anomalies, warnings may be reported in a
+     * {@linkplain java.util.logging.Logger logger} named {@code "org.apache.sis.io.wkt"}.
+     *
+     * <div class="note"><b>Tip:</b>
+     * for processing warnings in a different way than logging them, one can use
+     * {@link WKTFormat#parseObject(String)} followed by a call to {@link WKTFormat#getWarnings()}.</div>
      *
      * @param  text  object encoded in Well-Known Text format (version 1 or 2).
      * @return the result of parsing the given text.

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -258,7 +258,7 @@ public abstract class AbstractMetadata i
      * adds a title to a citation:
      *
      * {@preformat java
-     *     TreeTable.Node node = ...; // The node for a DefaultCitation.
+     *     TreeTable.Node node = ...;                               // The node for a DefaultCitation.
      *     TreeTable.Node child = node.newChild();
      *     child.setValue(TableColumn.IDENTIFIER, "title");
      *     child.setValue(TableColumn.VALUE, "Le petit prince");
@@ -272,7 +272,7 @@ public abstract class AbstractMetadata i
      * The default implementation is equivalent to the following method call:
      *
      * {@preformat java
-     *   return getStandard().asTreeTable(this, null, ValueExistencePolicy.NON_EMPTY);
+     *   return getStandard().asTreeTable(this, null, ValueExistencePolicy.COMPACT);
      * }
      *
      * @return a tree table representation of the specified metadata.
@@ -280,7 +280,7 @@ public abstract class AbstractMetadata i
      * @see MetadataStandard#asTreeTable(Object, Class, ValueExistencePolicy)
      */
     public TreeTable asTreeTable() {
-        return getStandard().asTreeTable(this, null, ValueExistencePolicy.NON_EMPTY);
+        return getStandard().asTreeTable(this, null, ValueExistencePolicy.COMPACT);
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -590,8 +590,12 @@ public class MetadataStandard implements
 
     /**
      * Returns the implementation class for the given interface, or {@code null} if none.
-     * The default implementation returns {@code null} in every cases. Subclasses shall
-     * override this method in order to map GeoAPI interfaces to their implementation.
+     * If non-null, the returned class must have a public no-argument constructor and the
+     * metadata instance created by that constructor must be initially empty (no default value).
+     * That no-argument constructor should never throw any checked exception.
+     *
+     * <p>The default implementation returns {@code null} in every cases. Subclasses shall
+     * override this method in order to map GeoAPI interfaces to their implementation.</p>
      *
      * @param  <T>   the compile-time {@code type}.
      * @param  type  the interface, typically from the {@code org.opengis.metadata} package.
@@ -645,14 +649,14 @@ public class MetadataStandard implements
     }
 
     /**
-     * Returns the type of all properties, or their declaring type, defined in the given
-     * metadata type. The keys in the returned map are the same than the keys in the above
-     * {@linkplain #asNameMap name map}. The values are determined by the {@code valuePolicy}
-     * argument, which can be {@linkplain TypeValuePolicy#ELEMENT_TYPE element type} or the
+     * Returns the type of all properties, or their declaring type, defined in the given metadata type.
+     * The keys in the returned map are the same than the keys in the above {@linkplain #asNameMap name map}.
+     * The values are determined by the {@code valuePolicy} argument, which can be
+     * {@linkplain TypeValuePolicy#ELEMENT_TYPE element type} or the
      * {@linkplain TypeValuePolicy#DECLARING_INTERFACE declaring interface} among others.
      *
-     * <p><b>Example:</b> the following code prints the
-     * {@link org.opengis.util.InternationalString} class name:</p>
+     * <div class="note"><b>Example:</b>
+     * the following code prints the {@link org.opengis.util.InternationalString} class name:
      *
      * {@preformat java
      *   MetadataStandard  standard = MetadataStandard.ISO_19115;
@@ -660,6 +664,7 @@ public class MetadataStandard implements
      *   Class<?> value = types.get("alternateTitle");
      *   System.out.println(value);                       // class org.opengis.util.InternationalString
      * }
+     * </div>
      *
      * @param  type         the interface or implementation class of a metadata.
      * @param  keyPolicy    determines the string representation of map keys.
@@ -708,7 +713,7 @@ public class MetadataStandard implements
      * </ul>
      *
      * <div class="note"><b>Note:</b>
-     * The rational for implementing {@code CheckedContainer} is to consider each {@code ExtendedElementInformation}
+     * the rational for implementing {@code CheckedContainer} is to consider each {@code ExtendedElementInformation}
      * instance as the set of all possible values for the property. If the information had a {@code contains(E)} method,
      * it would return {@code true} if the given value is valid for that property.</div>
      *
@@ -726,8 +731,8 @@ public class MetadataStandard implements
      *
      * @see org.apache.sis.metadata.iso.DefaultExtendedElementInformation
      */
-    public Map<String,ExtendedElementInformation> asInformationMap(Class<?> type,
-            final KeyNamePolicy keyPolicy) throws ClassCastException
+    public Map<String,ExtendedElementInformation> asInformationMap(Class<?> type, final KeyNamePolicy keyPolicy)
+            throws ClassCastException
     {
         ensureNonNull("type",     type);
         ensureNonNull("keyNames", keyPolicy);
@@ -739,6 +744,35 @@ public class MetadataStandard implements
     }
 
     /**
+     * Returns indices for all properties defined in the given metadata type.
+     * The keys in the returned map are the same than the keys in the above {@linkplain #asNameMap name map}.
+     * The values are arbitrary indices numbered from 0 inclusive to <var>n</var> exclusive, where <var>n</var>
+     * is the number of properties declared in the given metadata type.
+     *
+     * <p>Property indices may be used as an alternative to property names by some applications doing their own storage.
+     * Such index usages are fine for temporary storage during the Java Virtual Machine lifetime, but indices should not
+     * be used in permanent storage. The indices are stable as long as the metadata implementation does not change,
+     * but may change when the implementation is upgraded to a newer version.</p>
+     *
+     * @param  type       the interface or implementation class of a metadata.
+     * @param  keyPolicy  determines the string representation of map keys.
+     * @return indices of all properties defined by the given metadata type.
+     * @throws ClassCastException if the specified interface or implementation class does
+     *         not extend or implement a metadata interface of the expected package.
+     */
+    public Map<String,Integer> asIndexMap(Class<?> type, final KeyNamePolicy keyPolicy)
+            throws ClassCastException
+    {
+        ensureNonNull("type",      type);
+        ensureNonNull("keyPolicy", keyPolicy);
+        final Class<?> implementation = getImplementation(type);
+        if (implementation != null) {
+            type = implementation;
+        }
+        return new IndexMap(getAccessor(new CacheKey(type), true), keyPolicy);
+    }
+
+    /**
      * Returns a view of the specified metadata object as a {@link Map}.
      * The map is backed by the metadata object using Java reflection, so changes in the
      * underlying metadata object are immediately reflected in the map and conversely.
@@ -860,7 +894,7 @@ public class MetadataStandard implements
      * adds a title to a citation:
      *
      * {@preformat java
-     *     TreeTable.Node node = ...; // The node for a DefaultCitation.
+     *     TreeTable.Node node = ...;                               // The node for a DefaultCitation.
      *     TreeTable.Node child = node.newChild();
      *     child.setValue(TableColumn.IDENTIFIER, "title");
      *     child.setValue(TableColumn.VALUE, "Le petit prince");

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/ObjectPair.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -37,11 +37,7 @@ final class ObjectPair {
     /**
      * The set of objects currently in process of being compared.
      */
-    static final ThreadLocal<Set<ObjectPair>> CURRENT = new ThreadLocal<Set<ObjectPair>>() {
-        @Override protected Set<ObjectPair> initialValue() {
-            return new HashSet<>();
-        }
-    };
+    static final ThreadLocal<Set<ObjectPair>> CURRENT = ThreadLocal.withInitial(HashSet::new);
 
     /**
      * The pair of objects in process of being compared.

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -702,9 +702,29 @@ class PropertyAccessor {
         try {
             return method.invoke(metadata, (Object[]) null);
         } catch (IllegalAccessException e) {
-            // Should never happen since 'getters' should contains only public methods.
-            throw new AssertionError(e);
+            /*
+             * Should never happen since 'getters' should contains only public methods.
+             */
+            throw new AssertionError(method.toString(), e);
+        } catch (IllegalArgumentException e) {
+            /*
+             * May happen if the getter method is defined only in the implementation class, not in the interface,
+             * but the given metadata object is an instance of another implementation class than the expected one.
+             *
+             * Example: CI_Citation.graphics didn't existed in ISO 19115:2003 and has been added in ISO 19115:2014.
+             * Consequently there is no Citation.getGraphics() method in GeoAPI 3.0 interfaces (only in GeoAPI 3.1),
+             * but there is a DefaultCitation.getGraphics() method in Apache SIS implementation since SIS is a little
+             * bit ahead of GeoAPI. But if the 'metadata' argument is another implementation of the Citation interface,
+             * attempt to invoke DefaultCitation.getGraphics() will fail with IllegalArgumentException.
+             */
+            if (!method.getDeclaringClass().isInstance(metadata)) {
+                return null;
+            }
+            throw e;                                // Exception thrown for another reason. This is probably a bug.
         } catch (InvocationTargetException e) {
+            /*
+             * Exception in user code (not a wrong usage of reflection).
+             */
             final Throwable cause = e.getTargetException();
             if (cause instanceof RuntimeException) {
                 throw (RuntimeException) cause;

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNode.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNode.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNode.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNode.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -178,12 +178,20 @@ class TreeNode implements Node {
         this.parent   = parent;
         this.metadata = metadata;
         this.baseType = baseType;
-        if (!table.standard.isMetadata(baseType)) {
+        if (!isMetadata(baseType)) {
             children = LEAF;
         }
     }
 
     /**
+     * Returns {@code true} if nodes for values of the given type can be expanded with more children.
+     * A return value of {@code false} means that values of the given type are leaves.
+     */
+    final boolean isMetadata(final Class<?> type) {
+        return table.standard.isMetadata(type);
+    }
+
+    /**
      * Returns the key to use for calls to {@link MetadataStandard} methods.
      * This key is used only for some default method implementations in the root node;
      * children will use the class of their node value instead.
@@ -344,10 +352,63 @@ class TreeNode implements Node {
          * {@link KeyNamePolicy#UML_IDENTIFIER} instead than {@link KeyNamePolicy#JAVABEANS_PROPERTY}
          * in order to get the singular form instead of the plural one, because we will create one
          * node for each element in a collection.
+         *
+         * <p>If the property name is equals, ignoring case, to the simple type name, then this method
+         * returns the subtype name. For example instead of:</p>
+         *
+         * {@preformat text
+         *   Citation
+         *    └─Cited responsible party
+         *       └─Party
+         *          └─Name ……………………………… Jon Smith
+         * }
+         *
+         * we format:
+         *
+         * {@preformat
+         *   Citation
+         *    └─Cited responsible party
+         *       └─Individual
+         *          └─Name ……………………………… Jon Smith
+         * }
+         *
+         * @see <a href="https://issues.apache.org/jira/browse/SIS-298">SIS-298</a>
          */
         @Override
         CharSequence getName() {
-            return CharSequences.camelCaseToSentence(getIdentifier()).toString();
+            String identifier = getIdentifier();
+            if (identifier.equalsIgnoreCase(Classes.getShortName(baseType))) {
+                final Object value = getUserObject();
+                if (value != null) {
+                    Class<?> type = standardSubType(Classes.getLeafInterfaces(value.getClass(), baseType));
+                    if (type != null && type != Void.TYPE) {
+                        identifier = Classes.getShortName(type);
+                    }
+                }
+            }
+            return CharSequences.camelCaseToSentence(identifier).toString();
+        }
+
+        /**
+         * Returns the element of the given array which is both assignable to {@link #baseType} and a member
+         * of the standard represented by {@link TreeTableView#standard}. If no such type is found, returns
+         * {@code null}. If more than one type is found, returns the {@link Void#TYPE} sentinel value.
+         */
+        private Class<?> standardSubType(final Class<?>[] subtypes) {
+            Class<?> type = null;
+            for (Class<?> c : subtypes) {
+                if (baseType.isAssignableFrom(c)) {
+                    if (!isMetadata(c)) {
+                        c = standardSubType(c.getInterfaces());
+                    }
+                    if (type == null) {
+                        type = c;
+                    } else if (type != c) {
+                        return Void.TYPE;
+                    }
+                }
+            }
+            return type;
         }
 
         /**
@@ -592,19 +653,14 @@ class TreeNode implements Node {
             cachedValue = null;             // Use the cached value only once after iteration.
             /*
              * If there is a value, check if the cached collection is still applicable.
+             * We verify that the collection is a wrapper for the same metadata object.
+             * If we need to create a new collection, we know that the property accessor
+             * exists otherwise the call to 'isLeaf()' above would have returned 'true'.
              */
-            if (children instanceof TreeNodeChildren) {
-                final TreeNodeChildren candidate = (TreeNodeChildren) children;
-                if (candidate.metadata == value) {
-                    return candidate;
-                }
+            if (children == null || ((TreeNodeChildren) children).metadata != value) {
+                children = new TreeNodeChildren(this, value,
+                        table.standard.getAccessor(new CacheKey(value.getClass(), baseType), true));
             }
-            /*
-             * At this point, we need to create a new collection. The property accessor shall
-             * exist, otherwise the call to 'isLeaf()' above would have returned 'true'.
-             */
-            children = new TreeNodeChildren(this, value,
-                    table.standard.getAccessor(new CacheKey(value.getClass(), baseType), true));
         }
         return children;
     }
@@ -712,8 +768,10 @@ class TreeNode implements Node {
                         throw new IllegalArgumentException(Errors.format(Errors.Keys.ElementAlreadyPresent_1, value));
                     }
                     delegate = siblings.childAt(indexInData, indexInList);
-                    // Do not set 'delegate.cachedValue = value', since 'value' may
-                    // have been converted by the setter method to an other value.
+                    /*
+                     * Do not set 'delegate.cachedValue = value', since 'value' may
+                     * have been converted by the setter method to another value.
+                     */
                     return;
                 }
             }
@@ -736,36 +794,54 @@ class TreeNode implements Node {
     }
 
     /**
+     * Returns the children if the value policy is {@link ValueExistencePolicy#COMPACT}, or {@code null} otherwise.
+     */
+    private TreeNodeChildren getCompactChildren() {
+        if (table.valuePolicy == ValueExistencePolicy.COMPACT) {
+            final Collection<Node> children = getChildren();
+            if (children instanceof TreeNodeChildren) {
+                return (TreeNodeChildren) children;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the value of this node in the given column, or {@code null} if none. This method verifies
      * the {@code column} argument, then delegates to {@link #getName()}, {@link #getUserObject()} or
      * other properties.
      */
     @Override
     public final <V> V getValue(final TableColumn<V> column) {
-        ArgumentChecks.ensureNonNull("column", column);
         Object value = null;
-
-        // Check the columns in what we think may be the most frequently
-        // asked columns first, and less frequently asked columns last.
-        if (column == TableColumn.VALUE) {
+        ArgumentChecks.ensureNonNull("column", column);
+        if (column == TableColumn.IDENTIFIER) {
+            value = getIdentifier();
+        } else if (column == TableColumn.INDEX) {
+            value = getIndex();
+        } else if (column == TableColumn.NAME) {
+            if (name == null) {
+                name = getName();
+            }
+            value = name;
+        } else if (column == TableColumn.TYPE) {
+            final TreeNodeChildren children = getCompactChildren();
+            if (children == null || (value = children.getParentType()) == null) {
+                value = baseType;
+            }
+        } else if (column == TableColumn.VALUE) {
             if (isLeaf()) {
                 value = cachedValue;
                 cachedValue = null;                 // Use the cached value only once after iteration.
                 if (value == null) {
                     value = getUserObject();
                 }
+            } else {
+                final TreeNodeChildren children = getCompactChildren();
+                if (children != null) {
+                    value = children.getParentTitle();
+                }
             }
-        } else if (column == TableColumn.NAME) {
-            if (name == null) {
-                name = getName();
-            }
-            value = name;
-        } else if (column == TableColumn.IDENTIFIER) {
-            value = getIdentifier();
-        } else if (column == TableColumn.INDEX) {
-            value = getIndex();
-        } else if (column == TableColumn.TYPE) {
-            value = baseType;
         }
         return column.getElementType().cast(value);
     }
@@ -783,9 +859,12 @@ class TreeNode implements Node {
     public final <V> void setValue(final TableColumn<V> column, final V value) throws UnsupportedOperationException {
         ArgumentChecks.ensureNonNull("column", column);
         if (column == TableColumn.VALUE) {
-            ArgumentChecks.ensureNonNull("value", value);
+            ArgumentChecks.ensureNonNull("value", value);                       // See javadoc.
             cachedValue = null;
-            setUserObject(value);
+            final TreeNodeChildren children = getCompactChildren();
+            if (children == null || !(children.setParentTitle(value))) {
+                setUserObject(value);
+            }
         } else if (TreeTableView.COLUMNS.contains(column)) {
             throw new UnsupportedOperationException(unmodifiableCellValue(column));
         } else {

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNodeChildren.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNodeChildren.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNodeChildren.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNodeChildren.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -98,6 +98,30 @@ final class TreeNodeChildren extends Abs
     private final TreeNode[] children;
 
     /**
+     * Index of the property to write in the parent node instead than as a child.
+     * If a property has the same name than the parent property that contains it,
+     * we write its value in that parent property. For example instead of:
+     *
+     * {@preformat text
+     *   Citation
+     *    └─Date
+     *       ├─Date………………… 2012/01/01
+     *       └─Date type…… Creation
+     * }
+     *
+     * We simplify as:
+     *
+     * {@preformat text
+     *   Citation
+     *    └─Date………………………… 2012/01/01
+     *       └─Date type…… Creation
+     * }
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-298">SIS-298</a>
+     */
+    final int titleProperty;
+
+    /**
      * Modification count, incremented when the content of this collection is modified. This check
      * is done on a <cite>best effort basis</cite> only, since we can't not track the changes which
      * are done independently in the {@linkplain #metadata} object.
@@ -116,6 +140,57 @@ final class TreeNodeChildren extends Abs
         this.metadata = metadata;
         this.accessor = accessor;
         this.children = new TreeNode[accessor.count()];
+        /*
+         * Search for something that looks like the main property, to be associated with the parent node
+         * instead than provided as a child. The intend is to have more compact and easy to read trees.
+         * That property shall be a singleton for a simple value (not another metadata object).
+         */
+        if (parent.table.valuePolicy == ValueExistencePolicy.COMPACT) {
+            TitleProperty an = accessor.implementation.getAnnotation(TitleProperty.class);
+            if (an == null) {
+                Class<?> implementation = parent.table.standard.getImplementation(accessor.type);
+                if (implementation != null) {
+                    an = implementation.getAnnotation(TitleProperty.class);
+                }
+            }
+            if (an != null) {
+                final int index = accessor.indexOf(an.name(), false);
+                final Class<?> type = accessor.type(index, TypeValuePolicy.ELEMENT_TYPE);
+                if (type != null && !parent.isMetadata(type) && type == accessor.type(index, TypeValuePolicy.PROPERTY_TYPE)) {
+                    titleProperty = index;
+                    return;
+                }
+            }
+        }
+        titleProperty = -1;
+    }
+
+    /**
+     * If a simple value should be associated to the parent node, returns the type of that value.
+     * Otherwise returns {@code null}.
+     */
+    final Class<?> getParentType() {
+        return (titleProperty >= 0) ? accessor.type(titleProperty, TypeValuePolicy.ELEMENT_TYPE) : null;
+    }
+
+    /**
+     * If a simple value should be associated to the parent node, returns that value.
+     * Otherwise returns {@code null}.
+     */
+    final Object getParentTitle() {
+        return (titleProperty >= 0) ? valueAt(titleProperty) : null;
+    }
+
+    /**
+     * Sets the value associated to the parent node, if possible.
+     * This returned boolean tells whether the value has been written.
+     */
+    final boolean setParentTitle(final Object value) {
+        if (titleProperty < 0) {
+            return false;
+        }
+        accessor.set(titleProperty, metadata, value, PropertyAccessor.RETURN_NULL);
+        return true;
     }
 
     /**
@@ -229,7 +304,9 @@ final class TreeNodeChildren extends Abs
      */
     @Override
     public int size() {
-        return accessor.count(metadata, parent.table.valuePolicy, PropertyAccessor.COUNT_DEEP);
+        int count = accessor.count(metadata, parent.table.valuePolicy, PropertyAccessor.COUNT_DEEP);
+        if (titleProperty >= 0 && !isSkipped(valueAt(titleProperty))) count--;
+        return count;
     }
 
     /**
@@ -310,11 +387,9 @@ final class TreeNodeChildren extends Abs
         private Object nextValue;
 
         /**
-         * If the call to {@link #next()} found a collection, the iterator over the elements
-         * in that collection. Otherwise {@code null}.
-         *
-         * <p>A non-null value (even if that sub-iterator has no next elements)
-         * means that {@link #nextValue} is an element of that sub-iteration.</p>
+         * If the call to {@link #next()} found a collection, the iterator over the elements in that collection.
+         * Otherwise {@code null}. A non-null value (even if that sub-iterator has no next elements) means that
+         * {@link #nextValue} is an element of that sub-iteration.
          */
         private Iterator<?> subIterator;
 
@@ -385,38 +460,40 @@ final class TreeNodeChildren extends Abs
              */
             final int count = childCount();
             while (nextInAccessor < count) {
-                nextValue = valueAt(nextInAccessor);
-                if (!isSkipped(nextValue)) {
-                    if (isCollection(nextInAccessor)) {
-                        /*
-                         * If the property is a collection, unconditionally get the first element
-                         * even if absent (null) in order to comply with the ValueExistencePolicy.
-                         * if we were expected to ignore empty collections, 'isSkipped(nextValue)'
-                         * would have returned 'true'.
-                         */
-                        if (nextValue != null) {
-                            subIterator = ((Iterable<?>) nextValue).iterator();
-                        } else {
-                            subIterator = Collections.emptyIterator();
+                if (nextInAccessor != titleProperty) {
+                    nextValue = valueAt(nextInAccessor);
+                    if (!isSkipped(nextValue)) {
+                        if (isCollection(nextInAccessor)) {
                             /*
-                             * Null collections are illegal (it shall be empty collections instead),
-                             * but we try to keep the iterator robut to ill-formed metadata, because
-                             * we want AbstractMetadata.toString() to work so we can spot problems.
-                             */
-                        }
-                        subIndex = 0;
-                        if (subIterator.hasNext()) {
-                            nextValue = subIterator.next();
-                        } else {
-                            nextValue = null;
-                            /*
-                             * Do not set 'childIterator' to null, since the above 'nextValue'
-                             * is considered as part of the child iteration.
+                             * If the property is a collection, unconditionally get the first element
+                             * even if absent (null) in order to comply with the ValueExistencePolicy.
+                             * if we were expected to ignore empty collections, 'isSkipped(nextValue)'
+                             * would have returned 'true'.
                              */
+                            if (nextValue != null) {
+                                subIterator = ((Iterable<?>) nextValue).iterator();
+                            } else {
+                                subIterator = Collections.emptyIterator();
+                                /*
+                                 * Null collections are illegal (it shall be empty collections instead),
+                                 * but we try to keep the iterator robut to ill-formed metadata, because
+                                 * we want AbstractMetadata.toString() to work so we can spot problems.
+                                 */
+                            }
+                            subIndex = 0;
+                            if (subIterator.hasNext()) {
+                                nextValue = subIterator.next();
+                            } else {
+                                nextValue = null;
+                                /*
+                                 * Do not set 'childIterator' to null, since the above 'nextValue'
+                                 * is considered as part of the child iteration.
+                                 */
+                            }
                         }
+                        isNextVerified = true;
+                        return true;
                     }
-                    isNextVerified = true;
-                    return true;
                 }
                 nextInAccessor++;
             }

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeTableView.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeTableView.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeTableView.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeTableView.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -104,7 +104,7 @@ final class TreeTableView implements Tre
     {
         this.standard    = standard;
         this.valuePolicy = valuePolicy;
-        this.root = new TreeNode(this, metadata, baseType);
+        this.root        = new TreeNode(this, metadata, baseType);
     }
 
     /**
@@ -113,7 +113,7 @@ final class TreeTableView implements Tre
     @Override
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     public List<TableColumn<?>> getColumns() {
-        return COLUMNS;     // Unmodifiable
+        return COLUMNS;                                 // Unmodifiable
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/ValueExistencePolicy.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/ValueExistencePolicy.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/ValueExistencePolicy.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/ValueExistencePolicy.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -38,7 +38,7 @@ import org.apache.sis.xml.NilReason;
  * Those explanations can be obtained by calls to the {@link org.apache.sis.xml.NilReason#forObject(Object)} method.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.4
+ * @version 0.8
  *
  * @see MetadataStandard#asValueMap(Object, Class, KeyNamePolicy, ValueExistencePolicy)
  *
@@ -122,6 +122,59 @@ public enum ValueExistencePolicy {
         /** Skips all null or empty values. */
         @Override boolean isSkipped(final Object value) {
             return isNullOrEmpty(value);
+        }
+
+        /** Never substitute null or empty collections since they should be skipped. */
+        @Override boolean substituteByNullElement(final Collection<?> values) {
+            return false;
+        }
+    },
+
+    /**
+     * Includes non-empty properties but omits {@linkplain TitleProperty title properties}.
+     * Values associated to title properties are instead associated with the parent node.
+     * This policy is relevant for metadata classes annotated with {@link TitleProperty};
+     * for all other classes, this policy is identical to {@link #NON_EMPTY}.
+     *
+     * <div class="note"><b>Example:</b>
+     * the {@link org.apache.sis.metadata.iso.citation.DefaultCitation} and
+     * {@link org.apache.sis.metadata.iso.citation.DefaultCitationDate} classes are annotated with
+     * <code>&#64;TitleProperty(name="title")</code> and <code>&#64;TitleProperty(name="date")</code>
+     * respectively. The following table compares the trees produced by two policies:
+     *
+     * <table class="sis">
+     *   <caption>Comparison of "non-empty" and "compact" policy on the same metadata</caption>
+     *   <tr>
+     *     <th>{@code NON_EMPTY}</th>
+     *     <th class="sep">{@code COMPACT}</th>
+     *   </tr><tr><td>
+     *     {@preformat text
+     *       Citation
+     *        ├─Title……………………… My document
+     *        └─Date
+     *           ├─Date………………… 2012/01/01
+     *           └─Date type…… Creation
+     *     }
+     *   </td><td class="sep">
+     *     {@preformat text
+     *       Citation……………………… My document
+     *        └─Date………………………… 2012/01/01
+     *           └─Date type…… Creation
+     *     }
+     *   </td></tr>
+     * </table></div>
+     *
+     * This policy is the default behavior of {@link AbstractMetadata#asTreeTable()},
+     * and consequently defines the default rendering of {@link AbstractMetadata#toString()}.
+     *
+     * @see TitleProperty
+     *
+     * @since 0.8
+     */
+    COMPACT() {
+        /** Skips all null or empty values. */
+        @Override boolean isSkipped(final Object value) {
+            return isNullOrEmpty(value);
         }
 
         /** Never substitute null or empty collections since they should be skipped. */

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultApplicationSchemaInformation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultApplicationSchemaInformation.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultApplicationSchemaInformation.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultApplicationSchemaInformation.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -26,6 +26,14 @@ import org.opengis.metadata.citation.Onl
 
 /**
  * Information about the application schema used to build the dataset.
+ * The following properties are mandatory in a well-formed metadata according ISO 19115:
+ *
+ * <div class="preformat">{@code MD_ApplicationSchemaInformation}
+ * {@code   ├─name……………………………………………………………} Name of the application schema used.
+ * {@code   │   ├─title………………………………………………} Name by which the cited resource is known.
+ * {@code   │   └─date…………………………………………………} Reference date for the cited resource.
+ * {@code   ├─schemaLanguage…………………………………} Identification of the schema language used.
+ * {@code   └─constraintLanguage………………………} Formal language used in Application Schema.</div>
  *
  * <p><b>Limitations:</b></p>
  * <ul>

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultExtendedElementInformation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultExtendedElementInformation.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultExtendedElementInformation.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultExtendedElementInformation.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -27,8 +27,10 @@ import org.opengis.metadata.Datatype;
 import org.opengis.metadata.citation.Responsibility;
 import org.opengis.metadata.ExtendedElementInformation;
 import org.opengis.util.InternationalString;
+import org.apache.sis.metadata.TitleProperty;
 import org.apache.sis.measure.ValueRange;
 import org.apache.sis.util.iso.Types;
+import org.apache.sis.internal.metadata.Dependencies;
 import org.apache.sis.internal.metadata.LegacyPropertyAdapter;
 
 import static org.apache.sis.internal.metadata.MetadataUtilities.ensurePositive;
@@ -37,6 +39,23 @@ import static org.apache.sis.internal.me
 /**
  * New metadata element, not found in ISO 19115, which is required to describe geographic data.
  * Metadata elements are contained in a {@linkplain DefaultMetadataExtensionInformation metadata extension information}.
+ * The following properties are mandatory or conditional (i.e. mandatory under some circumstances)
+ * in a well-formed metadata according ISO 19115:
+ *
+ * <div class="preformat">{@code MD_ExtendedElementInformation}
+ * {@code   ├─name………………………………………………………} Name of the extended metadata element.
+ * {@code   ├─definition………………………………………} Definition of the extended element.
+ * {@code   ├─obligation………………………………………} Obligation of the extended element.
+ * {@code   ├─condition…………………………………………} Condition under which the extended element is mandatory.
+ * {@code   ├─dataType……………………………………………} Code which identifies the kind of value provided in the extended element.
+ * {@code   ├─maximumOccurrence……………………} Maximum occurrence of the extended element.
+ * {@code   ├─domainValue……………………………………} Valid values that can be assigned to the extended element.
+ * {@code   ├─parentEntity…………………………………} Name of the metadata entity(s) under which this extended metadata element may appear.
+ * {@code   ├─rule………………………………………………………} Specifies how the extended element relates to other existing elements and entities.
+ * {@code   └─source…………………………………………………} Name of the person or organisation creating the extended element.
+ * {@code       ├─party…………………………………………} Information about the parties.
+ * {@code       │   └─name…………………………………} Name of the party.
+ * {@code       └─role……………………………………………} Function performed by the responsible party.</div>
  *
  * <p><b>Limitations:</b></p>
  * <ul>
@@ -55,6 +74,7 @@ import static org.apache.sis.internal.me
  * @module
  */
 @SuppressWarnings("CloneableClassWithoutClone")                 // ModifiableMetadata needs shallow clones.
+@TitleProperty(name = "name")
 @XmlType(name = "MD_ExtendedElementInformation_Type", propOrder = {
     "name",
     "shortName",
@@ -543,6 +563,7 @@ public class DefaultExtendedElementInfor
     @Override
     @Deprecated
     @XmlElement(name = "rationale")
+    @Dependencies("getRationale")
     public Collection<InternationalString> getRationales() {
         return new AbstractSet<InternationalString>() {
             /** Returns 0 if empty, or 1 if a density has been specified. */

Modified: sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultFeatureTypeList.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultFeatureTypeList.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultFeatureTypeList.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultFeatureTypeList.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -24,6 +24,11 @@ import org.opengis.metadata.FeatureTypeL
 
 /**
  * List of names of feature types with the same spatial representation (same as spatial attributes).
+ * The following properties are mandatory in a well-formed metadata according ISO 19115:
+ *
+ * <div class="preformat">{@code MD_FeatureTypeList}
+ * {@code   ├─spatialObject………………} Instance of a type defined in the spatial schema.
+ * {@code   └─spatialSchemaName……} Name of the spatial schema used.</div>
  *
  * <p><b>Limitations:</b></p>
  * <ul>



Mime
View raw message