Author: desruisseaux
Date: Fri Feb  9 19:14:03 2018
New Revision: 1823695

URL: http://svn.apache.org/viewvc?rev=1823695&view=rev
Log:
Improve AnnotationConsistencyCheck and fix some JAXB annotations as a result of 
those verifications.
Some GeoAPI annotations have also been fixed as a result of this work and those 
fixes are required by SIS:
https://github.com/opengeospatial/geoapi/commit/af650d33c567d6d11420d7ba16822fb4f67f5b08
Tests in sis-metadata and downstream modules have not yet been fully fixed.

Modified:
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/MD_Scope.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultBand.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultSampleDimension.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/package-info.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultUsability.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataConsistencyCheck.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/AnnotationConsistencyCheck.java
    
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
    
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Namespaces.java
    
sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/NamespacesTest.java

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/MD_Scope.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/MD_Scope.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/MD_Scope.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/MD_Scope.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -60,7 +60,7 @@ public class MD_Scope extends PropertyTy
 
     /**
      * Invoked by {@link PropertyType} at marshalling time for wrapping the 
given metadata value
-     * in a {@code <gmd:DQ_Scope>} XML element.
+     * in a {@code <mcc:MD_Scope>} XML element.
      *
      * @param  metadata  the metadata element to marshall.
      * @return a {@code PropertyType} wrapping the given the metadata element.
@@ -72,7 +72,7 @@ public class MD_Scope extends PropertyTy
 
     /**
      * Invoked by JAXB at marshalling time for getting the actual metadata to 
write
-     * inside the {@code <gmd:DQ_Scope>} XML element.
+     * inside the {@code <mcc:MD_Scope>} XML element.
      * This is the value or a copy of the value given in argument to the 
{@code wrap} method.
      *
      * @return the metadata to be marshalled.

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -1575,7 +1575,6 @@ public class DefaultMetadata extends ISO
     /**
      * Sets the default locale for this record (used in ISO 19115-3 format).
      */
-    @SuppressWarnings("unused")
     private void setDefaultLocale(final Locale newValue) {
         setLanguages(OtherLocales.setFirst(languages, newValue)); // See "Note 
about deprecated methods implementation"
     }
@@ -1591,7 +1590,6 @@ public class DefaultMetadata extends ISO
     /**
      * Sets the other locales for this record (used in ISO 19115-3 format).
      */
-    @SuppressWarnings("unused")
     private void setOtherLocales(final Collection<? extends Locale> newValues) 
{
         setLanguages(OtherLocales.merge(CollectionsExt.first(languages), 
newValues));
     }

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -344,12 +344,12 @@ public class DefaultResponsibleParty ext
     @XmlElement(name = "contactInfo")
     public Contact getContactInfo() {
         final Collection<Party> parties = getParties();
-        if (parties != null) { // May be null on marshalling.
+        if (parties != null) {                                          // May 
be null on marshalling.
             for (final Party party : parties) {
                 final Collection<? extends Contact> contacts = 
party.getContactInfo();
-                if (contacts != null) { // May be null on marshalling.
+                if (contacts != null) {                                 // May 
be null on marshalling.
                     for (final Contact contact : contacts) {
-                        if (contact != null) { // Paranoiac check.
+                        if (contact != null) {                          // 
Paranoiac check.
                             return contact;
                         }
                     }

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultBand.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultBand.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultBand.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultBand.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -346,7 +346,10 @@ public class DefaultBand extends Default
     }
 
     /**
-     * {@inheritDoc}
+     * Returns the smallest distance between which separate points can be 
distinguished,
+     * as specified in instrument design.
+     *
+     * @return {@inheritDoc}
      */
     @Override
     @ValueRange(minimum = 0, isMinIncluded = false)
@@ -364,7 +367,9 @@ public class DefaultBand extends Default
     }
 
     /**
-     * {@inheritDoc}
+     * Returns type of transfer function to be used when scaling a physical 
value for a given element.
+     *
+     * @return {@inheritDoc}
      */
     @Override
     @XmlElement(name = "transferFunctionType")

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultSampleDimension.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultSampleDimension.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultSampleDimension.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultSampleDimension.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -403,6 +403,12 @@ public class DefaultSampleDimension exte
     /**
      * Returns type of transfer function to be used when scaling a physical 
value for a given element.
      *
+     * <div class="note"><b>Note on XML marshalling:</b>
+     * ISO 19115-2 defines this property in {@linkplain DefaultBand a subtype} 
for historical reasons.
+     * Apache SIS moves this property up in the hierarchy since this property 
can apply to any sample dimension,
+     * not only the measurements in the electromagnetic spectrum. However this 
property will not appear in XML
+     * documents unless this {@code SampleDimension} is actually a {@code 
Band}.</div>
+     *
      * @return type of transfer function, or {@code null}.
      */
     @Override
@@ -452,6 +458,12 @@ public class DefaultSampleDimension exte
      * Returns the smallest distance between which separate points can be 
distinguished,
      * as specified in instrument design.
      *
+     * <div class="note"><b>Note on XML marshalling:</b>
+     * ISO 19115-2 defines this property in {@linkplain DefaultBand a subtype} 
for historical reasons.
+     * Apache SIS moves this property up in the hierarchy since this property 
can apply to any sample dimension,
+     * not only the measurements in the electromagnetic spectrum. However this 
property will not appear in XML
+     * documents unless this {@code SampleDimension} is actually a {@code 
Band}.</div>
+     *
      * @return smallest distance between which separate points can be 
distinguished, or {@code null}.
      */
     @Override

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -268,7 +268,7 @@ public class DefaultDataFile extends ISO
      */
     @Override
     @Deprecated
-    @XmlElement(name = "fileFormat", namespace = LegacyNamespaces.GMD)
+    @XmlElement(name = "fileFormat", namespace = LegacyNamespaces.GMX)
     public Format getFileFormat() {
         return FilterByVersion.LEGACY_METADATA.accept() ? fileFormat : null;
     }

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/package-info.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/package-info.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/package-info.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/package-info.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -80,7 +80,8 @@
                 @XmlNs(prefix = "mrd", namespaceURI = Namespaces.MRD),      // 
Metadata for Resource Distribution
                 @XmlNs(prefix = "mdt", namespaceURI = Namespaces.MDT),      // 
Metadata for Data Transfer
                 @XmlNs(prefix = "mcc", namespaceURI = Namespaces.MCC),      // 
Metadata Common Classes
-                @XmlNs(prefix = "gmd", namespaceURI = LegacyNamespaces.GMD)
+                @XmlNs(prefix = "gmd", namespaceURI = LegacyNamespaces.GMD),
+                @XmlNs(prefix = "gmx", namespaceURI = LegacyNamespaces.GMX)
 })
 @XmlAccessorType(XmlAccessType.NONE)
 @XmlJavaTypeAdapters({

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -327,6 +327,7 @@ public class DefaultResolution extends I
      */
     @Override
     @ValueRange(minimum=0, isMinIncluded=false)
+    @XmlElement(name = "angularDistance")
     @XmlJavaTypeAdapter(GO_Real.Since2014.class)
     public Double getAngularDistance() {
         return (property == ANGULAR) ? (Double) value : null;

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultUsability.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultUsability.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultUsability.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultUsability.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -19,7 +19,7 @@ package org.apache.sis.metadata.iso.qual
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlRootElement;
 import org.opengis.metadata.quality.Usability;
-import org.apache.sis.xml.Namespaces;
+import org.apache.sis.internal.jaxb.LegacyNamespaces;
 
 
 /**
@@ -43,10 +43,13 @@ import org.apache.sis.xml.Namespaces;
  * @version 1.0
  * @since   0.3
  * @module
+ *
+ * @deprecated Not found in ISO 19115-3:2016 schemas.
  */
+@Deprecated
 @SuppressWarnings("CloneableClassWithoutClone")                 // 
ModifiableMetadata needs shallow clones.
-@XmlType(name = "QE_Usability_Type", namespace = Namespaces.GMI)
-@XmlRootElement(name = "QE_Usability", namespace = Namespaces.GMI)
+@XmlType(name = "QE_Usability_Type", namespace = LegacyNamespaces.GMI)
+@XmlRootElement(name = "QE_Usability", namespace = LegacyNamespaces.GMI)
 public class DefaultUsability extends AbstractElement implements Usability {
     /**
      * Serial number for inter-operability with different versions.

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataConsistencyCheck.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataConsistencyCheck.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataConsistencyCheck.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataConsistencyCheck.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -34,8 +34,6 @@ import org.apache.sis.test.TestUtilities
 import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
-import static org.opengis.test.Assert.*;
-
 
 /**
  * Base class for tests done on metadata objects using reflection. This base 
class tests JAXB annotations
@@ -185,7 +183,9 @@ public abstract strictfp class MetadataC
 
     /**
      * For every properties in every non-{@code Codelist} types listed in the 
{@link #types} array,
-     * tests the property values. This method performs the tests documented in 
class javadoc.
+     * tests the property values. This method verifies that all {@link 
AbstractMetadata} instances
+     * are initially {@linkplain AbstractMetadata#isEmpty() empty}, all getter 
methods return a null
+     * singleton or an empty collection, and tests setting a random value.
      */
     @Test
     public void testPropertyValues() {
@@ -194,12 +194,11 @@ public abstract strictfp class MetadataC
             if (!ControlledVocabulary.class.isAssignableFrom(type)) {
                 final Class<?> impl = getImplementation(type);
                 if (impl != null) {
-                    assertTrue(type.isAssignableFrom(impl));
+                    assertTrue("Not an implementation of expected interface.", 
type.isAssignableFrom(impl));
                     testPropertyValues(new 
PropertyAccessor(standard.getCitation(), type, impl, impl));
                 }
             }
         }
-        done();
     }
 
     /**
@@ -254,7 +253,7 @@ public abstract strictfp class MetadataC
             if (value == null) {
                 assertFalse("Null values are not allowed to be collections.", 
isCollection);
             } else {
-                assertInstanceOf("Wrong property type.", propertyType, value);
+                assertTrue("Wrong property type.", 
propertyType.isInstance(value));
                 if (value instanceof CheckedContainer<?>) {
                     assertTrue("Wrong element type in collection.",
                             
elementType.isAssignableFrom(((CheckedContainer<?>) value).getElementType()));
@@ -272,6 +271,10 @@ public abstract strictfp class MetadataC
                 fail("Non writable property: " + accessor + '.' + property);
             }
             if (isWritable) {
+                if (Date.class.isAssignableFrom(accessor.type(i, 
TypeValuePolicy.ELEMENT_TYPE))) {
+                    // Dates requires sis-temporal module, which is not 
available for sis-metadata.
+                    continue;
+                }
                 final Object newValue = sampleValueFor(property, elementType);
                 final Object oldValue = accessor.set(i, instance, newValue, 
PropertyAccessor.RETURN_PREVIOUS);
                 assertEquals("PropertyAccessor.set(…) shall return the value 
previously returned by get(…).", value, oldValue);

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -16,22 +16,17 @@
  */
 package org.apache.sis.metadata.iso;
 
-import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import org.opengis.util.ControlledVocabulary;
 import org.opengis.annotation.UML;
-import org.opengis.annotation.Specification;
+import org.opengis.util.ControlledVocabulary;
 import org.apache.sis.internal.jaxb.Context;
 import org.apache.sis.metadata.MetadataStandard;
 import org.apache.sis.metadata.MetadataConsistencyCheck;
 import org.apache.sis.test.LoggingWatcher;
 import org.apache.sis.test.DependsOn;
-import org.apache.sis.xml.Namespaces;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
-
 
 /**
  * Tests all known {@link ISOMetadata} subclasses for JAXB annotations and 
getter/setter methods.
@@ -212,7 +207,9 @@ public final strictfp class AllMetadataT
     }
 
     /**
-     * Performs the test documented in the {@link MetadataConsistencyCheck} 
javadoc.
+     * {@inheritDoc}
+     * Once the test is completed, this method verifies that the expected 
warnings have been logged,
+     * and no unexpected logging occurred.
      */
     @Test
     @Override
@@ -226,87 +223,7 @@ public final strictfp class AllMetadataT
     }
 
     /**
-     * Returns the name of the XML element for the given UML element.
-     * This method checks for the special cases which are known to have 
different UML and XML names.
-     *
-     * @return {@inheritDoc}
-     */
-    @Override
-    protected String getExpectedXmlElementName(final Class<?> enclosing, final 
UML uml) {
-        String name = super.getExpectedXmlElementName(enclosing, uml);
-        switch (name) {
-            case "MD_Scope": {                  // ISO 19115:2014
-                name = "DQ_Scope";              // ISO 19115:2003
-                break;
-            }
-            case "distributedComputingPlatform": {
-                name = "DCP";
-                break;
-            }
-            case "stepDateTime": {
-                name = "dateTime";
-                break;
-            }
-            case "locale": {
-                if (enclosing == 
org.opengis.metadata.content.FeatureCatalogueDescription.class) {
-                    name = "language";
-                }
-                break;
-            }
-        }
-        return name;
-    }
-
-    /**
-     * Returns the expected namespace for an element defined by the given 
specification.
-     * For example the namespace of any type defined by {@link 
Specification#ISO_19115}
-     * is {@code "http://www.isotc211.org/2005/gmd"}.
-     *
-     * @return {@inheritDoc}
-     */
-    @Override
-    protected String getExpectedNamespace(final Class<?> impl, final 
Specification specification) {
-        if (impl == 
org.apache.sis.metadata.iso.identification.DefaultCoupledResource.class ||
-            impl == 
org.apache.sis.metadata.iso.identification.DefaultOperationChainMetadata.class 
||
-            impl == 
org.apache.sis.metadata.iso.identification.DefaultOperationMetadata.class ||
-            impl == 
org.apache.sis.metadata.iso.identification.DefaultServiceIdentification.class)
-        {
-            assertEquals(Specification.ISO_19115, specification);
-            return Namespaces.SRV;
-        }
-        return super.getExpectedNamespace(impl, specification);
-    }
-
-    /**
-     * Returns the type of the given element, or {@code null} if the type is 
not yet
-     * determined (the later cases could change in a future version).
-     *
-     * @return {@inheritDoc}
-     */
-    @Override
-    protected String getExpectedXmlTypeForElement(final Class<?> type, final 
Class<?> impl) {
-        final String rootName = type.getAnnotation(UML.class).identifier();
-        switch (rootName) {
-            // Following prefix was changed in ISO 19115 corrigendum,
-            // but ISO 19139:2007 still use the old prefix.
-            case "SV_ServiceIdentification": {
-                return "MD_ServiceIdentification_Type";
-            }
-            // Following prefix was changed in ISO 19115:2014,
-            // but ISO 19139:2007 still use the old prefix.
-            case "MD_Scope": {
-                return "DQ_Scope_Type";
-            }
-        }
-        final StringBuilder buffer = new StringBuilder(rootName.length() + 13);
-        if (impl.getSimpleName().startsWith("Abstract")) {
-            buffer.append("Abstract");
-        }
-        return buffer.append(rootName).append("_Type").toString();
-    }
-
-    /**
-     * Returns the ISO 19139 wrapper for the given GeoAPI type,
+     * Returns the ISO 19115-3 wrapper for the given GeoAPI type,
      * or {@code null} if no adapter is expected for the given type.
      *
      * @return {@inheritDoc}
@@ -323,28 +240,19 @@ public final strictfp class AllMetadataT
             return null;
         }
         final String classname = "org.apache.sis.internal.jaxb." +
-              (ControlledVocabulary.class.isAssignableFrom(type) ? "code" : 
"metadata") +
-              '.' + type.getAnnotation(UML.class).identifier();
+                (ControlledVocabulary.class.isAssignableFrom(type) ? "code" : 
"metadata") +
+                '.' + type.getAnnotation(UML.class).identifier();
         final Class<?> wrapper = Class.forName(classname);
-        assertTrue("Expected a final class for " + wrapper.getName(), 
Modifier.isFinal(wrapper.getModifiers()));
-        return wrapper;
-    }
-
-    /**
-     * Returns {@code true} if the given method is a non-standard extension.
-     * If {@code true}, then {@code method} does not need to have UML 
annotation.
-     *
-     * @param  method  the method to verify.
-     * @return {@code true} if the given method is an extension, or {@code 
false} otherwise.
-     *
-     * @since 0.5
-     */
-    @Override
-    protected boolean isExtension(final Method method) {
-        if 
(org.opengis.metadata.distribution.StandardOrderProcess.class.isAssignableFrom(method.getDeclaringClass()))
 {
-            return method.getName().equals("getCurrency");
+        Class<?>[] expectedFinalClasses = wrapper.getClasses();   // 
"Since2014" internal class.
+        if (expectedFinalClasses.length == 0) {
+            expectedFinalClasses = new Class<?>[] {wrapper};      // If no 
"Since2014", then wrapper itself should be final.
+        }
+        for (final Class<?> c : expectedFinalClasses) {
+            if (!Modifier.isFinal(c.getModifiers())) {
+                fail("Expected a final class for " + c.getName());
+            }
         }
-        return super.isExtension(method);
+        return wrapper;
     }
 
     /**

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/AnnotationConsistencyCheck.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/AnnotationConsistencyCheck.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/AnnotationConsistencyCheck.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/AnnotationConsistencyCheck.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -18,8 +18,11 @@ package org.apache.sis.test.xml;
 
 import java.util.Set;
 import java.util.HashSet;
+import java.util.Objects;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import javax.xml.XMLConstants;
 import javax.xml.bind.annotation.XmlNs;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlSchema;
@@ -27,20 +30,22 @@ import javax.xml.bind.annotation.XmlElem
 import javax.xml.bind.annotation.XmlElementRef;
 import javax.xml.bind.annotation.XmlElementRefs;
 import javax.xml.bind.annotation.XmlRootElement;
-import org.opengis.util.CodeList;
-import org.opengis.util.ControlledVocabulary;
 import org.opengis.annotation.UML;
+import org.opengis.annotation.Classifier;
+import org.opengis.annotation.Stereotype;
 import org.opengis.annotation.Obligation;
 import org.opengis.annotation.Specification;
+import org.opengis.util.CodeList;
+import org.opengis.util.ControlledVocabulary;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.xml.Namespaces;
+import org.apache.sis.internal.jaxb.Schemas;
+import org.apache.sis.internal.jaxb.LegacyNamespaces;
 import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
-import org.junit.After;
 import org.junit.Test;
-
-import static org.junit.Assert.*;
-import static org.apache.sis.test.TestUtilities.getSingleton;
+import junit.framework.AssertionFailedError;
 
 
 /**
@@ -50,11 +55,10 @@ import static org.apache.sis.test.TestUt
  * <ul>
  *   <li>All implementation classes have {@link XmlRootElement} and {@link 
XmlType} annotations.</li>
  *   <li>The name declared in the {@code XmlType} annotations matches the
- *       {@link #getExpectedXmlTypeForElement expected value}.</li>
+ *       {@link #getExpectedXmlTypeName expected value}.</li>
  *   <li>The name declared in the {@code XmlRootElement} (classes) or {@link 
XmlElement} (methods)
  *       annotations matches the identifier declared in the {@link UML} 
annotation of the GeoAPI interfaces.
- *       The UML - XML name mapping can be changed by overriding {@link 
#getExpectedXmlElementName(Class, UML)} and
- *       {@link #getExpectedXmlElementName(Class, UML)}.</li>
+ *       The UML - XML name mapping can be changed by overriding {@link 
#getExpectedXmlElementName(Class, UML)}.</li>
  *   <li>The {@code XmlElement.required()} boolean is consistent with the UML 
{@linkplain Obligation obligation}.</li>
  *   <li>The namespace declared in the {@code XmlRootElement} or {@code 
XmlElement} annotations
  *       is not redundant with the {@link XmlSchema} annotation in the 
package.</li>
@@ -80,30 +84,37 @@ public abstract strictfp class Annotatio
 
     /**
      * The GeoAPI interfaces, {@link CodeList} or {@link Enum} types to test.
+     * This array is specified at construction time. Each test iterates over
+     * all types in this array.
      */
     protected final Class<?>[] types;
 
     /**
      * The type being tested, or {@code null} if none. In case of test 
failure, this information
-     * will be used by {@link #printFailureLocation()} for formatting a 
message giving the name
-     * of class and method where the failure occurred.
+     * will be used by the {@code assert(…)} methods for formatting a message 
giving the name of
+     * class and method where the failure occurred.
+     *
+     * @see #fail(String)
      */
     protected String testingClass;
 
     /**
      * The method being tested, or {@code null} if none. In case of test 
failure, this information
-     * will be used by {@link #printFailureLocation()} for formatting a 
message giving the name of
+     * will be used by the {@code assert(…)} methods for formatting a message 
giving the name of
      * class and method where the failure occurred.
+     *
+     * @see #fail(String)
      */
     protected String testingMethod;
 
     /**
      * Creates a new test suite for the given types.
+     * The given sequence of types is assigned to the {@link #types} field.
      *
-     * @param types The GeoAPI interfaces, {@link CodeList} or {@link Enum} 
types to test.
+     * @param  types  the GeoAPI interfaces, {@link CodeList} or {@link Enum} 
types to test.
      */
     protected AnnotationConsistencyCheck(final Class<?>... types) {
-        this.types = types;
+        this.types = types;     // No need to clone — test classes are 
normally used only by SIS.
     }
 
     /**
@@ -175,8 +186,8 @@ public abstract strictfp class Annotatio
      * of the given class if {@code getWrapperFor(Class)} threw {@code 
ClassNotFoundException}.
      *
      * @param  type  the GeoAPI interface, {@link CodeList} or {@link Enum} 
type.
-     * @return the wrapper for the given type. {@link WrapperClass#type} is 
{@code null} if
-     *         no wrapper has been found.
+     * @return the wrapper for the given type.
+     *         {@link WrapperClass#type} is {@code null} if no wrapper has 
been found.
      * @throws ClassNotFoundException if a wrapper was expected but not found 
in the
      *         given type neither in any of the parent classes.
      */
@@ -198,66 +209,228 @@ public abstract strictfp class Annotatio
     }
 
     /**
-     * Returns the XML type for an element of the given type. For example in 
ISO 19115-3,
-     * the XML type of {@code CI_Citation} is {@code CI_Citation_Type}.
-     *
-     * @param  type  the GeoAPI interface.
-     * @param  impl  the implementation class.
-     * @return the name of the XML type for the given element, or {@code null} 
if none.
-     *
-     * @see #testImplementationAnnotations()
-     */
-    protected abstract String getExpectedXmlTypeForElement(Class<?> type, 
Class<?> impl);
-
-    /**
-     * Returns the expected namespace for an element defined by the given 
specification.
-     * For example the namespace of any type defined by {@link 
Specification#ISO_19115}
-     * is {@code "http://www.isotc211.org/2005/gmd"}.
+     * Returns the beginning of expected namespace for an element defined by 
the given UML.
+     * For example the namespace of most types defined by {@link 
Specification#ISO_19115}
+     * starts with is {@code "http://standards.iso.org/iso/19115/-3/"}.
      *
      * <p>The default implementation recognizes the
-     * {@linkplain Specification#ISO_19115 ISO 19115},
+     * {@linkplain Specification#ISO_19115   ISO 19115},
      * {@linkplain Specification#ISO_19115_2 ISO 19115-2},
-     * {@linkplain Specification#ISO_19139 ISO 19139} and
-     * {@linkplain Specification#ISO_19108 ISO 19108} specifications.
+     * {@linkplain Specification#ISO_19139   ISO 19139} and
+     * {@linkplain Specification#ISO_19108   ISO 19108} specifications.
      * Subclasses shall override this method if they need to support more 
namespaces.</p>
      *
+     * <p>Note that a more complete verification is done by {@link 
SchemaCompliance}.
+     * But the test done in this {@link AnnotationConsistencyCheck} class can 
be run without network access.</p>
+     *
      * <p>The prefix for the given namespace will be fetched by
      * {@link Namespaces#getPreferredPrefix(String, String)}.</p>
      *
-     * @param  impl           the implementation class, {@link CodeList} or 
{@link Enum} type.
-     * @param  specification  the specification that define the type, or 
{@code null} if unspecified.
+     * @param  impl  the implementation class, {@link CodeList} or {@link 
Enum} type.
+     * @param  uml   the UML associated to the class or the method.
      * @return the expected namespace.
-     * @throws IllegalArgumentException if the given specification is unknown 
to this method.
+     * @throws IllegalArgumentException if the given UML is unknown to this 
method.
      */
-    protected String getExpectedNamespace(final Class<?> impl, final 
Specification specification) {
-        switch (specification) {
-            case ISO_19115:   return Namespaces.GMD;
-            case ISO_19115_2: return Namespaces.GMI;
-            case ISO_19139:   return Namespaces.GMX;
-            case ISO_19108:   return Namespaces.GMD;
-            default: throw new 
IllegalArgumentException(specification.toString());
+    @SuppressWarnings("deprecation")
+    protected String getExpectedNamespaceStart(final Class<?> impl, final UML 
uml) {
+        final String identifier = uml.identifier();
+        switch (identifier) {
+            case "SV_CoupledResource":
+            case "SV_OperationMetadata":
+            case "SV_OperationChainMetadata":
+            case "SV_ServiceIdentification": {              // Historical 
reasons (other standard integrated into ISO 19115)
+                assertEquals("Unexpected @Specification value.", 
Specification.ISO_19115, uml.specification());
+                assertEquals("Specification version should be latest ISO 
19115.", (short) 0, uml.version());
+                return Namespaces.SRV;
+            }
+            case "DQ_TemporalAccuracy":                     // Renamed 
DQ_TemporalQuality
+            case "DQ_NonQuantitativeAttributeAccuracy": {   // Renamed 
DQ_NonQuantitativeAttributeCorrectness
+                assertEquals("Unexpected @Specification value.", 
Specification.ISO_19115, uml.specification());
+                assertEquals("Specification version should be legacy ISO 
19115.", (short) 2003, uml.version());
+                return LegacyNamespaces.GMD;
+            }
+            case "role": {
+                if 
(org.opengis.metadata.citation.ResponsibleParty.class.isAssignableFrom(impl)) {
+                    return LegacyNamespaces.GMD;            // Override a 
method defined in Responsibility
+                }
+                break;
+            }
+            case "lineage": {
+                if 
(org.opengis.metadata.quality.DataQuality.class.isAssignableFrom(impl)) {
+                    return LegacyNamespaces.GMD;            // Deprecated 
property in a type not yet upgraded.
+                }
+                break;
+            }
+            case "nameOfMeasure":
+            case "measureIdentification":
+            case "measureDescription":
+            case "evaluationMethodType":
+            case "evaluationMethodDescription":
+            case "evaluationProcedure": {
+                if 
(org.opengis.metadata.quality.Element.class.isAssignableFrom(impl)) {
+                    return LegacyNamespaces.GMD;            // Deprecated 
property in a type not yet upgraded.
+                }
+                break;
+            }
+            case "dateTime": {
+                if 
(org.opengis.metadata.quality.Element.class.isAssignableFrom(impl)) {
+                    return Namespaces.DQC;
+                }
+                break;
+            }
+            case "fileFormat": {
+                if 
(org.opengis.metadata.distribution.DataFile.class.isAssignableFrom(impl)) {
+                    return LegacyNamespaces.GMX;            // Deprecated 
method (removed from ISO 19115-3:2016)
+                }
+                break;
+            }
+        }
+        /*
+         * GeoAPI has not yet been upgraded to ISO 19157. Interfaces in the 
"org.opengis.metadata.quality"
+         * package are still defined according the old specification. Those 
types have the "DQ_" or "QE_"
+         * prefix. This issue applies also to properties (starting with a 
lower case).
+         */
+        if (identifier.startsWith("DQ_")) {
+            assertEquals("Unexpected @Specification value.", 
Specification.ISO_19115, uml.specification());
+            assertEquals("Specification version should be legacy ISO 19115.", 
(short) 2003, uml.version());
+            return Namespaces.MDQ;
+        }
+        if (identifier.startsWith("QE_")) {
+            assertEquals("Unexpected @Specification value.", 
Specification.ISO_19115_2, uml.specification());
+            switch (uml.version()) {
+                case 0:    return Namespaces.MDQ;
+                case 2009: return LegacyNamespaces.GMI;
+                default: fail("Unexpected version number in " + uml);
+            }
+        }
+        if 
(org.opengis.metadata.quality.DataQuality.class.isAssignableFrom(impl) ||    // 
For properties in those types.
+            org.opengis.metadata.quality.Element.class.isAssignableFrom(impl) 
||
+            org.opengis.metadata.quality.Result.class.isAssignableFrom(impl))
+        {
+            return Namespaces.MDQ;
+        }
+        /*
+         * General cases (after we processed all the special cases)
+         * based on which standard defines the type or property.
+         */
+        if (uml.version() != 0) {
+            switch (uml.specification()) {
+                case ISO_19115:   return LegacyNamespaces.GMD;
+                case ISO_19115_2: return LegacyNamespaces.GMI;
+            }
+        }
+        switch (uml.specification()) {
+            case ISO_19115:
+            case ISO_19115_2:
+            case ISO_19115_3: return Schemas.METADATA_ROOT;
+            case ISO_19139:   return LegacyNamespaces.GMX;
+            case ISO_19108:   return LegacyNamespaces.GMD;
+            default: throw new IllegalArgumentException(uml.toString());
         }
     }
 
     /**
-     * Returns the name of the XML element for the given UML element.
-     * This method is invoked in two situations:
+     * Returns the name of the XML type for an interface described by the 
given UML.
+     * For example in ISO 19115-3, the XML type of {@code CI_Citation} is 
{@code CI_Citation_Type}.
+     * The default implementation returns {@link UML#identifier()}, possibly 
with {@code "Abstract"} prepended,
+     * and unconditionally with {@code "_Type"} appended.
+     * Subclasses shall override this method when mismatches are known to 
exist between the UML and XML type names.
      *
-     * <ul>
-     *   <li>For the root XML element name of an interface, in which case 
{@code enclosing} is {@code null}.</li>
-     *   <li>For the XML element name of a property (field or method) defined 
by an interface,
-     *       in which case {@code enclosing} is the interface containing the 
property.</li>
-     * </ul>
+     * @param  stereotype  the stereotype of the interface, or {@code null} if 
none.
+     * @param  uml         the UML of the interface for which to get the 
corresponding XML type name.
+     * @return the name of the XML type for the given element, or {@code null} 
if none.
+     *
+     * @see #testImplementationAnnotations()
+     */
+    protected String getExpectedXmlTypeName(final Stereotype stereotype, final 
UML uml) {
+        final String rootName = uml.identifier();
+        final StringBuilder buffer = new StringBuilder(rootName.length() + 13);
+        if (Stereotype.ABSTRACT.equals(stereotype)) {
+            buffer.append("Abstract");
+        }
+        return buffer.append(rootName).append("_Type").toString();
+    }
+
+    /**
+     * Returns the name of the XML root element for an interface described by 
the given UML.
+     * The default implementation returns {@link UML#identifier()}, possibly 
with {@code "Abstract"} prepended.
+     * Subclasses shall override this method when mismatches are known to 
exist between the UML and XML element names.
+     *
+     * @param  stereotype  the stereotype of the interface, or {@code null} if 
none.
+     * @param  uml         the UML of the interface for which to get the 
corresponding XML root element name.
+     * @return the name of the XML root element for the given UML.
+     *
+     * @see #testImplementationAnnotations()
+     */
+    protected String getExpectedXmlRootElementName(final Stereotype 
stereotype, final UML uml) {
+        String name = uml.identifier();
+        if (Stereotype.ABSTRACT.equals(stereotype)) {
+            name = "Abstract".concat(name);
+        }
+        return name;
+    }
+
+    /**
+     * Returns the name of the XML element for a method described by the given 
UML.
+     * This method is invoked for a property (field or method) defined by an 
interface.
+     * The {@code enclosing} argument is the interface containing the property.
      *
-     * The default implementation returns {@link UML#identifier()}. Subclasses 
shall override this method
-     * when mismatches are known to exist between the UML and XML element 
names.
+     * <p>The default implementation returns {@link UML#identifier()}. 
Subclasses shall override this method
+     * when mismatches are known to exist between the UML and XML element 
names.</p>
      *
      * @param  enclosing  the GeoAPI interface which contains the property, or 
{@code null} if none.
      * @param  uml        the UML element for which to get the corresponding 
XML element name.
      * @return the XML element name for the given UML element.
+     *
+     * @see #testMethodAnnotations()
      */
     protected String getExpectedXmlElementName(final Class<?> enclosing, final 
UML uml) {
-        return uml.identifier();
+        String name = uml.identifier();
+        switch (name) {
+            case "stepDateTime": {
+                if 
(org.opengis.metadata.lineage.ProcessStep.class.isAssignableFrom(enclosing)) {
+                    name = "dateTime";
+                }
+                break;
+            }
+            case "satisfiedPlan": {
+                if 
(org.opengis.metadata.acquisition.Requirement.class.isAssignableFrom(enclosing))
 {
+                    name = "satisifiedPlan";                // Mispelling in 
ISO 19115-3:2016
+                }
+                break;
+            }
+            case "meteorologicalConditions": {
+                if 
(org.opengis.metadata.acquisition.EnvironmentalRecord.class.isAssignableFrom(enclosing))
 {
+                    name = "meterologicalConditions";       // Mispelling in 
ISO 19115-3:2016
+                }
+                break;
+            }
+            case "detectedPolarization": {
+                if 
(org.opengis.metadata.content.Band.class.isAssignableFrom(enclosing)) {
+                    name = "detectedPolarisation";          // Spelling change 
in XSD files
+                }
+                break;
+            }
+            case "transmittedPolarization": {
+                if 
(org.opengis.metadata.content.Band.class.isAssignableFrom(enclosing)) {
+                    name = "transmittedPolarisation";       // Spelling change 
in XSD files
+                }
+                break;
+            }
+            case "featureType": {
+                if 
(org.opengis.metadata.distribution.DataFile.class.isAssignableFrom(enclosing)) {
+                    name = "featureTypes";                  // Spelling change 
in XSD files
+                }
+                break;
+            }
+            case "valueType": {
+                if 
(org.opengis.metadata.quality.Result.class.isAssignableFrom(enclosing)) {
+                    return "valueRecordType";
+                }
+                break;
+            }
+        }
+        return name;
     }
 
     /**
@@ -267,11 +440,11 @@ public abstract strictfp class Annotatio
      * <ul>
      *   <li>The namespace is not redundant with the package-level {@link 
XmlSchema} namespace.</li>
      *   <li>The namespace is declared in a package-level {@link XmlNs} 
annotation.</li>
-     *   <li>The namespace is equals to the {@linkplain #getExpectedNamespace 
expected namespace}.</li>
+     *   <li>The namespace starts with the {@linkplain 
#getExpectedNamespaceStart expected namespace}.</li>
      * </ul>
      *
      * @param  namespace  the namespace given by the {@code @XmlRootElement} 
or {@code @XmlElement} annotation.
-     * @param  impl       the implementation or wrapper class for which to get 
the package namespace.
+     * @param  impl       the implementation or wrapper class from which to 
get the package namespace.
      * @param  uml        the {@code @UML} annotation, or {@code null} if none.
      * @return the actual namespace (same as {@code namespace} if it was not 
{@value #DEFAULT}).
      */
@@ -283,32 +456,45 @@ public abstract strictfp class Annotatio
          * given namespace is not redundant with that package-level namespace.
          */
         final XmlSchema schema = 
impl.getPackage().getAnnotation(XmlSchema.class);
-        assertNotNull("Missing @XmlSchema package annotation.", schema);
-        final String schemaNamespace = schema.namespace();
-        assertFalse("Missing namespace in @XmlSchema package annotation.", 
schemaNamespace.trim().isEmpty());
-        assertFalse("Namespace declaration is redundant with @XmlSchema.", 
namespace.equals(schemaNamespace));
+        assertNotNull("Missing @XmlSchema annotation in package-info.", 
schema);
+        final String schemaNamespace = schema.namespace();      // May be 
XMLConstants.NULL_NS_URI
+        assertFalse("Namespace declaration is redundant with package-info 
@XmlSchema.", namespace.equals(schemaNamespace));
+        /*
+         * Resolve the namespace given in argument: using the class-level 
namespace if needed,
+         * or the package-level namespace if the class-level one is not 
defined.
+         */
+        if (DEFAULT.equals(namespace)) {
+            final XmlType type = impl.getAnnotation(XmlType.class);
+            if (type == null || DEFAULT.equals(namespace = type.namespace())) {
+                namespace = schemaNamespace;
+            }
+            assertFalse("No namespace defined.", 
XMLConstants.NULL_NS_URI.equals(namespace));
+        }
         /*
          * Check that the namespace is declared in the package-level @XmlNs 
annotation.
          * We do not verify the validity of those @XmlNs annotations, since 
this is the
          * purpose of the 'testPackageAnnotations()' method.
          */
-        if (!DEFAULT.equals(namespace)) {
-            boolean found = false;
-            for (final XmlNs ns : schema.xmlns()) {
-                if (namespace.equals(ns.namespaceURI())) {
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) {
-                fail("Namespace for " + impl + " is not declared in the 
package @XmlSchema.xmlns().");
+        boolean found = false;
+        for (final XmlNs ns : schema.xmlns()) {
+            if (namespace.equals(ns.namespaceURI())) {
+                found = true;
+                break;
             }
-        } else {
-            namespace = schemaNamespace;
         }
+        if (!found) {
+            fail("Namespace for " + impl + " is not declared in the package 
@XmlSchema.xmlns().");
+        }
+        /*
+         * Check that the namespace is one of the namespaces controlled by the 
specification.
+         * We check only the namespace start, since some specifications define 
many namespaces
+         * under a common root (e.g. "http://standards.iso.org/iso/19115/-3/";).
+         */
         if (uml != null) {
-            assertEquals("Wrong namespace for the ISO specification.",
-                    getExpectedNamespace(impl, uml.specification()), 
namespace);
+            final String expected = getExpectedNamespaceStart(impl, uml);
+            if (!namespace.startsWith(expected)) {
+                fail("Expected " + expected + "… namespace for that ISO 
specification but got " + namespace);
+            }
         }
         return namespace;
     }
@@ -355,50 +541,40 @@ public abstract strictfp class Annotatio
     }
 
     /**
-     * Gets the {@link XmlElement} annotation for the no-argument method of 
the given name
-     * in the given implementation class. If the method is not annotated, then 
fallback on
-     * a field having the same name than the UML identifier. If no such field 
is found or
-     * is annotated, returns {@code null}.
-     *
-     * @param  impl    the implementation class.
-     * @param  method  the name of the getter method to search for.
-     * @param  uml     the UML annotation on the GeoAPI interface, or {@code 
null} if none.
-     * @return the {@code XmlElement}, or {@code null} if none.
-     */
-    private static XmlElement getXmlElement(final Class<?> impl, final String 
method, final UML uml) {
-        XmlElement element = null;
-        try {
-            element = impl.getMethod(method, (Class<?>[]) 
null).getAnnotation(XmlElement.class);
-            if (element == null && uml != null) {
-                element = 
impl.getDeclaredField(uml.identifier()).getAnnotation(XmlElement.class);
-            }
-        } catch (NoSuchMethodException ex) {
-            fail("Missing implementation: " + ex);
-        } catch (NoSuchFieldException ex) {
-            // Ignore - we will consider that there is no annotation.
-        }
-        return element;
-    }
-
-    /**
-     * Returns {@code true} if the given method should be ignored.
-     * This method returns {@code true} for some standard methods from the JDK.
-     */
-    private static boolean isIgnored(final Method method) {
-        final String name = method.getName();
-        return name.equals("equals") || name.equals("hashCode") || 
name.equals("doubleValue");
-    }
-
-    /**
-     * Returns {@code true} if the given method is a non-standard extension.
-     * If {@code true}, then {@code method} does not need to have UML 
annotation.
+     * Returns {@code true} if the given method should be ignored,
+     * either because it is a standard method from the JDK or because it is a 
non-standard extension.
+     * If {@code true}, then {@code method} does not need to have {@link UML} 
or {@link XmlElement} annotation.
      *
      * @param  method  the method to verify.
-     * @return {@code true} if the given method is an extension, or {@code 
false} otherwise.
+     * @return {@code true} if the given method should be ignored, or {@code 
false} otherwise.
      *
      * @since 0.5
      */
-    protected boolean isExtension(final Method method) {
+    protected boolean isIgnored(final Method method) {
+        switch (method.getName()) {
+            /*
+             * GeoAPI extension for inter-operability with JDK API, not 
defined in ISO specification.
+             */
+            case "getCurrency": {
+                return 
org.opengis.metadata.distribution.StandardOrderProcess.class.isAssignableFrom(method.getDeclaringClass());
+            }
+            /*
+             * ISO 19115-2 properties moved from MI_Band to MD_SampleDimension 
by GeoAPI.
+             * Must be taken in account only when checking the Band subtype.
+             */
+            case "getNominalSpatialResolution":
+            case "getTransferFunctionType": {
+                final Class<?> dc = method.getDeclaringClass();
+                return 
org.opengis.metadata.content.SampleDimension.class.isAssignableFrom(dc)
+                        && 
!org.opengis.metadata.content.Band.class.isAssignableFrom(dc);
+            }
+            /*
+             * Standard Java methods overridden in some GeoAPI interfaces for 
Javadoc purposes.
+             */
+            case "equals":
+            case "hashCode":
+            case "doubleValue": return true;
+        }
         return false;
     }
 
@@ -409,7 +585,7 @@ public abstract strictfp class Annotatio
      * <ul>
      *   <li>All elements in {@link #types} except code lists are 
interfaces.</li>
      *   <li>All elements in {@code types} have a {@link UML} annotation.</li>
-     *   <li>All methods expect deprecated methods and methods overriding JDK 
methods
+     *   <li>All methods except deprecated methods and methods overriding JDK 
methods
      *       have a {@link UML} annotation.</li>
      * </ul>
      */
@@ -423,7 +599,7 @@ public abstract strictfp class Annotatio
             if (!ControlledVocabulary.class.isAssignableFrom(type)) {
                 for (final Method method : type.getDeclaredMethods()) {
                     testingMethod = method.getName();
-                    if (!isIgnored(method) && !isExtension(method)) {
+                    if (!isIgnored(method)) {
                         uml = method.getAnnotation(UML.class);
                         if (!method.isAnnotationPresent(Deprecated.class)) {
                             assertNotNull("Missing @UML annotation.", uml);
@@ -432,11 +608,10 @@ public abstract strictfp class Annotatio
                 }
             }
         }
-        done();
     }
 
     /**
-     * Tests the annotations in the {@code package-info} files of SIS 
implementations of the
+     * Tests the annotations in the {@code package-info} files of Apache SIS 
implementations of the
      * interfaces enumerated in the {@code #types} array. More specifically 
this method tests that:
      *
      * <ul>
@@ -466,7 +641,6 @@ public abstract strictfp class Annotatio
                 assertEquals("Unexpected namespace prefix.", 
Namespaces.getPreferredPrefix(namespace, null), ns.prefix());
             }
         }
-        done();
     }
 
     /**
@@ -476,9 +650,10 @@ public abstract strictfp class Annotatio
      * <ul>
      *   <li>All implementation classes have {@link XmlRootElement} and {@link 
XmlType} annotations.</li>
      *   <li>The name declared in the {@code XmlType} annotations matches the
-     *       {@link #getExpectedXmlTypeForElement expected value}.</li>
+     *       {@link #getExpectedXmlTypeName expected value}.</li>
      *   <li>The name declared in the {@code XmlRootElement} annotations 
matches the identifier declared
-     *       in the {@link UML} annotation of the GeoAPI interfaces.</li>
+     *       in the {@link UML} annotation of the GeoAPI interfaces, with 
{@code "Abstract"} prefix added
+     *       if needed.</li>
      *   <li>The namespace declared in the {@code XmlRootElement} annotations 
is not redundant with
      *       the {@link XmlSchema} annotation in the package.</li>
      * </ul>
@@ -512,8 +687,13 @@ public abstract strictfp class Annotatio
             final XmlRootElement root = 
impl.getAnnotation(XmlRootElement.class);
             assertNotNull("Missing @XmlRootElement annotation.", root);
             final UML uml = type.getAnnotation(UML.class);
+            Stereotype stereotype = null;
             if (uml != null) {
-                assertEquals("Wrong @XmlRootElement.name().", 
getExpectedXmlElementName(null, uml), root.name());
+                final Classifier c = type.getAnnotation(Classifier.class);
+                if (c != null) {
+                    stereotype = c.value();
+                }
+                assertEquals("Wrong @XmlRootElement.name().", 
getExpectedXmlRootElementName(stereotype, uml), root.name());
             }
             /*
              * Check that the namespace is the expected one (according 
subclass)
@@ -525,13 +705,12 @@ public abstract strictfp class Annotatio
              */
             final XmlType xmlType = impl.getAnnotation(XmlType.class);
             assertNotNull("Missing @XmlType annotation.", xmlType);
-            String expected = getExpectedXmlTypeForElement(type, impl);
+            String expected = getExpectedXmlTypeName(stereotype, uml);
             if (expected == null) {
                 expected = DEFAULT;
             }
             assertEquals("Wrong @XmlType.name().", expected, xmlType.name());
         }
-        done();
     }
 
     /**
@@ -569,17 +748,51 @@ public abstract strictfp class Annotatio
                 }
                 testingMethod = method.getName();
                 final UML uml = method.getAnnotation(UML.class);
-                final XmlElement element = getXmlElement(impl, testingMethod, 
uml);
-                /*
-                 * Just display the missing @XmlElement annotation for the 
method, since we know
-                 * that some elements are not yet implemented (and 
consequently can not yet be
-                 * annotated).
-                 */
-                if (element == null) {
-                    // Note: lines with the "[WARNING]" string are highlighted 
by Jenkins.
-                    warning("[WARNING] Missing @XmlElement annotation for ");
+                XmlElement element;
+                try {
+                    element = 
impl.getMethod(testingMethod).getAnnotation(XmlElement.class);
+                } catch (NoSuchMethodException e) {
+                    fail(e.toString());
                     continue;
                 }
+                if (element == null) {
+                    if (uml == null) {
+                        continue;
+                    }
+                    /*
+                     * If the method does not have a @XmlElement annotation, 
search for a private method having the
+                     * @XmlElement annotation with expected name. This 
situation happens when metadata object needs
+                     * to perform some extra step at XML marshalling time only 
(not when using directly the API),
+                     * for example verifying whether we are marshalling ISO 
19139:2007 or ISO 19115-3:2016.
+                     */
+                    boolean wasPublic = false;
+                    final String identifier = uml.identifier();
+                    for (final Method pm : impl.getDeclaredMethods()) {
+                        final XmlElement e = 
pm.getAnnotation(XmlElement.class);
+                        if (e != null && identifier.equals(e.name())) {
+                            final boolean isPublic = 
Modifier.isPublic(pm.getModifiers());
+                            if (element != null) {
+                                if (isPublic & !wasPublic) continue;           
 // Give precedence to private methods.
+                                if (isPublic == wasPublic) {
+                                    fail("Duplicated @XmlElement for \"" + 
identifier + "\".");
+                                }
+                            }
+                            wasPublic = isPublic;
+                            element = e;
+                        }
+                    }
+                    /*
+                     * If a few case the annotation is not on a getter method, 
but directly on the field.
+                     * The main case is the "pass" field in 
DefaultConformanceResult.
+                     */
+                    if (element == null) try {
+                        element = 
impl.getDeclaredField(identifier).getAnnotation(XmlElement.class);
+                        assertNotNull("Missing @XmlElement annotation.", 
element);
+                    } catch (NoSuchFieldException e) {
+                        fail("Missing @XmlElement annotation.");
+                        continue;   // As a metter of principle (should never 
reach this point).
+                    }
+                }
                 /*
                  * The UML annotation is mandatory in the default 
implementation of the
                  * 'testInterfaceAnnotations()' method, but we don't require 
the UML to
@@ -588,7 +801,9 @@ public abstract strictfp class Annotatio
                  */
                 if (uml != null) {
                     assertEquals("Wrong @XmlElement.name().", 
getExpectedXmlElementName(type, uml), element.name());
-                    assertEquals("Wrong @XmlElement.required().", 
uml.obligation() == Obligation.MANDATORY, element.required());
+                    if (!method.isAnnotationPresent(Deprecated.class) && 
uml.version() == 0) {
+                        assertEquals("Wrong @XmlElement.required().", 
uml.obligation() == Obligation.MANDATORY, element.required());
+                    }
                 }
                 /*
                  * Check that the namespace is the expected one (according 
subclass)
@@ -597,7 +812,6 @@ public abstract strictfp class Annotatio
                 assertExpectedNamespace(element.namespace(), impl, uml);
             }
         }
-        done();
     }
 
     /**
@@ -661,69 +875,136 @@ public abstract strictfp class Annotatio
                              "getter method - not in a parent class, to avoid 
issues with JAXB.",
                              getter.getDeclaringClass(), 
setter.getDeclaringClass());
                 assertEquals("The setter parameter type shall be the same than 
the getter return type.",
-                             getter.getReturnType(), 
getSingleton(setter.getParameterTypes()));
+                             getter.getReturnType(), 
TestUtilities.getSingleton(setter.getParameterTypes()));
                 element = getter.getAnnotation(XmlElement.class);
                 assertEquals("Expected @XmlElement XOR @XmlElementRef.", 
(element == null),
                              getter.isAnnotationPresent(XmlElementRef.class) ||
                              getter.isAnnotationPresent(XmlElementRefs.class));
             }
             /*
-             * If the annotation is @XmlElement, ensure that XmlElement.name() 
is equals to
-             * the UML identifier. Then verify that the
+             * If the annotation is @XmlElement, ensure that XmlElement.name() 
is equals
+             * to the UML identifier. Then verify that the namespace is the 
expected one.
              */
             if (element != null) {
                 assertFalse("Expected @XmlElementRef.", wrapper.isInherited);
                 final UML uml = type.getAnnotation(UML.class);
-                if (uml != null) { // 'assertNotNull' is 
'testInterfaceAnnotations()' job.
+                if (uml != null) {                  // 'assertNotNull' is 
'testInterfaceAnnotations()' job.
                     assertEquals("Wrong @XmlElement.", uml.identifier(), 
element.name());
                 }
                 final String namespace = 
assertExpectedNamespace(element.namespace(), wrapper.type, uml);
                 if (!ControlledVocabulary.class.isAssignableFrom(type)) {
                     final String expected = 
getNamespace(getImplementation(type));
-                    if (expected != null) { // 'assertNotNull' is 
'testImplementationAnnotations()' job.
+                    if (expected != null) {         // 'assertNotNull' is 
'testImplementationAnnotations()' job.
                         assertEquals("Inconsistent @XmlRootElement 
namespace.", expected, namespace);
                     }
                 }
             }
         }
-        done();
-    }
-
-    /**
-     * Shall be invoked after every successful test in order
-     * to disable the report of failed class or method.
-     */
-    protected final void done() {
-        testingClass  = null;
-        testingMethod = null;
     }
 
     /**
-     * Prints the given message followed by the name of the class being tested.
+     * Prepends the {@link #testingClass} and {@link #testingMethod} before 
the given message.
+     * This is used by {@code assertFoo(…)} methods in case of failure.
      */
-    private void warning(String message) {
+    private String location(String message) {
         if (testingClass != null) {
-            final StringBuilder buffer = new StringBuilder(message);
-            buffer.append(testingClass);
+            final StringBuilder buffer = new StringBuilder(100).append("Error 
with ").append(testingClass);
             if (testingMethod != null) {
                 buffer.append('.').append(testingMethod).append("()");
             }
-            message = buffer.toString();
+            message = buffer.append(": ").append(message).toString();
         }
-        out.println(message);
+        return message;
     }
 
     /**
-     * If a test failed, reports the class and method names were the failure 
occurred.
-     * The message will be written in the {@link #out} printer.
+     * Unconditionally fails the test. This method is equivalent to JUnit 
{@link org.junit.Assert#fail(String)}
+     * except that the error message contains the {@link #testingClass} and 
{@link #testingMethod}.
+     *
+     * @param  message  the failure message.
      *
      * @see #testingClass
      * @see #testingMethod
      */
-    @After
-    public final void printFailureLocation() {
-        if (testingClass != null) {
-            warning("TEST FAILURE: ");
+    protected final void fail(final String message) {
+        throw new AssertionFailedError(location(message));
+    }
+
+    /**
+     * Fails the test if the given condition is false. This method is 
equivalent to JUnit
+     * {@link org.junit.Assert#assertTrue(String, boolean)} except that the 
error message
+     * contains the {@link #testingClass} and {@link #testingMethod}.
+     *
+     * @param  message    the message in case of failure.
+     * @param  condition  the condition that must be {@code true}.
+     */
+    protected final void assertTrue(final String message, final boolean 
condition) {
+        if (!condition) throw new AssertionFailedError(location(message));
+    }
+
+    /**
+     * Fails the test if the given condition is true. This method is 
equivalent to JUnit
+     * {@link org.junit.Assert#assertFalse(String, boolean)} except that the 
error message
+     * contains the {@link #testingClass} and {@link #testingMethod}.
+     *
+     * @param  message    the message in case of failure.
+     * @param  condition  the condition that must be {@code false}.
+     */
+    protected final void assertFalse(final String message, final boolean 
condition) {
+        if (condition) throw new AssertionFailedError(location(message));
+    }
+
+    /**
+     * Fails the test if the given object is null. This method is equivalent 
to JUnit
+     * {@link org.junit.Assert#assertNotNull(String, Object)} except that the 
error
+     * message contains the {@link #testingClass} and {@link #testingMethod}.
+     *
+     * @param  message  the message in case of failure.
+     * @param  obj      the object that must be non-null.
+     */
+    protected final void assertNotNull(final String message, final Object obj) 
{
+        if (obj == null) throw new AssertionFailedError(location(message));
+    }
+
+    /**
+     * Fails the test if the given objects are the same. This method is 
equivalent to JUnit
+     * {@link org.junit.Assert#assertNotSame(String, Object, Object)} except 
that the error
+     * message contains the {@link #testingClass} and {@link #testingMethod}.
+     *
+     * @param  message  the message in case of failure.
+     * @param  o1       the first object (may be null).
+     * @param  o2       the second object (may be null).
+     */
+    protected final void assertNotSame(final String message, final Object o1, 
final Object o2) {
+        if (o1 == o2) throw new AssertionFailedError(location(message));
+    }
+
+    /**
+     * Fails the test if the given objects are not the same. This method is 
equivalent to JUnit
+     * {@link org.junit.Assert#assertSame(String, Object, Object)} except that 
the error message
+     * contains the {@link #testingClass} and {@link #testingMethod}.
+     *
+     * @param  message   the message in case of failure.
+     * @param  expected  the first object (may be null).
+     * @param  actual    the second object (may be null).
+     */
+    protected final void assertSame(final String message, final Object 
expected, final Object actual) {
+        if (expected != actual) throw new 
AssertionFailedError(location(message));
+    }
+
+    /**
+     * Fails the test if the given objects are not equal. This method is 
equivalent to JUnit
+     * {@link org.junit.Assert#assertEquals(String, Object, Object)} except 
that the error
+     * message contains the {@link #testingClass} and {@link #testingMethod}.
+     *
+     * @param  message   the message in case of failure.
+     * @param  expected  the first object (may be null).
+     * @param  actual    the second object (may be null).
+     */
+    protected final void assertEquals(final String message, final Object 
expected, final Object actual) {
+        if (!Objects.equals(expected, actual)) {
+            throw new AssertionFailedError(location(message) + 
System.lineSeparator()
+                        + "Expected " + expected + " but got " + actual);
         }
     }
 }

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -58,7 +58,7 @@ final strictfp class PackageVerifier {
      * Classes or properties having a JAXB annotation in this namespace should 
be deprecated.
      */
     private static final Set<String> LEGACY_NAMESPACES = 
Collections.unmodifiableSet(new HashSet<>(
-            Arrays.asList(LegacyNamespaces.GMD, LegacyNamespaces.GMI, 
LegacyNamespaces.SRV)));
+            Arrays.asList(LegacyNamespaces.GMD, LegacyNamespaces.GMI, 
LegacyNamespaces.GMX, LegacyNamespaces.SRV)));
 
     /**
      * Types declared in JAXB annotations to be considered as equivalent to 
types in XML schemas.

Modified: 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Namespaces.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Namespaces.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Namespaces.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Namespaces.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -532,6 +532,7 @@ public final class Namespaces extends St
      */
     private static final String[] GENERIC_URLS = {
         "http://standards.iso.org/iso/19115/-3/";,
+        "http://standards.iso.org/iso/19115/-2/";,
         "http://standards.iso.org/iso/19157/-2/";,
         "http://standards.iso.org/iso/19111/";,
         "http://standards.iso.org/iso/19110/";,
@@ -565,6 +566,8 @@ public final class Namespaces extends St
         p.put("http://www.opengis.net/citygml/building/1.0";,            
"build");
         p.put("http://www.opengis.net/citygml/cityfurniture/1.0";,   
"furniture");
         p.put("http://www.opengis.net/citygml/transportation/1.0";,         
"tr");
+        p.put("http://www.isotc211.org/2005/gco";,                        
"gco1");
+        p.put("http://www.isotc211.org/2005/srv";,                        
"srv1");
         p.put("http://www.purl.org/dc/elements/1.1/";,                     
"dc2");
         p.put("http://www.purl.org/dc/terms/";,                           
"dct2");
         p.put("http://purl.org/dc/terms/";,                                
"dct");

Modified: 
sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/NamespacesTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/NamespacesTest.java?rev=1823695&r1=1823694&r2=1823695&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/NamespacesTest.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/NamespacesTest.java
 [UTF-8] Fri Feb  9 19:14:03 2018
@@ -36,7 +36,11 @@ public final strictfp class NamespacesTe
      */
     @Test
     public void testGetPreferredPrefix() {
-        assertEquals("gml", 
Namespaces.getPreferredPrefix("http://www.opengis.net/gml/3.2";, null));
+        assertEquals("gml",  
Namespaces.getPreferredPrefix("http://www.opengis.net/gml/3.2";, null));
+        assertEquals("mdb",  
Namespaces.getPreferredPrefix("http://standards.iso.org/iso/19115/-3/mdb/1.0";, 
null));
+        assertEquals("gmi",  
Namespaces.getPreferredPrefix("http://standards.iso.org/iso/19115/-2/gmi/1.0";, 
null));
+        assertEquals("srv",  
Namespaces.getPreferredPrefix("http://standards.iso.org/iso/19115/-3/srv/2.0";, 
null));
+        assertEquals("srv1", 
Namespaces.getPreferredPrefix("http://www.isotc211.org/2005/srv";, null));
     }
 
     /**


Reply via email to