sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Introduce a new syntax in "RenameOnImport.lst" file for declaring that a class inherit the properties of another class. It make the file shorter and reduce the amount of hack with namespaces. But the main intent is to make easier to extend with profile. This work allow us to fix https://issues.apache.org/jira/browse/SIS-404
Date Tue, 09 Oct 2018 17:35:44 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new b81a8f5  Introduce a new syntax in "RenameOnImport.lst" file for declaring that a class inherit the properties of another class. It make the file shorter and reduce the amount of hack with namespaces. But the main intent is to make easier to extend with profile. This work allow us to fix https://issues.apache.org/jira/browse/SIS-404
b81a8f5 is described below

commit b81a8f5a94799bac67588de02e502a00e924f111
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Oct 9 19:33:25 2018 +0200

    Introduce a new syntax in "RenameOnImport.lst" file for declaring that a class inherit the properties of another class.
    It make the file shorter and reduce the amount of hack with namespaces. But the main intent is to make easier to extend with profile.
    This work allow us to fix https://issues.apache.org/jira/browse/SIS-404
---
 .../apache/sis/internal/jaxb/TypeRegistration.java | 133 +++++---
 .../sis/internal/metadata/MetadataTypes.java       |  13 +-
 .../java/org/apache/sis/xml/MarshallerPool.java    |   2 +-
 .../src/main/java/org/apache/sis/xml/Pooled.java   |  10 +-
 .../java/org/apache/sis/xml/PooledMarshaller.java  |  10 +-
 .../main/java/org/apache/sis/xml/Transformer.java  | 159 +++++----
 .../org/apache/sis/xml/TransformingReader.java     |  15 +-
 .../org/apache/sis/xml/TransformingWriter.java     |   8 +-
 .../src/main/java/org/apache/sis/xml/readme.html   |  74 +++++
 .../org/apache/sis/xml/RenameOnExport.lst          |  15 +-
 .../org/apache/sis/xml/RenameOnImport.lst          | 354 +++------------------
 .../org/apache/sis/xml/RenameListGenerator.java    |  19 +-
 .../sis/internal/referencing/ReferencingTypes.java |  16 +-
 .../sis/internal/system/DelayedExecutor.java       |   2 +-
 .../sis/internal/system/DelayedRunnable.java       |  14 +-
 ide-project/NetBeans/build.xml                     |   3 +
 .../sis/internal/profile/fra/ProfileTypes.java     |  14 +-
 .../sis/internal/profile/fra/package-info.java     |   2 +-
 .../sis/internal/profile/fra/RenameOnImport.lst    |  10 +
 .../profile/fra/DataIdentificationTest.java        |   2 -
 20 files changed, 414 insertions(+), 461 deletions(-)

diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
index a0c0624..e3a4bb3 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
@@ -16,11 +16,15 @@
  */
 package org.apache.sis.internal.jaxb;
 
+import java.util.Set;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.ServiceLoader;
+import java.util.concurrent.TimeUnit;
+import java.util.function.UnaryOperator;
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 import javax.xml.bind.JAXBContext;
@@ -28,6 +32,8 @@ import javax.xml.bind.JAXBException;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.system.SystemListener;
 import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.system.DelayedExecutor;
+import org.apache.sis.internal.system.DelayedRunnable;
 
 
 /**
@@ -42,7 +48,7 @@ import org.apache.sis.internal.system.DefaultFactories;
  * }
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  *
  * @see org.apache.sis.xml.MarshallerPool
  *
@@ -52,9 +58,9 @@ import org.apache.sis.internal.system.DefaultFactories;
 public abstract class TypeRegistration {
     /**
      * Undocumented (for now) marshaller property for specifying conversions to apply on root objects
-     * before marshalling. Conversions are applied by the {@link #toImplementation(Object)} method.
+     * before marshalling. Conversions are applied by {@link UnaryOperator} instances.
      *
-     * @see #addDefaultRootAdapters(Map)
+     * @see #getPrivateInfo(Map)
      */
     public static final String ROOT_ADAPTERS = "org.apache.sis.xml.rootAdapters";
 
@@ -66,12 +72,20 @@ public abstract class TypeRegistration {
     private static Reference<JAXBContext> context;
 
     /**
-     * The {@link TypeRegistration} instances found on the classpath for which the
-     * {@link #toImplementation(Object)} method has been overridden.
+     * Converters to apply before to marshal an object, or an empty array if none.
+     * This is {@code null} if not yet initialized or if classpath changed.
      *
-     * @see #addDefaultRootAdapters(Map)
+     * @see #getPrivateInfo(Map)
      */
-    private static TypeRegistration[] converters;
+    private static UnaryOperator<Object>[] converters;
+
+    /**
+     * The registrations, cached only a few seconds. We do not need to keep them a long time
+     * because {@link TypeRegistration} is used only for initializing some classes or fields.
+     *
+     * @see #services()
+     */
+    private static ServiceLoader<TypeRegistration> services;
 
     /**
      * Forces reloading of JAXB context and converters if the classpath changes.
@@ -82,6 +96,7 @@ public abstract class TypeRegistration {
                 synchronized (TypeRegistration.class) {
                     context    = null;
                     converters = null;
+                    services   = null;
                 }
             }
         });
@@ -97,75 +112,91 @@ public abstract class TypeRegistration {
      * Adds to the given collection every types that should be given to the initial JAXB context.
      * The types added by this method include only implementation classes having JAXB annotations.
      * If the module can also marshal arbitrary implementations of some interfaces (e.g. GeoAPI),
-     * then the {@link #canMarshalInterfaces()} method should be overridden.
+     * then the {@link #beforeMarshal()} method should be overridden.
      *
      * @param  addTo  the collection in which to add new types.
      */
     protected abstract void getTypes(final Collection<Class<?>> addTo);
 
     /**
-     * Returns {@code true} if the module can also marshal arbitrary implementation of some interfaces.
-     * If this method returns {@code true}, then the {@link #toImplementation(Object)} method shall be
-     * overridden.
+     * If some objects need to be converted before marshalling, the converter for performing those conversions.
+     * The converter {@code apply(Object)} method may return {@code null} if the value class is not recognized,
+     * or {@code value} if the class is recognized but the value does not need to be changed.
+     * Implementations will typically perform an {@code instanceof} check, then invoke one
+     * of the {@code castOrCopy(…)} static methods defined in various Apache SIS classes.
      *
-     * @return whether the module can also marshal arbitrary implementation of some interfaces.
+     * @return converter for the value to marshal, or {@code null} if there is no value to convert.
      *
      * @since 0.8
      */
-    protected boolean canMarshalInterfaces() {
-        return false;
+    protected UnaryOperator<Object> beforeMarshal() {
+        return null;
     }
 
     /**
-     * If the given value needs to be converted before marshalling, apply the conversion now.
-     * Otherwise returns {@code null} if the value class is not recognized, or {@code value}
-     * if the class is recognized but the value does not need to be changed.
-     *
-     * <p>Subclasses that override this method will typically perform an {@code instanceof} check, then
-     * invoke one of the {@code castOrCopy(…)} static methods defined in various Apache SIS classes.</p>
+     * Returns {@code true} if {@code "RenameOnImport.lst"} and/or {@code "RenameOnExport.lst"} files are provided.
+     * If {@code true}, then those files shall be located in the same directory than this {@code TypeRegistration}
+     * subclass.
      *
-     * <p>This method is invoked only if {@link #canMarshalInterfaces()} returns {@code true}.</p>
-     *
-     * @param  value  the value to convert before marshalling.
-     * @return the value to marshall; or {@code null} if this method does not recognize the value class.
-     * @throws JAXBException if an error occurred while converting the given object.
+     * @param  export  {@code true} for {@code "RenameOnImport.lst"}, {@code false} for {@code "RenameOnImport.lst"}.
+     * @return whether {@code "RenameOnImport.lst"} and/or {@code "RenameOnExport.lst"} files are provided.
+     */
+    protected boolean hasRenameFile(boolean export) {
+        return false;
+    }
+
+    /**
+     * Adds in the given set the classes to use for loading  {@code "RenameOnImport.lst"} and/or {@code "RenameOnExport.lst"} files.
+     * The given set should preserve insertion order, since the order in which files are loaded may matter.
      *
-     * @since 0.8
+     * @param  export  {@code true} for {@code "RenameOnImport.lst"}, {@code false} for {@code "RenameOnImport.lst"}.
+     * @param  addTo   where to add the classes to use for loading the resource files.
      */
-    public Object toImplementation(final Object value) throws JAXBException {
-        return null;
+    public static synchronized void getRenameFileLoader(final boolean export, final Set<Class<?>> addTo) {
+        for (final TypeRegistration t : services()) {
+            if (t.hasRenameFile(export)) {
+                addTo.add(t.getClass());
+            }
+        }
+    }
+
+    /**
+     * Returns the {@code TypeRegistration} instances.
+     * Must be invoked in a synchronized block.
+     */
+    private static ServiceLoader<TypeRegistration> services() {
+        ServiceLoader<TypeRegistration> s = services;
+        if (s == null) {
+            services = s = DefaultFactories.createServiceLoader(TypeRegistration.class);
+            DelayedExecutor.schedule(new DelayedRunnable(1, TimeUnit.MINUTES) {
+                @Override public void run() {services = null;}
+            });
+        }
+        return s;
     }
 
     /**
      * Scans the classpath for root classes to put in JAXB context and for converters to those classes.
      * Those lists are determined dynamically from the SIS modules found on the classpath.
-     * The list of root classes is created only if the {@code getTypes} argument is {@code true}.
+     * This method does nothing if this class already has all required information.
+     *
+     * <p>The list of converters is cached (that result is stored in {@link #converters}) while the list
+     * of root classes in JAXB context is not cached. So the information about whether this method needs
+     * to fetch the list of root classes or not must be specified by the {@code getTypes} argument.</p>
      *
      * @param  getTypes  whether to get the root classes to put in JAXB context (may cause class loading).
      * @return if {@code getTypes} was {@code true}, the root classes to be bound in {@code JAXBContext}.
      */
+    @SuppressWarnings({"unchecked", "rawtypes"})
     private static Class<?>[] load(final boolean getTypes) {
-        /*
-         * Implementation note: do not keep the ServiceLoader in static field because:
-         *
-         * 1) It would cache more TypeRegistration instances than needed for this method call.
-         * 2) The ClassLoader between different invocations may be different in an OSGi context.
-         */
-        final ArrayList<Class<?>> types = new ArrayList<>();
-        final ArrayList<TypeRegistration> toImpl = (converters == null) ? new ArrayList<>() : null;
-        if (toImpl != null || getTypes) {
-            for (final TypeRegistration t : DefaultFactories.createServiceLoader(TypeRegistration.class)) {
-                if (getTypes) {
-                    t.getTypes(types);
-                }
-                if (toImpl != null && t.canMarshalInterfaces()) {
-                    toImpl.add(t);
-                }
-            }
-            if (toImpl != null) {
-                converters = toImpl.toArray(new TypeRegistration[toImpl.size()]);
-            }
+        final ArrayList<Class<?>>              types  = new ArrayList<>();
+        final ArrayList<UnaryOperator<Object>> toImpl = new ArrayList<>();
+        for (final TypeRegistration t : services()) {
+            if (getTypes) t.getTypes(types);
+            final UnaryOperator<Object> c = t.beforeMarshal();
+            if (c != null) toImpl.add(c);
         }
+        converters = toImpl.toArray(new UnaryOperator[toImpl.size()]);
         return types.toArray(new Class<?>[types.size()]);
     }
 
@@ -205,11 +236,11 @@ public abstract class TypeRegistration {
      *
      * @since 0.8
      */
-    public static Map<String,?> addDefaultRootAdapters(final Map<String,?> properties) {
+    public static Map<String,?> getPrivateInfo(final Map<String,?> properties) {
         if (properties != null && properties.containsKey(ROOT_ADAPTERS)) {
             return properties;
         }
-        TypeRegistration[] c;
+        UnaryOperator<Object>[] c;
         synchronized (TypeRegistration.class) {
             c = converters;
             if (c == null) {
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataTypes.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataTypes.java
index baec5da..67a212d 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataTypes.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataTypes.java
@@ -17,6 +17,7 @@
 package org.apache.sis.internal.metadata;
 
 import java.util.Collection;
+import java.util.function.UnaryOperator;
 import org.opengis.metadata.Metadata;
 import org.opengis.metadata.content.Band;
 import org.opengis.metadata.content.ImageDescription;
@@ -47,7 +48,7 @@ import org.opengis.metadata.spatial.Georeferenceable;
  * @since   0.3
  * @module
  */
-public final class MetadataTypes extends TypeRegistration {
+public final class MetadataTypes extends TypeRegistration implements UnaryOperator<Object> {
     /**
      * Adds to the given collection the metadata types that should be given to the initial JAXB context.
      */
@@ -58,13 +59,13 @@ public final class MetadataTypes extends TypeRegistration {
     }
 
     /**
-     * Notifies that the {@code sis-metadata} module can marshal arbitrary implementations of some metadata interfaces.
+     * Returns the converter to apply before marshalling objects.
      *
-     * @return {@code true}.
+     * @return {@code this}.
      */
     @Override
-    protected boolean canMarshalInterfaces() {
-        return true;
+    protected UnaryOperator<Object> beforeMarshal() {
+        return this;
     }
 
     /**
@@ -78,7 +79,7 @@ public final class MetadataTypes extends TypeRegistration {
      * @return the given value as a type that can be marshalled, or {@code null}.
      */
     @Override
-    public Object toImplementation(final Object value) {
+    public Object apply(final Object value) {
         /*
          * Classes that are most likely to be used should be checked first.  If a type is a specialization
          * of another type (e.g. ImageDescription extends CoverageDescription), the specialized type shall
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/MarshallerPool.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/MarshallerPool.java
index 6d3e9ae..a35a90c 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/MarshallerPool.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/MarshallerPool.java
@@ -165,7 +165,7 @@ public class MarshallerPool {
          * We presume that if the user specified his own JAXBContext, then he does not expect us to change the
          * classes that he wants to marshal.
          */
-        this(TypeRegistration.getSharedContext(), TypeRegistration.addDefaultRootAdapters(properties));
+        this(TypeRegistration.getSharedContext(), TypeRegistration.getPrivateInfo(properties));
     }
 
     /**
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/Pooled.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/Pooled.java
index 3bf4389..e468a6b 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/Pooled.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/Pooled.java
@@ -23,6 +23,7 @@ import java.util.ConcurrentModificationException;
 import java.util.IllformedLocaleException;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.function.UnaryOperator;
 import javax.xml.validation.Schema;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.JAXBException;
@@ -146,7 +147,7 @@ abstract class Pooled {
      *
      * @see #getRootAdapters()
      */
-    private TypeRegistration[] rootAdapters;
+    private UnaryOperator<Object>[] rootAdapters;
 
     /**
      * The object to inform about warnings, or {@code null} if none.
@@ -381,8 +382,9 @@ abstract class Pooled {
                     return;
                 }
                 case TypeRegistration.ROOT_ADAPTERS: {
-                    rootAdapters = (TypeRegistration[]) value;
-                    // No clone for now because ROOT_ADAPTERS is not yet a public API.
+                    @SuppressWarnings("unchecked")
+                    UnaryOperator<Object>[] c = (UnaryOperator<Object>[]) value;
+                    rootAdapters = c;       // No clone for now because ROOT_ADAPTERS is not yet a public API.
                     return;
                 }
             }
@@ -487,7 +489,7 @@ abstract class Pooled {
      * @return a direct reference to the internal array of converters - do not modify.
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
-    final TypeRegistration[] getRootAdapters() {
+    final UnaryOperator<Object>[] getRootAdapters() {
         return rootAdapters;
     }
 
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/PooledMarshaller.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/PooledMarshaller.java
index 55027e4..b212908 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/PooledMarshaller.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/PooledMarshaller.java
@@ -22,6 +22,7 @@ import java.io.OutputStream;
 import java.io.FileOutputStream;
 import java.io.BufferedOutputStream;
 import java.io.IOException;
+import java.util.function.UnaryOperator;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.PropertyException;
@@ -36,7 +37,6 @@ import javax.xml.validation.Schema;
 import org.xml.sax.ContentHandler;
 import org.w3c.dom.Node;
 import org.apache.sis.internal.jaxb.Context;
-import org.apache.sis.internal.jaxb.TypeRegistration;
 import org.apache.sis.internal.jaxb.UseLegacyMetadata;
 
 
@@ -138,12 +138,12 @@ final class PooledMarshaller extends Pooled implements Marshaller {
      * If the given object is not recognized or is already an instance of the expected class,
      * then it is returned unchanged.
      */
-    private Object toImplementation(final Object value) throws JAXBException {
+    private Object toImplementation(final Object value) {
         specificBitMasks = value.getClass().isAnnotationPresent(UseLegacyMetadata.class) ? Context.LEGACY_METADATA : 0;
-        final TypeRegistration[] converters = getRootAdapters();
+        final UnaryOperator<Object>[] converters = getRootAdapters();
         if (converters != null) {
-            for (final TypeRegistration t : converters) {
-                final Object c = t.toImplementation(value);
+            for (final UnaryOperator<Object> t : converters) {
+                final Object c = t.apply(value);
                 if (c != null) return c;
             }
         }
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java
index 705ccdb..0c49150 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java
@@ -18,14 +18,18 @@ package org.apache.sis.xml;
 
 import java.util.Map;
 import java.util.HashMap;
+import java.util.Set;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.NoSuchElementException;
 import java.util.InvalidPropertiesFormatException;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.LineNumberReader;
+import java.util.function.Function;
 import javax.xml.XMLConstants;
 import javax.xml.namespace.QName;
 import javax.xml.stream.XMLStreamException;
@@ -34,6 +38,7 @@ import javax.xml.stream.events.Namespace;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.internal.jaxb.TypeRegistration;
 
 
 /**
@@ -98,7 +103,15 @@ abstract class Transformer {
     private static final char RENAME_SEPARATOR = '/';
 
     /**
-     * A flag after type name in files loaded by {@link #load(String, int)}, meaning that the type itself
+     * Character used for separating a class name from the parent class name. When the {@code Child : Parent} syntax
+     * is used, the child inherits all properties defined in the parent. The parent class must be defined before the
+     * child class (no forward reference). We do not store the relationship between the two classes, so it is not
+     * necessary to extend a parent that define no property.
+     */
+    private static final char EXTENDS = ':';
+
+    /**
+     * A flag after type name in files loaded by {@link #load(boolean, String, int)}, meaning that the type itself
      * is in a different namespace than the properties listed below the type. For example in the following:
      *
      * {@preformat text
@@ -110,8 +123,9 @@ abstract class Transformer {
      *
      * {@code SV_ServiceIdentification} type is defined in the {@code "http://standards.iso.org/iso/19115/-3/srv/2.0"}
      * namespace, but the {@code citation} and {@code abstract} properties inherited from {@code Identification} are
-     * defined in the {@code http://standards.iso.org/iso/19115/-3/mri/1.0} namespace. If the {@value} flag is not
-     * present, then the type is assumed in the same namespace than the properties (this is the most common case).
+     * defined in the {@code http://standards.iso.org/iso/19115/-3/mri/1.0} namespace (note: using {@link #EXTENDS}
+     * is a better way to achieve the same result for this particular example). If the {@value} flag is not present,
+     * then the type is assumed in the same namespace than the properties (this is the most common case).
      */
     static final char NO_NAMESPACE = '!';
 
@@ -184,8 +198,8 @@ abstract class Transformer {
     /**
      * Returns {@code true} if the given string is a namespace URI, or {@code false} if it is a property name.
      * This method implements a very fast check based on the presence of {@code ':'} in {@code "http://foo.bar"}.
-     * It assumes that all namespaces declared in files loaded by {@link #load(String, int)} use the {@code "http"}
-     * protocol and no property name use the {@code ':'} character.
+     * It assumes that all namespaces declared in files loaded by {@link #load(boolean, String, int)} use the
+     * {@code "http"} protocol and no property name use the {@code ':'} character.
      */
     static boolean isNamespace(final String candidate) {
         return (candidate.length() > 4) && (candidate.charAt(4) == ':');
@@ -220,63 +234,98 @@ abstract class Transformer {
      *   </ul></li>
      * </ul>
      *
+     * @param  export    {@code true} for {@code "RenameOnImport.lst"}, {@code false} for {@code "RenameOnImport.lst"}.
      * @param  filename  name of the file to load.
      * @param  capacity  initial hash map capacity. This is only a hint.
      */
-    static Map<String, Map<String,String>> load(final String filename, final int capacity) {
+    static Map<String, Map<String,String>> load(final boolean export, final String filename, final int capacity) {
         final Map<String, Map<String,String>> m = new HashMap<>(capacity);
-        try (LineNumberReader in = new LineNumberReader(new InputStreamReader(
-                TransformingReader.class.getResourceAsStream(filename), "UTF-8")))
-        {
-            Map<String,String> attributes = null;               // All attributes for a given type.
-            String namespace = null;                            // Value to store in 'attributes' map.
-            String line;
-            while ((line = in.readLine()) != null) {
-                final int length = line.length();
-                final int start = CharSequences.skipLeadingWhitespaces(line, 0, length);
-                if (start < length && line.charAt(start) != '#') {
-                    String element = line.substring(start).trim();
-                    switch (start) {
-                        case 0: {                                                   // New namespace URI.
-                            if (!isNamespace(element)) break;                       // Report illegal format.
-                            namespace  = element.intern();
-                            attributes = null;
-                            continue;
-                        }
-                        case 1: {                                                   // New type in above namespace URI.
-                            if (namespace == null) break;                           // Report illegal format.
-                            final int s = element.indexOf(NO_NAMESPACE);
-                            if (s >= 0) {
-                                element = element.substring(0, s).trim();
-                            }
-                            element = element.intern();
-                            attributes = m.computeIfAbsent(element, (k) -> new HashMap<>());
-                            if (s < 0) {
-                                // Record namespace for this type only if '!' is not present.
-                                if (attributes.put(element, namespace) != null) break;
+        final Set<Class<?>> renameLoaders = new LinkedHashSet<>(8);
+        renameLoaders.add(Transformer.class);
+        TypeRegistration.getRenameFileLoader(export, renameLoaders);
+        for (final Class<?> loader : renameLoaders) {
+            try (LineNumberReader in = new LineNumberReader(new InputStreamReader(loader.getResourceAsStream(filename), "UTF-8"))) {
+                Map<String,String> attributes = null;               // All attributes for a given type.
+                String namespace = null;                            // Value to store in 'attributes' map.
+                String line;
+                while ((line = in.readLine()) != null) {
+                    final int length = line.length();
+                    final int start = CharSequences.skipLeadingWhitespaces(line, 0, length);
+                    if (start < length && line.charAt(start) != '#') {
+                        String element = line.substring(start).trim();
+                        switch (start) {
+                            /*
+                             * Begin a new namespace. Must be before any class or property.
+                             */
+                            case 0: {                                                   // New namespace URI.
+                                if (!isNamespace(element)) break;                       // Report illegal format.
+                                namespace  = element.intern();
+                                attributes = null;
+                                continue;
                             }
-                            continue;
-                        }
-                        case 2: {                                                   // New attribute in above type.
-                            if (attributes == null || namespace == null) break;     // Report illegal format.
-                            final int s = element.indexOf(RENAME_SEPARATOR);
-                            if (s >= 0) {
-                                final String old = element.substring(0, s).trim().intern();
-                                element = element.substring(s+1).trim().intern();
-                                if (attributes.put(old, element) != null) break;    // Report an error if duplicated values.
-                            } else {
+                            /*
+                             * Add a class into the above-defined namespace.
+                             * The class may inherit the properties of another class.
+                             * Inherited properties may be in a different namespace than the new class.
+                             */
+                            case 1: {                                                   // New type in above namespace URI.
+                                if (namespace == null) break;                           // Report illegal format.
+                                final int noNS = element.indexOf(NO_NAMESPACE);
+                                if (noNS >= 0) {
+                                    element = CharSequences.trimWhitespaces(element, 0, noNS).toString();
+                                }
+                                final Function<String, Map<String,String>> init;
+                                final int s = element.indexOf(EXTENDS);
+                                if (s >= 0) {
+                                    final String parent;
+                                    parent  = CharSequences.trimWhitespaces(element, s+1, element.length()).toString();
+                                    element = CharSequences.trimWhitespaces(element, 0, s).toString();
+                                    init = (k) -> {
+                                        Map<String,String> properties = m.get(parent);
+                                        if (properties != null) {
+                                            properties = new HashMap<>(properties);
+                                            properties.remove(parent);
+                                            return properties;
+                                        }
+                                        throw new NoSuchElementException(parent);
+                                    };
+                                } else {
+                                    init = (k) -> new HashMap<>();
+                                }
                                 element = element.intern();
+                                attributes = m.computeIfAbsent(element, init);
+                                if (noNS < 0) {
+                                    // Record namespace for this type only if '!' is not present.
+                                    if (attributes.put(element, namespace) != null) break;
+                                }
+                                continue;
+                            }
+                            /*
+                             * Add a property into the above-defined class.
+                             * All properties are associated to above-defined namespace.
+                             * A property may have an alias (e.g. "center/centre").
+                             */
+                            case 2: {                                                   // New attribute in above type.
+                                if (attributes == null || namespace == null) break;     // Report illegal format.
+                                final int s = element.indexOf(RENAME_SEPARATOR);
+                                if (s >= 0) {
+                                    final String old = element.substring(0, s).trim().intern();
+                                    element = element.substring(s+1).trim().intern();
+                                    if (attributes.put(old, element) != null) break;    // Report an error if duplicated values.
+                                } else {
+                                    element = element.intern();
+                                }
+                                if (attributes.put(element, namespace) != null) break;  // Report an error if duplicated values.
+                                continue;
                             }
-                            if (attributes.put(element, namespace) != null) break;  // Report an error if duplicated values.
-                            continue;
                         }
+                        throw new InvalidPropertiesFormatException(Errors.format(       // See method javadoc.
+                                Errors.Keys.ErrorInFileAtLine_2, filename, in.getLineNumber()));
                     }
-                    throw new InvalidPropertiesFormatException(Errors.format(       // See method javadoc.
-                            Errors.Keys.ErrorInFileAtLine_2, filename, in.getLineNumber()));
                 }
+            } catch (IOException e) {
+                throw new ExceptionInInitializerError(e);
             }
-        } catch (IOException e) {
-            throw new ExceptionInInitializerError(e);
         }
         /*
          * At this point we finished computing the map values. Many values are maps with only 1 entry.
@@ -443,9 +492,9 @@ abstract class Transformer {
     }
 
     /**
-     * Renames en element using the namespaces map give to the {@code open(…)} and {@code close(…)} methods.
-     * When unmarshalling, this imports a name read from the XML document to the name to give to JAXB.
-     * When marshalling, this exports a name used in JAXB annotation to the name to use in XML document.
+     * Renames en element using the namespaces map given to the {@code open(…)} and {@code close(…)} methods.
+     * When unmarshalling, this method converts a name read from the XML document to the name to give to JAXB.
+     * When marshalling, this method converts a name used in JAXB annotation to the name to use in XML document.
      * The new namespace depends on both the old namespace and the element name.
      * The prefix is computed by {@link #prefixReplacement(String, String)}.
      *
@@ -475,7 +524,7 @@ abstract class Transformer {
     }
 
     /**
-     * Returns the map loaded by {@link #load(String, int)}.
+     * Returns the map loaded by {@link #load(boolean, String, int)}.
      * This is a static field in the {@link TransformingReader} or {@link TransformingWriter} subclass.
      *
      * @param  namespace  the namespace URI for which to get the substitution map (never null).
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java
index 34a0a1f..0da5899 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java
@@ -39,7 +39,7 @@ import static javax.xml.stream.XMLStreamConstants.*;
 /**
  * A XML reader replacing the namespaces found in XML documents by the namespaces expected by SIS at unmarshalling time.
  * This class forwards every method calls to the wrapped {@link XMLEventReader}, but with some {@code namespaceURI}
- * modified before being transfered. This class uses a dictionary for identifying the XML namespaces expected by JAXB
+ * modified before being transferred. This class uses a dictionary for identifying the XML namespaces expected by JAXB
  * implementation. This is needed when a single namespace in a legacy schema has been splitted into many namespaces
  * in the newer schema. This happen for example in the upgrade from ISO 19139:2007 to ISO 19115-3.
  * In such cases, we need to check which attribute is being mapped in order to determine the new namespace.
@@ -77,7 +77,7 @@ final class TransformingReader extends Transformer implements XMLEventReader {
      *
      * This map is initialized only once and should not be modified after that point.
      */
-    private static final Map<String, Map<String,String>> NAMESPACES = load(FILENAME, 250);
+    private static final Map<String, Map<String,String>> NAMESPACES = load(false, FILENAME, 260);
 
     /**
      * Returns the namespace for the given ISO type, or {@code null} if unknown.
@@ -280,11 +280,11 @@ final class TransformingReader extends Transformer implements XMLEventReader {
     }
 
     /**
-     * Returns the map loaded by {@link #load(String, int)} if the given namespace is a known legacy namespace.
-     * This method returns a non-empty map only for legacy namespaces for which the {@value #FILENAME} file has
-     * been designed. This is necessary for avoiding confusion with classes of the same name defined in other
-     * standards. For example the {@code Record} class name is used by other standards like Catalog Service for
-     * the Web (OGC CSW), and we don't want to replace the namespace of CSW classes.
+     * Returns the map loaded by {@link #load(boolean, String, int)} if the given namespace is a known legacy namespace.
+     * This method returns a non-empty map only for legacy namespaces for which the {@value #FILENAME} file has been designed.
+     * This is necessary for avoiding confusion with classes of the same name defined in other standards.
+     * For example the {@code Record} class name is used by other standards like Catalog Service for the Web (OGC CSW),
+     * and we don't want to replace the namespace of CSW classes.
      *
      * @param  namespace  the namespace URI for which to get the substitution map.
      * @return the substitution map for the given namespace, or an empty map if none.
@@ -294,6 +294,7 @@ final class TransformingReader extends Transformer implements XMLEventReader {
     final Map<String, Map<String,String>> renamingMap(final String namespace) {
         if (!namespace.isEmpty()) {
             switch (removeTrailingSlash(namespace)) {
+                case "http://www.cnig.gouv.fr/2005/fra":        // TODO: move to sis-french-profile module.
                 case LegacyNamespaces.GMI_ALIAS:
                 case LegacyNamespaces.GMI:
                 case LegacyNamespaces.GMD:
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java
index ca570d2..2984ed3 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java
@@ -62,10 +62,10 @@ final class TransformingWriter extends Transformer implements XMLEventWriter {
      * Location of the file listing types and their properties contained in legacy namespaces.
      * This is used for mapping new ISO 19115-3:2016 namespaces to legacy ISO 19139:2007 ones,
      * where the same {@code "http://standards.iso.org/iso/19115/-3/…"} URI is used in places
-     * where legacy schemas had two distinct URIs: {@code "http://www.isotc211.org/2005/gmd"}
+     * where legacy schema had two distinct URIs:  {@code "http://www.isotc211.org/2005/gmd"}
      * and {@code "http://standards.iso.org/iso/19115/-2/gmi/1.0"}.
      */
-    static final String FILENAME = "RenameOnExport.lst";
+    private static final String FILENAME = "RenameOnExport.lst";
 
     /**
      * The mapping from (<var>type</var>, <var>attribute</var>) pairs to legacy namespaces.
@@ -84,7 +84,7 @@ final class TransformingWriter extends Transformer implements XMLEventWriter {
      *
      * This map is initialized only once and should not be modified after that point.
      */
-    private static final Map<String, Map<String,String>> NAMESPACES = load(FILENAME, 60);
+    private static final Map<String, Map<String,String>> NAMESPACES = load(true, FILENAME, 60);
 
     /**
      * Elements that appear in different order in ISO 19139:2007 (or other legacy standards) compared
@@ -214,7 +214,7 @@ final class TransformingWriter extends Transformer implements XMLEventWriter {
     }
 
     /**
-     * Returns the map loaded by {@link #load(String, int)}.
+     * Returns the map loaded by {@link #load(boolean, String, int)}.
      *
      * @param  namespace  the namespace URI for which to get the substitution map.
      * @return the substitution map for the given namespace.
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/readme.html b/core/sis-metadata/src/main/java/org/apache/sis/xml/readme.html
new file mode 100644
index 0000000..f03b263
--- /dev/null
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/readme.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Syntax of Rename files</title>
+    <meta charset="UTF-8">
+    <style>
+      p {
+        text-align: justify;
+      }
+      pre {
+        border-left-style: solid;
+        border-left-color: darkgray;
+      }
+    </style>
+  </head>
+  <body>
+    <h1>Syntax of <code>RenameOnImport</code>/<code>Export</code> files</h1>
+    <p>
+      <b>WARNING: the syntax documented in this page is not committed API and may change in any future SIS version.</b>
+    </p>
+    <p>
+      This package provides two files in the <code>resources/org/apache/sis/xml/</code> folder:
+      <code>RenameOnImport.lst</code> and <code>RenameOnExport.lst</code>.
+      Those files are used by <code>TransformingReader.java</code> and <code>TransformingWriter.java</code> respectively
+      for converting XML namespaces between new specifications (ISO 19115-3:2016 and ISO 19115-4) and old specifications
+      (ISO 19139:2007 and ISO 19115-2:2009). The JAXB annotations in Apache SIS use the newer specifications.
+      The <code>Rename*.lst</code> files are needed only when reading or writing to older specifications.
+      Those files are used for performing the work of a lightweight XSLT engine.
+      Both share the same syntax:
+    </p>
+    <ul>
+      <li>Lines with zero-space indentation are namespace URIs.</li>
+      <li>Lines with one-space  indentation are XML type names.</li>
+      <li>Lines with two-spaces indentation are property names.</li>
+      <li>The "/" character in "<var>before</var>/<var>after</var>" means that a property name needs to be changed.
+          <var>Before</var> is the name before the renaming process and <var>after</var> is the name after the renaming process.</li>
+      <li>The ":" character in "<var>Parent</var> : <var>Child</var>" means that a subclass inherits all properties from a parent class.
+          The <var>parent</var> must be defined before the <var>child</var> (no forward references).
+      <li>The "!" character in "<var>Class</var> !<var>reason</var>" skips the association of current namespace to that class
+          (but namespace will still be associated to the properties). <var>Reason</var> is a free text.</li>
+    </ul>
+    <p>
+      For example the following snippet from <code>RenameOnImport.lst</code> declares that the <code>Citation.title</code>,
+      <code>Citation.edition</code> and <code>Address.country</code> properties are defined in the <b><code>cit</code></b> namespace,
+      while the <code>Extent.description</code> property is defined in the <b><code>gex</code></b> namespace
+      and the <code>Georectified.centrePoint</code> property is defined in the <b><code>msr</code></b> namespace.
+      Those information are required when reading a file encoded by the old standards
+      because almost all properties where in the single <code>gmd</code> namespace.
+      In general those information are used for converting only the <em>namespaces</em>; the class and property names are unchanged.
+      But in the special case where the "<var>before</var>/<var>after</var>" syntax is used, then class and/or property names are also changed.
+      For example in the following example, <code>Georectified.centerPoint</code> (from the old standard)
+      is renamed as <code>Georectified.centrePoint</code> (new standard).
+    </p>
+    <blockquote><pre>http://standards.iso.org/iso/19115/-3/<b>cit</b>/1.0
+ CI_Citation
+  title
+  edition
+ CI_Address
+  country
+http://standards.iso.org/iso/19115/-3/<b>gex</b>/1.0
+ EX_Extent
+  description
+http://standards.iso.org/iso/19115/-3/<b>msr</b>/1.0
+ MI_Georectified
+  centerPoint/centrePoint</pre></blockquote>
+    <p>
+      Conversely, when writing a file, some additional renaming can be applied <em>after</em> the namespaces have been renamed to <code>gmd</code>.
+      The following snippet from <code>RenameOnExport.lst</code> performs the converse of the property renaming shown in previous example:
+    </p>
+    <blockquote><pre>http://www.isotc211.org/2005/gmd
+ MD_Georectified
+  centrePoint/centerPoint</pre></blockquote>
+  </body>
+</html>
diff --git a/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst b/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst
index 308e7ca..2ea16d3 100644
--- a/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst
+++ b/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst
@@ -1,15 +1,11 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements;
+# and to You under the Apache License, Version 2.0.
 #
-# Elements to rename during exports.
-# Lines with zero-space indentation are namespace URIs.
-# Lines with one-space  indentation are XML types.
-# Lines with two-spaces indentation are properties.
-# actual/exported means that a property needs to be renamed.
+# See readme.html is source code for the syntax of this file.
 #
 http://www.isotc211.org/2005/gmd
  MD_Georectified
   centrePoint/centerPoint
- MI_Georectified !other namespace
-  centrePoint/centerPoint
  DQ_QuantitativeResult
   valueRecordType/valueType
 http://www.isotc211.org/2005/srv
@@ -110,8 +106,7 @@ http://standards.iso.org/iso/19115/-2/gmi/1.0
   detectedPolarisation
  MI_CoverageDescription
   rangeElementDescription
- MI_ImageDescription
-  rangeElementDescription
+ MI_ImageDescription : MI_CoverageDescription
  MI_RangeElementDescription
   name
   definition
@@ -155,7 +150,7 @@ http://standards.iso.org/iso/19115/-2/gmi/1.0
   acquisitionInformation
  MI_Georeferenceable
   geolocationInformation
- MI_Georectified
+ MI_Georectified : MD_Georectified
   checkPoint
  MI_GCP
   geographicCoordinates
diff --git a/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst b/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst
index d924669..5018d16 100644
--- a/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst
+++ b/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst
@@ -1,9 +1,7 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements;
+# and to You under the Apache License, Version 2.0.
 #
-# Namespaces in which attribute are defined.
-# Lines with zero-space indentation are namespace URIs.
-# Lines with one-space  indentation are XML types.
-# Lines with two-spaces indentation are properties.
-# old/new means that a property needs to be renamed.
+# See readme.html is source code for the syntax of this file.
 #
 http://standards.iso.org/iso/19115/-3/cit/1.0
  AbstractCI_Party
@@ -39,9 +37,7 @@ http://standards.iso.org/iso/19115/-3/cit/1.0
   date
   dateType
  CI_DateTypeCode
- CI_Individual
-  contactInfo
-  name
+ CI_Individual : AbstractCI_Party
   positionName
  CI_OnLineFunctionCode
  CI_OnlineResource
@@ -52,20 +48,15 @@ http://standards.iso.org/iso/19115/-3/cit/1.0
   name
   protocol
   protocolRequest
- CI_Organisation
-  contactInfo
+ CI_Organisation : AbstractCI_Party
   individual
   logo
-  name
  CI_PresentationFormCode
  CI_Responsibility
   extent
   party
   role
- CI_ResponsibleParty !other namespace
-  extent
-  party
-  role
+ CI_ResponsibleParty : CI_Responsibility !legacy
  CI_RoleCode
  CI_Series
   issueIdentification
@@ -96,29 +87,25 @@ http://standards.iso.org/iso/19115/-3/gcx/1.0
 http://standards.iso.org/iso/19115/-3/gex/1.0
  AbstractEX_GeographicExtent
   extentTypeCode
- EX_BoundingPolygon
-  extentTypeCode
+ EX_BoundingPolygon : AbstractEX_GeographicExtent
   polygon
  EX_Extent
   description
   geographicElement
   temporalElement
   verticalElement
- EX_GeographicBoundingBox
+ EX_GeographicBoundingBox : AbstractEX_GeographicExtent
   eastBoundLongitude
-  extentTypeCode
   northBoundLatitude
   southBoundLatitude
   westBoundLongitude
- EX_GeographicDescription
-  extentTypeCode
+ EX_GeographicDescription : AbstractEX_GeographicExtent
   geographicIdentifier
- EX_SpatialTemporalExtent
+ EX_TemporalExtent
   extent
+ EX_SpatialTemporalExtent : EX_TemporalExtent
   spatialExtent
   verticalExtent
- EX_TemporalExtent
-  extent
  EX_VerticalExtent
   maximumValue
   minimumValue
@@ -267,31 +254,19 @@ http://standards.iso.org/iso/19115/-3/mco/1.0
   releasability
   responsibleParty
   useLimitation
- MD_LegalConstraints
+ MD_LegalConstraints : MD_Constraints
   accessConstraints
-  constraintApplicationScope
-  graphic
   otherConstraints
-  reference
-  releasability
-  responsibleParty
   useConstraints
-  useLimitation
  MD_Releasability
   addressee
   disseminationConstraints
   statement
  MD_RestrictionCode
- MD_SecurityConstraints
+ MD_SecurityConstraints : MD_Constraints
   classification
   classificationSystem
-  constraintApplicationScope
-  graphic
   handlingDescription
-  reference
-  releasability
-  responsibleParty
-  useLimitation
   userNote
 http://standards.iso.org/iso/19115/-3/mdb/1.0
  MD_Metadata
@@ -322,31 +297,7 @@ http://standards.iso.org/iso/19115/-3/mdb/1.0
  MD_MetadataScope
   name
   resourceScope
- MI_Metadata !other namespace
-  acquisitionInformation
-  alternativeMetadataReference
-  applicationSchemaInfo
-  contact
-  contentInfo
-  dataQualityInfo
-  dateInfo
-  defaultLocale
-  distributionInfo
-  identificationInfo
-  metadataConstraints
-  metadataExtensionInfo
-  metadataIdentifier
-  metadataLinkage
-  metadataMaintenance
-  metadataProfile
-  metadataScope
-  metadataStandard
-  otherLocale
-  parentMetadata
-  portrayalCatalogueInfo
-  referenceSystemInfo
-  resourceLineage
-  spatialRepresentationInfo
+ MI_Metadata : MD_Metadata !other namespace
 http://standards.iso.org/iso/19115/-3/mdt/1.0
  MX_DataFile
   featureTypes
@@ -387,31 +338,6 @@ http://standards.iso.org/iso/19115/-3/mrc/1.0
  MD_AttributeGroup
   attribute
   contentType
- MD_Band
-  bandBoundaryDefinition
-  bitsPerValue
-  boundMax
-  boundMin
-  boundUnits
-  description
-  detectedPolarisation
-  maxValue
-  meanValue
-  minValue
-  name
-  nominalSpatialResolution
-  numberOfValues
-  offset
-  otherProperty
-  otherPropertyType
-  peakResponse
-  scaleFactor
-  sequenceIdentifier
-  standardDeviation
-  toneGradation
-  transferFunctionType
-  transmittedPolarisation
-  units
  MD_CoverageContentTypeCode
  MD_CoverageDescription
   attributeDescription
@@ -427,9 +353,7 @@ http://standards.iso.org/iso/19115/-3/mrc/1.0
  MD_FeatureTypeInfo
   featureInstanceCount
   featureTypeName
- MD_ImageDescription
-  attributeDescription
-  attributeGroup
+ MD_ImageDescription : MD_CoverageDescription
   cameraCalibrationInformationAvailability
   cloudCoverPercentage
   compressionGenerationQuantity
@@ -439,77 +363,40 @@ http://standards.iso.org/iso/19115/-3/mrc/1.0
   imageQualityCode
   imagingCondition
   lensDistortionInformationAvailability
-  processingLevelCode
   radiometricCalibrationDataAvailability
-  rangeElementDescription
   triangulationIndicator
  MD_ImagingConditionCode
  MD_RangeDimension
   description
   name
   sequenceIdentifier
- MD_SampleDimension
+ MD_SampleDimension : MD_RangeDimension
   bitsPerValue
-  description
   maxValue
   meanValue
   minValue
-  name
   numberOfValues
   offset
   otherProperty
   otherPropertyType
   scaleFactor
-  sequenceIdentifier
   standardDeviation
   units
- MI_Band
+ MD_Band : MD_SampleDimension
   bandBoundaryDefinition
-  bitsPerValue
   boundMax
   boundMin
   boundUnits
-  description
   detectedPolarisation
-  maxValue
-  meanValue
-  minValue
-  name
   nominalSpatialResolution
-  numberOfValues
-  offset
-  otherProperty
-  otherPropertyType
   peakResponse
-  scaleFactor
-  sequenceIdentifier
-  standardDeviation
   toneGradation
   transferFunctionType
   transmittedPolarisation
-  units
+ MI_Band : MD_Band
  MI_BandDefinition
- MI_CoverageDescription
-  attributeDescription
-  attributeGroup
-  processingLevelCode
-  rangeElementDescription
- MI_ImageDescription
-  attributeDescription
-  attributeGroup
-  cameraCalibrationInformationAvailability
-  cloudCoverPercentage
-  compressionGenerationQuantity
-  filmDistortionInformationAvailability
-  illuminationAzimuthAngle
-  illuminationElevationAngle
-  imageQualityCode
-  imagingCondition
-  lensDistortionInformationAvailability
-  processingLevelCode
-  radiometricCalibrationDataAvailability
-  rangeElementDescription
-  triangulationIndicator
+ MI_CoverageDescription : MD_CoverageDescription
+ MI_ImageDescription : MD_ImageDescription
  MI_RangeElementDescription
   definition
   name
@@ -579,7 +466,7 @@ http://standards.iso.org/iso/19115/-3/mri/1.0
   topicCategory
  DS_AssociationTypeCode
  DS_InitiativeTypeCode
- MD_AggregateInformation !other namespace
+ MD_AggregateInformation !legacy
   metadataReference
   name
  MD_AssociatedResource
@@ -587,31 +474,11 @@ http://standards.iso.org/iso/19115/-3/mri/1.0
   initiativeType
   metadataReference
   name
- MD_DataIdentification
-  abstract
-  additionalDocumentation
-  associatedResource
-  citation
-  credit
+ MD_DataIdentification : AbstractMD_Identification
   defaultLocale
-  descriptiveKeywords
   environmentDescription
-  extent
-  graphicOverview
   otherLocale
-  pointOfContact
-  processingLevel
-  purpose
-  resourceConstraints
-  resourceFormat
-  resourceMaintenance
-  resourceSpecificUsage
-  spatialRepresentationType
-  spatialResolution
-  status
   supplementalInformation
-  temporalResolution
-  topicCategory
  MD_KeywordClass
   className
   conceptIdentifier
@@ -638,27 +505,6 @@ http://standards.iso.org/iso/19115/-3/mri/1.0
   usageDateTime
   userContactInfo
   userDeterminedLimitations
- SV_ServiceIdentification !other namespace
-  abstract
-  additionalDocumentation
-  associatedResource
-  citation
-  credit
-  descriptiveKeywords
-  extent
-  graphicOverview
-  pointOfContact
-  processingLevel
-  purpose
-  resourceConstraints
-  resourceFormat
-  resourceMaintenance
-  resourceSpecificUsage
-  spatialRepresentationType
-  spatialResolution
-  status
-  temporalResolution
-  topicCategory
 http://standards.iso.org/iso/19115/-3/mrl/1.0
  LE_Algorithm
   citation
@@ -740,71 +586,40 @@ http://standards.iso.org/iso/19115/-3/msr/1.0
  MD_GeometricObjects
   geometricObjectCount
   geometricObjectType
- MD_Georectified
+ MD_GridSpatialRepresentation
   axisDimensionProperties
   cellGeometry
-  centrePoint
+  numberOfDimensions
+  transformationParameterAvailability
+ MD_Georectified : MD_GridSpatialRepresentation
+  centerPoint/centrePoint
   checkPoint
   checkPointAvailability
   checkPointDescription
   cornerPoints
-  numberOfDimensions
   pointInPixel
   transformationDimensionDescription
   transformationDimensionMapping
-  transformationParameterAvailability
- MD_Georeferenceable
-  axisDimensionProperties
-  cellGeometry
+ MD_Georeferenceable : MD_GridSpatialRepresentation
   controlPointAvailability
   geolocationInformation
   georeferencedParameters
-  numberOfDimensions
   orientationParameterAvailability
   orientationParameterDescription
   parameterCitation
-  transformationParameterAvailability
- MD_GridSpatialRepresentation
-  axisDimensionProperties
-  cellGeometry
-  numberOfDimensions
-  transformationParameterAvailability
  MD_TopologyLevelCode
  MD_VectorSpatialRepresentation
   geometricObjects
   topologyLevel
  MI_GCP
   accuracyReport
- MI_GCPCollection
+ MI_GCPCollection : AbstractMI_GeolocationInformation
   collectionIdentification
   collectionName
   coordinateReferenceSystem
   gcp
-  qualityInfo
- MI_Georectified
-  axisDimensionProperties
-  cellGeometry
-  centerPoint/centrePoint
-  checkPoint
-  checkPointAvailability
-  checkPointDescription
-  cornerPoints
-  numberOfDimensions
-  pointInPixel
-  transformationDimensionDescription
-  transformationDimensionMapping
-  transformationParameterAvailability
- MI_Georeferenceable
-  axisDimensionProperties
-  cellGeometry
-  controlPointAvailability
-  geolocationInformation
-  georeferencedParameters
-  numberOfDimensions
-  orientationParameterAvailability
-  orientationParameterDescription
-  parameterCitation
-  transformationParameterAvailability
+ MI_Georectified : MD_Georectified
+ MI_Georeferenceable : MD_Georeferenceable
 http://standards.iso.org/iso/19115/-3/srv/2.0
  DCPList
  SV_CoupledResource
@@ -831,7 +646,7 @@ http://standards.iso.org/iso/19115/-3/srv/2.0
   repeatability
   valueType
  SV_ParameterDirection
- SV_ServiceIdentification
+ SV_ServiceIdentification : AbstractMD_Identification
   accessProperties
   containsChain
   containsOperations
@@ -844,74 +659,22 @@ http://standards.iso.org/iso/19115/-3/srv/2.0
   serviceType
   serviceTypeVersion
 http://standards.iso.org/iso/19157/-2/dqc/1.0
- AbstractDQ_Completeness !other namespace
-  dateTime
  AbstractDQ_Element !other namespace
   dateTime
- AbstractDQ_LogicalConsistency !other namespace
-  dateTime
- AbstractDQ_PositionalAccuracy !other namespace
-  dateTime
- AbstractDQ_TemporalAccuracy !other namespace
-  dateTime
- AbstractDQ_ThematicAccuracy !other namespace
-  dateTime
- DQ_AbsoluteExternalPositionalAccuracy !other namespace
-  dateTime
- DQ_AccuracyOfATimeMeasurement !other namespace
-  dateTime
- DQ_CompletenessCommission !other namespace
-  dateTime
- DQ_CompletenessOmission !other namespace
-  dateTime
- DQ_ConceptualConsistency !other namespace
-  dateTime
- DQ_DomainConsistency !other namespace
-  dateTime
- DQ_FormatConsistency !other namespace
-  dateTime
- DQ_GriddedDataPositionalAccuracy !other namespace
-  dateTime
- DQ_NonQuantitativeAttributeAccuracy !other namespace
-  dateTime
- DQ_QuantitativeAttributeAccuracy !other namespace
-  dateTime
- DQ_RelativeInternalPositionalAccuracy !other namespace
-  dateTime
- DQ_TemporalConsistency !other namespace
-  dateTime
- DQ_TemporalValidity !other namespace
-  dateTime
- DQ_ThematicClassificationCorrectness !other namespace
-  dateTime
- DQ_TopologicalConsistency !other namespace
-  dateTime
- QE_Usability !other namespace
-  dateTime
 http://standards.iso.org/iso/19157/-2/mdq/1.0
- AbstractDQ_Completeness
-  result
  AbstractDQ_Element
   result
- AbstractDQ_LogicalConsistency
-  result
- AbstractDQ_PositionalAccuracy
-  result
+ AbstractDQ_Completeness : AbstractDQ_Element
+ AbstractDQ_LogicalConsistency : AbstractDQ_Element
+ AbstractDQ_PositionalAccuracy : AbstractDQ_Element
  AbstractDQ_Result
- AbstractDQ_TemporalAccuracy !other namespace
-  result
- AbstractDQ_ThematicAccuracy
-  result
- DQ_AbsoluteExternalPositionalAccuracy
-  result
- DQ_AccuracyOfATimeMeasurement
-  result
- DQ_CompletenessCommission
-  result
- DQ_CompletenessOmission
-  result
- DQ_ConceptualConsistency
-  result
+ AbstractDQ_TemporalAccuracy : AbstractDQ_Element !other namespace
+ AbstractDQ_ThematicAccuracy : AbstractDQ_Element
+ DQ_AbsoluteExternalPositionalAccuracy : AbstractDQ_PositionalAccuracy
+ DQ_AccuracyOfATimeMeasurement : AbstractDQ_TemporalAccuracy
+ DQ_CompletenessCommission : AbstractDQ_Completeness
+ DQ_CompletenessOmission : AbstractDQ_Completeness
+ DQ_ConceptualConsistency : AbstractDQ_LogicalConsistency
  DQ_ConformanceResult
   explanation
   pass
@@ -919,39 +682,28 @@ http://standards.iso.org/iso/19157/-2/mdq/1.0
  DQ_DataQuality
   report
   scope
- DQ_DomainConsistency
-  result
+ DQ_DomainConsistency : AbstractDQ_LogicalConsistency
  DQ_EvaluationMethodTypeCode
- DQ_FormatConsistency
-  result
- DQ_GriddedDataPositionalAccuracy
-  result
- DQ_NonQuantitativeAttributeAccuracy !other namespace
-  result
- DQ_QuantitativeAttributeAccuracy
-  result
+ DQ_FormatConsistency : AbstractDQ_LogicalConsistency
+ DQ_GriddedDataPositionalAccuracy : AbstractDQ_PositionalAccuracy
+ DQ_NonQuantitativeAttributeAccuracy : AbstractDQ_ThematicAccuracy !other namespace
+ DQ_QuantitativeAttributeAccuracy : AbstractDQ_ThematicAccuracy
  DQ_QuantitativeResult
   value
   valueType/valueRecordType
   valueUnit
- DQ_RelativeInternalPositionalAccuracy
-  result
- DQ_TemporalConsistency
-  result
- DQ_TemporalValidity
-  result
- DQ_ThematicClassificationCorrectness
-  result
- DQ_TopologicalConsistency
-  result
+ DQ_RelativeInternalPositionalAccuracy : AbstractDQ_PositionalAccuracy
+ DQ_TemporalConsistency : AbstractDQ_TemporalAccuracy
+ DQ_TemporalValidity : AbstractDQ_TemporalAccuracy
+ DQ_ThematicClassificationCorrectness : AbstractDQ_ThematicAccuracy
+ DQ_TopologicalConsistency : AbstractDQ_LogicalConsistency
  QE_CoverageResult
   resultContentDescription
   resultFile
   resultFormat
   resultSpatialRepresentation
   spatialRepresentationType
- QE_Usability !other namespace
-  result
+ QE_Usability : AbstractDQ_Element !other namespace
 http://www.isotc211.org/2005/gts
  TM_Duration
  TM_PeriodDuration
diff --git a/core/sis-metadata/src/test/java/org/apache/sis/xml/RenameListGenerator.java b/core/sis-metadata/src/test/java/org/apache/sis/xml/RenameListGenerator.java
index 98d6c26..3381457 100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/xml/RenameListGenerator.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/xml/RenameListGenerator.java
@@ -37,11 +37,11 @@ import org.apache.sis.internal.xml.LegacyNamespaces;
 
 
 /**
- * Creates the {@value TransformingReader#FILENAME} file. This class needs to be executed only when the content
+ * Creates a file in the {@value TransformingReader#FILENAME} format. This class can be executed if the content
  * has changed, or for verifying the current file. Output format contains namespaces first, then classes,
  * then properties. Example:
  *
- * {@preformat
+ * {@preformat text
  * http://standards.iso.org/iso/19115/-3/cit/1.0
  *   CI_Address
  *     administrativeArea
@@ -49,6 +49,21 @@ import org.apache.sis.internal.xml.LegacyNamespaces;
  *   CI_Citation
  *     citedResponsibleParty
  * }
+ *
+ * This class can be used as a starting point for generating a new file from scratch.
+ * It should not be used for updating the existing file (unless a lot of things have changed)
+ * because some of {@value TransformingReader#FILENAME} content have been edited by hand.
+ * For generating a new file:
+ *
+ * {@preformat java
+ *     public static void main(String[] args) throws Exception {
+ *         RenameListGenerator gen = new RenameListGenerator(Paths.get("/path/to/your/classes"));
+ *         gen.add(Paths.get("root/package/of/classes/to/add"));
+ *         try (final BufferedWriter out = Files.newBufferedWriter(Paths.get("MyOutputFile.lst"))) {
+ *             gen.print(out);
+ *         }
+ *     }
+ * }
  */
 public final class RenameListGenerator {
     /**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingTypes.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingTypes.java
index bd6b00b..f1fa6f8 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingTypes.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingTypes.java
@@ -17,6 +17,7 @@
 package org.apache.sis.internal.referencing;
 
 import java.util.Collection;
+import java.util.function.UnaryOperator;
 import org.apache.sis.internal.jaxb.TypeRegistration;
 import org.apache.sis.parameter.DefaultParameterValue;
 import org.apache.sis.parameter.DefaultParameterValueGroup;
@@ -30,11 +31,11 @@ import org.opengis.referencing.ReferenceSystem;
  * This class is declared in the {@code META-INF/services/org.apache.sis.internal.jaxb.TypeRegistration} file.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
-public final class ReferencingTypes extends TypeRegistration {
+public final class ReferencingTypes extends TypeRegistration implements UnaryOperator<Object> {
     /**
      * Adds to the given collection the referencing types that should be given to the initial JAXB context.
      */
@@ -46,14 +47,13 @@ public final class ReferencingTypes extends TypeRegistration {
     }
 
     /**
-     * Notifies that the {@code sis-referencing} module can marshal arbitrary implementations
-     * of some coordinate reference system interfaces.
+     * Returns the converter to apply before marshalling objects.
      *
-     * @return {@code true}.
+     * @return {@code this}.
      */
     @Override
-    protected boolean canMarshalInterfaces() {
-        return true;
+    protected UnaryOperator<Object> beforeMarshal() {
+        return this;
     }
 
     /**
@@ -64,7 +64,7 @@ public final class ReferencingTypes extends TypeRegistration {
      * @return the given value as a type that can be marshalled, or {@code null}.
      */
     @Override
-    public Object toImplementation(final Object value) {
+    public Object apply(final Object value) {
         return (value instanceof ReferenceSystem) ? AbstractReferenceSystem.castOrCopy((ReferenceSystem) value) : null;
     }
 }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java b/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java
index 161fea5..cf26ccc 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java
@@ -39,7 +39,7 @@ import org.apache.sis.util.logging.Logging;
  * (profiling shows that even a single thread has very low activity), which reduces the interest of that class.
  * Combination of {@code ThreadPoolExecutor} super-class with {@code DelayedQueue} were not successful neither.
  *
- * <p>Given that it:</p>
+ * <p>Given that:</p>
  * <ul>
  *   <li>it seems difficult to configure {@code (Scheduled)ThreadPoolExecutor} in such a way
  *       that two or more threads are created only when really needed,</li>
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java b/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java
index 80c3114..816a9e3 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java
@@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicLong;
  * for more information.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.3
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -49,9 +49,19 @@ public abstract class DelayedRunnable implements Delayed, Runnable {
     final long timestamp;
 
     /**
+     * Creates a new task to be executed after the given delay.
+     *
+     * @param delay  delay before execution of this task.
+     * @param unit   unit of measurement of given {@code delay}.
+     */
+    protected DelayedRunnable(final int delay, final TimeUnit unit) {
+        timestamp = System.nanoTime() + unit.toNanos(delay);
+    }
+
+    /**
      * Creates a new task to be executed at the given time.
      * It is user's responsibility to add the {@link System#nanoTime()} value
-     * to the delay he wants to wait.
+     * to the delay (s)he wants to wait.
      *
      * @param timestamp  time of execution of this task, in nanoseconds relative to {@link System#nanoTime()}.
      */
diff --git a/ide-project/NetBeans/build.xml b/ide-project/NetBeans/build.xml
index 3051311..8c66df3 100644
--- a/ide-project/NetBeans/build.xml
+++ b/ide-project/NetBeans/build.xml
@@ -116,6 +116,9 @@
       <fileset dir="${project.root}/application/sis-console/src/main/resources">
         <include name="**/*.properties"/>
       </fileset>
+      <fileset dir="${project.root}/profiles/sis-french-profile/src/main/resources">
+        <include name="**/*.lst"/>
+      </fileset>
     </copy>
 
 
diff --git a/profiles/sis-french-profile/src/main/java/org/apache/sis/internal/profile/fra/ProfileTypes.java b/profiles/sis-french-profile/src/main/java/org/apache/sis/internal/profile/fra/ProfileTypes.java
index 27c1783..1b7c4cb 100644
--- a/profiles/sis-french-profile/src/main/java/org/apache/sis/internal/profile/fra/ProfileTypes.java
+++ b/profiles/sis-french-profile/src/main/java/org/apache/sis/internal/profile/fra/ProfileTypes.java
@@ -25,7 +25,7 @@ import org.apache.sis.internal.jaxb.TypeRegistration;
  * This class is declared in the {@code META-INF/services/org.apache.sis.internal.jaxb.TypeRegistration} file.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -42,4 +42,16 @@ public final class ProfileTypes extends TypeRegistration {
         addTo.add(LegalConstraints.class);
         addTo.add(SecurityConstraints.class);
     }
+
+    /**
+     * Returns {@code true} for {@code export = false} in order to notify that we provide
+     * a {@code "RenameOnImport.lst"} file that need to be read.
+     *
+     * @param  export  {@code true} for {@code "RenameOnImport.lst"}, {@code false} for {@code "RenameOnImport.lst"}.
+     * @return {@code true} for {@code "RenameOnImport.lst"}, {@code false} otherwise.
+     */
+    @Override
+    protected boolean hasRenameFile(boolean export) {
+        return !export;
+    }
 }
diff --git a/profiles/sis-french-profile/src/main/java/org/apache/sis/internal/profile/fra/package-info.java b/profiles/sis-french-profile/src/main/java/org/apache/sis/internal/profile/fra/package-info.java
index 224e525..e94357b 100644
--- a/profiles/sis-french-profile/src/main/java/org/apache/sis/internal/profile/fra/package-info.java
+++ b/profiles/sis-french-profile/src/main/java/org/apache/sis/internal/profile/fra/package-info.java
@@ -23,7 +23,7 @@
  *
  * @author  Cédric Briançon (Geomatys)
  * @author  Guilhem Legal (Geomatys)
- * @version 0.4
+ * @version 1.0
  *
  * @see org.apache.sis.profile.france
  *
diff --git a/profiles/sis-french-profile/src/main/resources/org/apache/sis/internal/profile/fra/RenameOnImport.lst b/profiles/sis-french-profile/src/main/resources/org/apache/sis/internal/profile/fra/RenameOnImport.lst
new file mode 100644
index 0000000..fc0fa04
--- /dev/null
+++ b/profiles/sis-french-profile/src/main/resources/org/apache/sis/internal/profile/fra/RenameOnImport.lst
@@ -0,0 +1,10 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements;
+# and to You under the Apache License, Version 2.0.
+#
+http://www.cnig.gouv.fr/2005/fra
+ FRA_DataIdentification : MD_DataIdentification
+ FRA_DirectReferenceSystem : MD_ReferenceSystem
+ FRA_IndirectReferenceSystem : MD_ReferenceSystem
+ FRA_Constraints : MD_Constraints
+ FRA_LegalConstraints : MD_LegalConstraints
+ FRA_SecurityConstraints : MD_SecurityConstraints
diff --git a/profiles/sis-french-profile/src/test/java/org/apache/sis/internal/profile/fra/DataIdentificationTest.java b/profiles/sis-french-profile/src/test/java/org/apache/sis/internal/profile/fra/DataIdentificationTest.java
index 1c800a5..a9435ba 100644
--- a/profiles/sis-french-profile/src/test/java/org/apache/sis/internal/profile/fra/DataIdentificationTest.java
+++ b/profiles/sis-french-profile/src/test/java/org/apache/sis/internal/profile/fra/DataIdentificationTest.java
@@ -18,7 +18,6 @@ package org.apache.sis.internal.profile.fra;
 
 import javax.xml.bind.JAXBException;
 import org.apache.sis.test.xml.TestCase;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import static org.apache.sis.test.MetadataAssert.*;
@@ -42,7 +41,6 @@ public final strictfp class DataIdentificationTest extends TestCase {
      * @see <a href="https://issues.apache.org/jira/browse/SIS-404">SIS-404</a>
      */
     @Test
-    @Ignore("Verify if we should discontinue this profile.")
     public void testMarshalling() throws JAXBException {
         final String xml =
                 "<fra:FRA_DataIdentification xmlns:gmd=\"http://www.isotc211.org/2005/gmd\"" +


Mime
View raw message