You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2013/04/22 18:32:37 UTC
svn commit: r1470608 - in /sis/branches/JDK7:
sis-metadata/src/main/java/org/apache/sis/metadata/
sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/
sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/
sis-metadata/src/main/j...
Author: desruisseaux
Date: Mon Apr 22 16:32:37 2013
New Revision: 1470608
URL: http://svn.apache.org/r1470608
Log:
Ported more tests.
Added:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/auto-properties.html (with props)
sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataTestCase.java (with props)
sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java (with props)
sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/AnnotationsTestCase.java (with props)
Modified:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultOnlineResource.java
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java
sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java
sis/branches/JDK7/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java
Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java?rev=1470608&r1=1470607&r2=1470608&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -124,7 +124,8 @@ final class PropertyAccessor {
final Class<?> type;
/**
- * The implementation class. The following condition must hold:
+ * The implementation class, or {@link #type} if none.
+ * The following condition must hold:
*
* {@preformat java
* type.isAssignableFrom(implementation);
@@ -218,7 +219,7 @@ final class PropertyAccessor {
* @param standard The standard which define the {@code type} interface.
* @param type The interface implemented by the metadata, which must be
* the value returned by {@link #getStandardType(Class, String)}.
- * @param implementation The class of metadata implementations.
+ * @param implementation The class of metadata implementations, or {@code type} if none.
*/
PropertyAccessor(final Citation standard, final Class<?> type, final Class<?> implementation) {
assert type.isAssignableFrom(implementation) : implementation;
@@ -590,6 +591,13 @@ final class PropertyAccessor {
}
/**
+ * Returns {@code true} if the property at the given index is writable.
+ */
+ final boolean isWritable(final int index) {
+ return (index >= 0) && (index < standardCount) && (setters != null) && (setters[index] != null);
+ }
+
+ /**
* Returns the value for the specified metadata, or {@code null} if none.
* If the given index is out of bounds, then this method returns {@code null},
* so it is safe to invoke this method even if {@link #indexOf(String, boolean)}
Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultOnlineResource.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultOnlineResource.java?rev=1470608&r1=1470607&r2=1470608&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultOnlineResource.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultOnlineResource.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -222,7 +222,7 @@ public class DefaultOnlineResource exten
/**
* Returns the location (address) for on-line access using a Uniform Resource Locator address or
- * similar addressing scheme such as @{@code http://www.statkart.no/isotc211}".
+ * similar addressing scheme such as "{@code http://www.statkart.no/isotc211}".
*/
@Override
@XmlElement(name = "linkage", required = true)
@@ -242,11 +242,16 @@ public class DefaultOnlineResource exten
}
/**
- * Returns the connection protocol to be used.
+ * Returns the connection protocol to be used. If no protocol has been {@linkplain #setProtocol(String)
+ * explicitely set}, then this method returns the {@linkplain #getLinkage() linkage}
+ * {@linkplain URI#getScheme() scheme} (if any).
*/
@Override
@XmlElement(name = "protocol")
public String getProtocol() {
+ if (protocol == null && linkage != null) {
+ return linkage.getScheme();
+ }
return protocol;
}
Added: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/auto-properties.html
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/auto-properties.html?rev=1470608&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/auto-properties.html (added)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/auto-properties.html Mon Apr 22 16:32:37 2013
@@ -0,0 +1,16 @@
+<html>
+ <head>
+ <title>List of auto-generated metadata properties</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ </head>
+ <body>
+ <h1>List of auto-generated metadata properties</h1>
+ <p>If any of the following properties is not explicitely defined by a call to the corresponding setter method,
+ then the Apache SIS default implementations will infer a default value as described below:</p>
+
+ <ul>
+ <li><code><a href="../citation/DefaultOnlineResource#getProtocol()">CI_OnlineResource.protocol</a></code>
+ defaults to the schema part of the linkage URI.</li>
+ </ul>
+ </body>
+</html>
Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/auto-properties.html
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/doc-files/auto-properties.html
------------------------------------------------------------------------------
svn:mime-type = text/html
Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java?rev=1470608&r1=1470607&r2=1470608&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -218,6 +218,8 @@ public class DefaultScopeDescription ext
c = cast(value, type);
} else if (isNullOrEmpty(newValue)) {
return;
+ } else {
+ property = code;
}
value = writeSet(newValue, c, type);
}
@@ -343,6 +345,7 @@ public class DefaultScopeDescription ext
public void setDataset(final String newValue) {
checkWritePermission();
if (newValue != null || property == DATASET) {
+ property = DATASET;
value = newValue;
}
}
@@ -370,6 +373,7 @@ public class DefaultScopeDescription ext
public void setOther(final String newValue) {
checkWritePermission();
if (newValue != null || property == OTHER) {
+ property = OTHER;
value = newValue;
}
}
Added: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataTestCase.java?rev=1470608&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataTestCase.java (added)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataTestCase.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.metadata;
+
+import java.util.Date;
+import java.util.Locale;
+import java.util.Random;
+import java.util.Collection;
+import org.opengis.util.CodeList;
+import org.apache.sis.util.Numbers;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.collection.CheckedContainer;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.test.AnnotationsTestCase;
+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
+ * as described in the {@linkplain AnnotationsTestCase parent class}, and tests additional aspects like:
+ *
+ * <ul>
+ * <li>All {@link AbstractMetadata} instance shall be initially {@linkplain AbstractMetadata#isEmpty() empty}.</li>
+ * <li>All getter methods shall returns a null singleton or an empty collection, never a null collection.</li>
+ * <li>After a call to a setter method, the getter method shall return a value equals to the given value.</li>
+ * </ul>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+@DependsOn(PropertyAccessorTest.class)
+public abstract strictfp class MetadataTestCase extends AnnotationsTestCase {
+ /**
+ * The standard implemented by the metadata objects to test.
+ */
+ private final MetadataStandard standard;
+
+ /**
+ * Random generator, or {@code null} if not needed.
+ */
+ private Random random;
+
+ /**
+ * Creates a new test suite for the given types.
+ *
+ * @param standard The standard implemented by the metadata objects to test.
+ * @param types The GeoAPI interfaces or {@link CodeList} types to test.
+ */
+ protected MetadataTestCase(final MetadataStandard standard, final Class<?>... types) {
+ super(types);
+ this.standard = standard;
+ }
+
+ /**
+ * Returns the SIS implementation for the given GeoAPI interface.
+ */
+ @Override
+ protected <T> Class<? extends T> getImplementation(final Class<T> type) {
+ assertTrue(standard.isMetadata(type));
+ return standard.getImplementation(type).asSubclass(type);
+ }
+
+ /**
+ * Return {@code true} if the given property is expected to be writable.
+ * If this method returns {@code false}, then {@link #testPropertyValues()}
+ * will not try to write a value for that property.
+ *
+ * <p>The default implementation returns {@code true}.</p>
+ *
+ * @param impl The implementation class.
+ * @param property The name of the property to test.
+ * @return {@code true} if the given property is writable.
+ */
+ protected boolean isWritable(final Class<?> impl, final String property) {
+ return true;
+ }
+
+ /**
+ * Returns a dummy value of the given type. The default implementation returns values for
+ * {@link CharSequence}, {@link Number}, {@link Date}, {@link Locale}, {@link CodeList}
+ * and types in the {@link #types} list.
+ *
+ * <p>The returned value may be of an other type than the given one if the
+ * {@code PropertyAccessor} converter method know how to convert that type.</p>
+ *
+ * @param property The name of the property for which to create a value.
+ * @param type The type of value to create.
+ * @return The value of the given {@code type}, or of a type convertible to the given type.
+ */
+ protected Object valueFor(final String property, final Class<?> type) {
+ if (CharSequence.class.isAssignableFrom(type)) {
+ return "Dummy value for " + property + '.';
+ }
+ switch (Numbers.getEnumConstant(type)) {
+ case Numbers.DOUBLE: return random.nextDouble() * 100;
+ case Numbers.FLOAT: return random.nextFloat() * 100f;
+ case Numbers.LONG: return (long) random.nextInt(1000000) + 1;
+ case Numbers.INTEGER: return random.nextInt( 10000) + 1;
+ case Numbers.SHORT: return (short) random.nextInt( 1000) + 1;
+ case Numbers.BYTE: return (byte) random.nextInt( 100) + 1;
+ case Numbers.BOOLEAN: return random.nextBoolean();
+ }
+ if (Date.class.isAssignableFrom(type)) {
+ return new Date(random.nextInt() * 1000L);
+ }
+ if (CodeList.class.isAssignableFrom(type)) try {
+ final CodeList[] codes = (CodeList[]) type.getMethod("values", (Class[]) null).invoke(null, (Object[]) null);
+ return codes[random.nextInt(codes.length)];
+ } catch (ReflectiveOperationException e) {
+ fail(e.toString());
+ }
+ if (Locale.class.isAssignableFrom(type)) {
+ switch (random.nextInt(4)) {
+ case 0: return Locale.ROOT;
+ case 1: return Locale.ENGLISH;
+ case 2: return Locale.FRENCH;
+ case 3: return Locale.JAPANESE;
+ }
+ }
+ if (ArraysExt.containsIdentity(types, type)) {
+ final Class<?> impl = getImplementation(type);
+ if (impl != null) try {
+ return impl.getConstructor((Class<?>[]) null).newInstance((Object[]) null);
+ } catch (ReflectiveOperationException e) {
+ fail(e.toString());
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Normalizes the type of the given value before comparison. In particular, {@code InternationalString}
+ * instances are converted to {@code String} instances.
+ */
+ private static Object normalizeType(Object value) {
+ if (value instanceof CharSequence) {
+ value = value.toString();
+ }
+ return value;
+ }
+
+ /**
+ * Validates the given newly constructed metadata. The default implementation ensures that
+ * {@link AbstractMetadata.isEmpty()} returns {@code true}.
+ *
+ * @param metadata The metadata to validate.
+ */
+ protected void validate(final AbstractMetadata metadata) {
+ assertTrue("AbstractMetadata.isEmpty()", metadata.isEmpty());
+ }
+
+ /**
+ * For every properties in every non-{@code Codelist} types listed in the {@link #types} array,
+ * test the property values. This method performs the tests documented in class javadoc.
+ */
+ @Test
+ public void testPropertyValues() {
+ random = TestUtilities.createRandomNumberGenerator("testPropertyValues");
+ for (final Class<?> type : types) {
+ if (!CodeList.class.isAssignableFrom(type)) {
+ final Class<?> impl = getImplementation(type);
+ if (impl != null) {
+ assertTrue(type.isAssignableFrom(impl));
+ testPropertyValues(new PropertyAccessor(standard.getCitation(), type, impl));
+ }
+ }
+ }
+ }
+
+ /**
+ * Implementation of {@link #testPropertyValues()} for a single class.
+ */
+ private void testPropertyValues(final PropertyAccessor accessor) {
+ /*
+ * Try to instantiate the implementation. Every implementation should
+ * have a no-args constructor, and their instantiation should never fail.
+ */
+ testingClass = accessor.implementation.getCanonicalName();
+ final Object instance;
+ try {
+ instance = accessor.implementation.getConstructor((Class<?>[]) null).newInstance((Object[]) null);
+ } catch (ReflectiveOperationException e) {
+ fail(e.toString());
+ return;
+ }
+ if (instance instanceof AbstractMetadata) {
+ validate((AbstractMetadata) instance);
+ }
+ /*
+ * Iterate over all properties defined in the interface,
+ * and checks for the existences of a setter method.
+ */
+ final int count = accessor.count();
+ for (int i=0; i<count; i++) {
+ testingMethod = accessor.name(i, KeyNamePolicy.METHOD_NAME);
+ final String property = accessor.name(i, KeyNamePolicy.JAVABEANS_PROPERTY);
+ assertNotNull("Missing method name.", testingMethod);
+ assertNotNull("Missing property name.", property);
+ assertEquals("Wrong property index.", i, accessor.indexOf(property, true));
+ /*
+ * Get the property type. In the special case where the property type
+ * is a collection, this is the type of elements in that collection.
+ */
+ final Class<?> propertyType = Numbers.primitiveToWrapper(accessor.type(i, TypeValuePolicy.PROPERTY_TYPE));
+ final Class<?> elementType = Numbers.primitiveToWrapper(accessor.type(i, TypeValuePolicy.ELEMENT_TYPE));
+ final boolean isCollection = Collection.class.isAssignableFrom(propertyType);
+ assertFalse("Element type can not be Collection.", Collection.class.isAssignableFrom(elementType));
+ assertEquals("Property and element types shall be the same if and only if not a collection.",
+ !isCollection, propertyType == elementType);
+ /*
+ * Try to get a value.
+ */
+ Object value = accessor.get(i, instance);
+ if (value == null) {
+ assertFalse("Null values are not allowed to be collections.", isCollection);
+ } else {
+ assertInstanceOf("Wrong property type.", propertyType, value);
+ if (value instanceof CheckedContainer<?>) {
+ assertTrue("Wrong element type in collection.",
+ elementType.isAssignableFrom(((CheckedContainer<?>) value).getElementType()));
+ }
+ if (isCollection) {
+ assertTrue("Collections shall be initially empty.", ((Collection<?>) value).isEmpty());
+ value = CollectionsExt.modifiableCopy((Collection<?>) value); // Protect from changes.
+ }
+ }
+ /*
+ * Try to write a value.
+ */
+ if (isWritable(accessor.implementation, property)) {
+ assertTrue(property, accessor.isWritable(i));
+ final Object newValue = valueFor(property, elementType);
+ final Object oldValue = accessor.set(i, instance, newValue, true);
+ assertEquals("PropertyAccessor.set(…) shall return the value previously returned by get(…).", value, oldValue);
+ value = accessor.get(i, instance);
+ if (isCollection) {
+ if (newValue == null) {
+ assertTrue("We didn't generated a random value for this type, consequently the "
+ + "collection should still empty.", ((Collection<?>) value).isEmpty());
+ value = null;
+ } else {
+ value = TestUtilities.getSingleton((Collection<?>) value);
+ }
+ }
+ assertEquals("PropertyAccessor.get(…) shall return the value that we have just set.",
+ normalizeType(newValue), normalizeType(value));
+ }
+ }
+ }
+}
Propchange: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataTestCase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataTestCase.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Added: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java?rev=1470608&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java (added)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.metadata.iso;
+
+import java.lang.reflect.Modifier;
+import org.opengis.util.CodeList;
+import org.opengis.annotation.UML;
+import org.opengis.annotation.Specification;
+import org.apache.sis.metadata.MetadataStandard;
+import org.apache.sis.metadata.MetadataTestCase;
+import org.apache.sis.metadata.iso.quality.AbstractElement;
+import org.apache.sis.metadata.iso.identification.DefaultRepresentativeFraction;
+import org.apache.sis.xml.Namespaces;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests all known {@link ISOMetadata} subclasses for JAXB annotations and getter/setter methods.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+public final strictfp class AllMetadataTest extends MetadataTestCase {
+ /**
+ * Creates a new test case with all GeoAPI interfaces and code lists to test.
+ */
+ public AllMetadataTest() {
+ super(MetadataStandard.ISO_19115,
+ org.opengis.metadata.ApplicationSchemaInformation.class,
+ org.opengis.metadata.Datatype.class,
+ org.opengis.metadata.ExtendedElementInformation.class,
+ org.opengis.metadata.FeatureTypeList.class,
+ org.opengis.metadata.Identifier.class,
+ org.opengis.metadata.Metadata.class,
+ org.opengis.metadata.MetadataExtensionInformation.class,
+// org.opengis.metadata.Obligation.class, // Excluded CodeList because it doesn't use the usual kind of adapter.
+ org.opengis.metadata.PortrayalCatalogueReference.class,
+ org.opengis.metadata.acquisition.AcquisitionInformation.class,
+ org.opengis.metadata.acquisition.Context.class,
+ org.opengis.metadata.acquisition.EnvironmentalRecord.class,
+ org.opengis.metadata.acquisition.Event.class,
+ org.opengis.metadata.acquisition.GeometryType.class,
+ org.opengis.metadata.acquisition.Instrument.class,
+ org.opengis.metadata.acquisition.Objective.class,
+ org.opengis.metadata.acquisition.ObjectiveType.class,
+ org.opengis.metadata.acquisition.Operation.class,
+ org.opengis.metadata.acquisition.OperationType.class,
+ org.opengis.metadata.acquisition.Plan.class,
+ org.opengis.metadata.acquisition.Platform.class,
+ org.opengis.metadata.acquisition.PlatformPass.class,
+ org.opengis.metadata.acquisition.Priority.class,
+ org.opengis.metadata.acquisition.RequestedDate.class,
+ org.opengis.metadata.acquisition.Requirement.class,
+ org.opengis.metadata.acquisition.Sequence.class,
+ org.opengis.metadata.acquisition.Trigger.class,
+ org.opengis.metadata.citation.Address.class,
+ org.opengis.metadata.citation.Citation.class,
+ org.opengis.metadata.citation.CitationDate.class,
+ org.opengis.metadata.citation.Contact.class,
+ org.opengis.metadata.citation.DateType.class,
+ org.opengis.metadata.citation.OnLineFunction.class,
+ org.opengis.metadata.citation.OnlineResource.class,
+ org.opengis.metadata.citation.PresentationForm.class,
+ org.opengis.metadata.citation.ResponsibleParty.class,
+ org.opengis.metadata.citation.Role.class,
+ org.opengis.metadata.citation.Series.class,
+ org.opengis.metadata.citation.Telephone.class,
+ org.opengis.metadata.constraint.Classification.class,
+ org.opengis.metadata.constraint.Constraints.class,
+ org.opengis.metadata.constraint.LegalConstraints.class,
+ org.opengis.metadata.constraint.Restriction.class,
+ org.opengis.metadata.constraint.SecurityConstraints.class,
+ org.opengis.metadata.content.Band.class,
+ org.opengis.metadata.content.BandDefinition.class,
+ org.opengis.metadata.content.ContentInformation.class,
+ org.opengis.metadata.content.CoverageContentType.class,
+ org.opengis.metadata.content.CoverageDescription.class,
+ org.opengis.metadata.content.FeatureCatalogueDescription.class,
+ org.opengis.metadata.content.ImageDescription.class,
+ org.opengis.metadata.content.ImagingCondition.class,
+ org.opengis.metadata.content.PolarizationOrientation.class,
+ org.opengis.metadata.content.RangeDimension.class,
+ org.opengis.metadata.content.RangeElementDescription.class,
+ org.opengis.metadata.content.TransferFunctionType.class,
+ org.opengis.metadata.distribution.DataFile.class,
+ org.opengis.metadata.distribution.DigitalTransferOptions.class,
+ org.opengis.metadata.distribution.Distribution.class,
+ org.opengis.metadata.distribution.Distributor.class,
+ org.opengis.metadata.distribution.Format.class,
+ org.opengis.metadata.distribution.Medium.class,
+ org.opengis.metadata.distribution.MediumFormat.class,
+ org.opengis.metadata.distribution.MediumName.class,
+ org.opengis.metadata.distribution.StandardOrderProcess.class,
+ org.opengis.metadata.extent.BoundingPolygon.class,
+ org.opengis.metadata.extent.Extent.class,
+ org.opengis.metadata.extent.GeographicBoundingBox.class,
+ org.opengis.metadata.extent.GeographicDescription.class,
+ org.opengis.metadata.extent.GeographicExtent.class,
+ org.opengis.metadata.extent.SpatialTemporalExtent.class,
+ org.opengis.metadata.extent.TemporalExtent.class,
+ org.opengis.metadata.extent.VerticalExtent.class,
+ org.opengis.metadata.identification.AggregateInformation.class,
+ org.opengis.metadata.identification.AssociationType.class,
+ org.opengis.metadata.identification.BrowseGraphic.class,
+ org.opengis.metadata.identification.CharacterSet.class,
+ org.opengis.metadata.identification.DataIdentification.class,
+ org.opengis.metadata.identification.Identification.class,
+ org.opengis.metadata.identification.InitiativeType.class,
+ org.opengis.metadata.identification.Keywords.class,
+ org.opengis.metadata.identification.KeywordType.class,
+ org.opengis.metadata.identification.Progress.class,
+ org.opengis.metadata.identification.RepresentativeFraction.class,
+ org.opengis.metadata.identification.Resolution.class,
+ org.opengis.metadata.identification.ServiceIdentification.class,
+ org.opengis.metadata.identification.TopicCategory.class,
+ org.opengis.metadata.identification.Usage.class,
+ org.opengis.metadata.lineage.Algorithm.class,
+ org.opengis.metadata.lineage.Lineage.class,
+ org.opengis.metadata.lineage.NominalResolution.class,
+ org.opengis.metadata.lineage.Processing.class,
+ org.opengis.metadata.lineage.ProcessStep.class,
+ org.opengis.metadata.lineage.ProcessStepReport.class,
+ org.opengis.metadata.lineage.Source.class,
+ org.opengis.metadata.maintenance.MaintenanceFrequency.class,
+ org.opengis.metadata.maintenance.MaintenanceInformation.class,
+ org.opengis.metadata.maintenance.ScopeCode.class,
+ org.opengis.metadata.maintenance.ScopeDescription.class,
+ org.opengis.metadata.quality.AbsoluteExternalPositionalAccuracy.class,
+ org.opengis.metadata.quality.AccuracyOfATimeMeasurement.class,
+ org.opengis.metadata.quality.Completeness.class,
+ org.opengis.metadata.quality.CompletenessCommission.class,
+ org.opengis.metadata.quality.CompletenessOmission.class,
+ org.opengis.metadata.quality.ConceptualConsistency.class,
+ org.opengis.metadata.quality.ConformanceResult.class,
+ org.opengis.metadata.quality.CoverageResult.class,
+ org.opengis.metadata.quality.DataQuality.class,
+ org.opengis.metadata.quality.DomainConsistency.class,
+ org.opengis.metadata.quality.Element.class,
+ org.opengis.metadata.quality.EvaluationMethodType.class,
+ org.opengis.metadata.quality.FormatConsistency.class,
+ org.opengis.metadata.quality.GriddedDataPositionalAccuracy.class,
+ org.opengis.metadata.quality.LogicalConsistency.class,
+ org.opengis.metadata.quality.NonQuantitativeAttributeAccuracy.class,
+ org.opengis.metadata.quality.PositionalAccuracy.class,
+ org.opengis.metadata.quality.QuantitativeAttributeAccuracy.class,
+ org.opengis.metadata.quality.QuantitativeResult.class,
+ org.opengis.metadata.quality.RelativeInternalPositionalAccuracy.class,
+ org.opengis.metadata.quality.Result.class,
+ org.opengis.metadata.quality.Scope.class,
+ org.opengis.metadata.quality.TemporalAccuracy.class,
+ org.opengis.metadata.quality.TemporalConsistency.class,
+ org.opengis.metadata.quality.TemporalValidity.class,
+ org.opengis.metadata.quality.ThematicAccuracy.class,
+ org.opengis.metadata.quality.ThematicClassificationCorrectness.class,
+ org.opengis.metadata.quality.TopologicalConsistency.class,
+ org.opengis.metadata.quality.Usability.class,
+ org.opengis.metadata.spatial.CellGeometry.class,
+ org.opengis.metadata.spatial.Dimension.class,
+ org.opengis.metadata.spatial.DimensionNameType.class,
+ org.opengis.metadata.spatial.GCP.class,
+ org.opengis.metadata.spatial.GCPCollection.class,
+ org.opengis.metadata.spatial.GeolocationInformation.class,
+ org.opengis.metadata.spatial.GeometricObjects.class,
+ org.opengis.metadata.spatial.GeometricObjectType.class,
+ org.opengis.metadata.spatial.Georectified.class,
+ org.opengis.metadata.spatial.Georeferenceable.class,
+ org.opengis.metadata.spatial.GridSpatialRepresentation.class,
+ org.opengis.metadata.spatial.PixelOrientation.class,
+ org.opengis.metadata.spatial.SpatialRepresentation.class,
+ org.opengis.metadata.spatial.SpatialRepresentationType.class,
+ org.opengis.metadata.spatial.TopologyLevel.class,
+ org.opengis.metadata.spatial.VectorSpatialRepresentation.class);
+ }
+
+ /**
+ * 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"}.
+ */
+ @Override
+ protected String getExpectedNamespace(final Class<?> impl, final Specification specification) {
+ if (impl == org.apache.sis.metadata.iso.identification.DefaultServiceIdentification.class) {
+ 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).
+ */
+ @Override
+ protected String getExpectedTypeForElement(final Class<?> type, final Class<?> impl) {
+ final String rootName = type.getAnnotation(UML.class).identifier();
+ switch (rootName) {
+ // We don't know yet what is the type of this one.
+ case "MD_FeatureTypeList": {
+ return null;
+ }
+ // Following prefix was changed in ISO 19115 corrigendum,
+ // but ISO 19139 still use the old prefix.
+ case "SV_ServiceIdentification": {
+ return "MD_ServiceIdentification_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,
+ * or {@code null} if no adapter is expected for the given type.
+ */
+ @Override
+ protected Class<?> getWrapperFor(final Class<?> type) throws ClassNotFoundException {
+ if (type.equals(org.opengis.metadata.Metadata.class)) {
+ /*
+ * We don't have adapter for Metadata, since it is the root element.
+ * We explicitly exclude it for avoiding confusion with PropertyType,
+ * which is the base class of all other adapters.
+ */
+ return null;
+ }
+ final String classname = "org.apache.sis.internal.jaxb." +
+ (CodeList.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;
+ }
+
+ /**
+ * Return {@code false} for the Apache SIS properties which are known to have no setter methods.
+ */
+ @Override
+ protected boolean isWritable(final Class<?> impl, final String property) {
+ if (DefaultRepresentativeFraction.class.isAssignableFrom(impl)) {
+ if (property.equals("doubleValue")) {
+ return false;
+ }
+ }
+ if (AbstractElement.class.isAssignableFrom(impl)) {
+ if (property.equals("dates")) {
+ return false;
+ }
+ }
+ return super.isWritable(impl, property);
+ }
+}
Propchange: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java?rev=1470608&r1=1470607&r2=1470608&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -42,7 +42,8 @@ import org.junit.BeforeClass;
org.apache.sis.metadata.InformationMapTest.class,
org.apache.sis.metadata.ValueMapTest.class,
org.apache.sis.metadata.MetadataStandardTest.class,
- org.apache.sis.metadata.PrunerTest.class
+ org.apache.sis.metadata.PrunerTest.class,
+ org.apache.sis.metadata.iso.AllMetadataTest.class
})
public final strictfp class MetadataTestSuite extends TestSuite {
/**
Modified: sis/branches/JDK7/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java?rev=1470608&r1=1470607&r2=1470608&view=diff
==============================================================================
--- sis/branches/JDK7/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -20,6 +20,7 @@ import org.opengis.geometry.Envelope;
import org.opengis.geometry.DirectPosition;
import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestCase;
+import org.apache.sis.test.DependsOnMethod;
import org.junit.Ignore;
import org.junit.Test;
@@ -451,9 +452,25 @@ public strictfp class GeneralEnvelopeTes
}
/**
+ * Tests the {@link GeneralEnvelope#toString()} method.
+ */
+ @Test
+ public void testToString() {
+ GeneralEnvelope envelope = new GeneralEnvelope(new double[] {-180, -90}, new double[] {180, 90});
+ assertEquals("BOX(-180 -90, 180 90)", envelope.toString());
+
+ envelope = new GeneralEnvelope(3);
+ envelope.setRange(0, -180, +180);
+ envelope.setRange(1, -90, +90);
+ envelope.setRange(2, 10, 30);
+ assertEquals("BOX3D(-180 -90 10, 180 90 30)", envelope.toString());
+ }
+
+ /**
* Tests the {@link GeneralEnvelope#GeneralEnvelope(CharSequence)} constructor.
*/
@Test
+ @DependsOnMethod("testToString")
public void testWktParsing() {
GeneralEnvelope envelope = new GeneralEnvelope("BOX(-180 -90,180 90)");
assertEquals(2, envelope.getDimension());
Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/AnnotationsTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/AnnotationsTestCase.java?rev=1470608&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/AnnotationsTestCase.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/AnnotationsTestCase.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -0,0 +1,672 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.test;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.lang.reflect.Method;
+import javax.xml.bind.annotation.XmlNs;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlSchema;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.opengis.util.CodeList;
+import org.opengis.annotation.UML;
+import org.opengis.annotation.Obligation;
+import org.opengis.annotation.Specification;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.xml.Namespaces;
+import org.junit.After;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.apache.sis.test.TestUtilities.getSingleton;
+
+
+/**
+ * Base class for validations of {@link UML}, {@link XmlElement} and other annotations.
+ * Some tests performed by this class are:
+ *
+ * <ul>
+ * <li>All implementation classes have {@link XmlRootElement} and {@link XmlType} annotations.</li>
+ * <li>The name declared in the {@code XmlType} annotations matches the
+ * {@linkplain #getExpectedTypeForElement 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.</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>
+ * <li>The prefixes declared in the {@link XmlNs} annotations match the
+ * {@linkplain Namespaces#getPreferredPrefix expected prefixes}.</li>
+ * <li>The {@linkplain #getWrapperFor wrapper}, if any, is consistent.</li>
+ * </ul>
+ *
+ * @author Cédric Briançon (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-3.05)
+ * @version 0.3
+ * @module
+ */
+public abstract strictfp class AnnotationsTestCase extends TestCase {
+ /**
+ * The {@value} string used in JAXB annotations for default names or namespaces.
+ */
+ private static final String DEFAULT = "##default";
+
+ /**
+ * The GeoAPI interfaces or {@link CodeList} types to test.
+ */
+ 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.
+ */
+ 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
+ * class and method where the failure occurred.
+ */
+ protected String testingMethod;
+
+ /**
+ * Creates a new test suite for the given types.
+ *
+ * @param types The GeoAPI interfaces or {@link CodeList} types to test.
+ */
+ protected AnnotationsTestCase(final Class<?>... types) {
+ this.types = types;
+ }
+
+ /**
+ * Returns the SIS implementation class for the given GeoAPI interface.
+ * For example the implementation of the {@link org.opengis.metadata.citation.Citation}
+ * interface is the {@link org.apache.sis.metadata.iso.citation.DefaultCitation} class.
+ *
+ * @param <T> The type represented by the {@code type} argument.
+ * @param type The GeoAPI interface (never a {@link CodeList} type).
+ * @return The SIS implementation for the given interface.
+ */
+ protected abstract <T> Class<? extends T> getImplementation(Class<T> type);
+
+ /**
+ * If the given GeoAPI type, when marshalled to XML, is wrapped into an other XML element,
+ * returns the class of the wrapper for that XML element. Otherwise returns {@code null}.
+ * Such wrappers are unusual in XML (except for lists), but the ISO 19139 standard do that
+ * systematically for every elements.
+ *
+ * <p><b>Example:</b> when a {@link org.apache.sis.metadata.iso.citation.DefaultContact}
+ * is marshalled to XML inside a {@code ResponsibleParty}, the element is not marshalled
+ * directly inside its parent as we usually do in XML. Instead, we have a {@code <CI_Contact>}.
+ * inside the {@code <contactInfo>} element as below:</p>
+ *
+ * {@preformat xml
+ * <CI_ResponsibleParty>
+ * <contactInfo>
+ * <CI_Contact>
+ * ...
+ * </CI_Contact>
+ * </contactInfo>
+ * </CI_ResponsibleParty>
+ * }
+ *
+ * To reflect that fact, this method shall return the {@link org.apache.sis.internal.jaxb.metadata.CI_Contact}
+ * wrapper class for the {@link org.apache.sis.metadata.iso.citation.DefaultCitation} argument.
+ * If no wrapper is expected for the given class, then this method shall return {@code null}.
+ *
+ * <p>If a wrapper is expected for the given class but was not found, then this method shall throw
+ * {@link ClassNotFoundException}. Note that no wrapper may be defined explicitely for the given type,
+ * while a wrapper is defined for a parent of the given type. This method does not need to care about
+ * such situation, since the caller will automatically searches for a parent class if
+ * {@code ClassNotFoundException} has been thrown.</p>
+ *
+ * <p>In SIS implementation, most wrappers are also {@link javax.xml.bind.annotation.adapters.XmlAdapter}.
+ * But this is not a requirement.</p>
+ *
+ * @param type The GeoAPI interface or {@link CodeList} type.
+ * @return The wrapper for the given type, or {@code null} if none.
+ * @throws ClassNotFoundException If a wrapper was expected but not found.
+ */
+ protected abstract Class<?> getWrapperFor(Class<?> type) throws ClassNotFoundException;
+
+ /**
+ * The value returned by {@link AnnotationsTestCase#getWrapperFor(Class)}, together with
+ * a boolean telling whether the wrapper has been found in the tested class or in one
+ * of its parent classes.
+ */
+ private static final class WrapperClass {
+ final Class<?> type;
+ boolean isInherited;
+
+ WrapperClass(final Class<?> type) {
+ this.type = type;
+ }
+ }
+
+ /**
+ * Returns the value of {@link #getWrapperFor(Class)} for the given class, or for a parent
+ * of the given class if {@code getWrapperFor(Class)} threw {@code ClassNotFoundException}.
+ *
+ * @param type The GeoAPI interface or {@link CodeList} type.
+ * @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.
+ */
+ private WrapperClass getWrapperInHierarchy(final Class<?> type) throws ClassNotFoundException {
+ try {
+ return new WrapperClass(getWrapperFor(type));
+ } catch (ClassNotFoundException e) {
+ for (final Class<?> parent : type.getInterfaces()) {
+ if (ArraysExt.containsIdentity(types, parent)) try {
+ final WrapperClass wrapper = getWrapperInHierarchy(parent);
+ wrapper.isInherited = true;
+ return wrapper;
+ } catch (ClassNotFoundException e2) {
+ e.addSuppressed(e2);
+ }
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Returns the XML type for an element of the given type. For example in ISO 19139,
+ * 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 getExpectedTypeForElement(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"}.
+ *
+ * <p>The default implementation recognizes the
+ * {@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.
+ * Subclasses shall override this method if they need to support more namespaces.</p>
+ *
+ * <p>The prefix for the given namespace will be fetched by
+ * {@link Namespaces#getPreferredPrefix(String, String)}.</p>
+ *
+ * @param impl The implementation class or {@link CodeList} type.
+ * @param specification The specification that define the type, or {@code null} if unspecified.
+ * @return The expected namespace.
+ * @throws IllegalArgumentException If the given specification 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());
+ }
+ }
+
+ /**
+ * Replaces {@value #DEFAULT} value by the {@link XmlSchema} namespace if needed,
+ * then performs validity check on the resulting namespace. This method checks that:
+ *
+ * <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>
+ * </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 uml The {@code @UML} annotation, or {@code null} if none.
+ * @return The actual namespace (same as {@code namespace} if it was not {@value #DEFAULT}).
+ */
+ private String assertExpectedNamespace(String namespace, final Class<?> impl, final UML uml) {
+ assertNotNull("Missing namespace.", namespace);
+ assertFalse("Missing namespace.", namespace.trim().isEmpty());
+ /*
+ * Get the namespace declared at the package level, and ensure the the
+ * 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));
+ /*
+ * 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;
+ }
+ }
+ assertTrue("Namespace is not declared in the package @XmlSchema.xmlns().", found);
+ } else {
+ namespace = schemaNamespace;
+ }
+ assertEquals("Wrong namespace for the ISO specification.",
+ getExpectedNamespace(impl, (uml != null) ? uml.specification() : null), namespace);
+ return namespace;
+ }
+
+ /**
+ * Returns the namespace declared in the {@link XmlSchema} annotation of the given package,
+ * or {@code null} if none.
+ *
+ * @param p The package, or {@code null}.
+ * @return The namespace, or {@code null} if none.
+ */
+ private static String getNamespace(final Package p) {
+ if (p != null) {
+ final XmlSchema schema = p.getAnnotation(XmlSchema.class);
+ if (schema != null) {
+ final String namespace = schema.namespace().trim();
+ if (!namespace.isEmpty() && !DEFAULT.equals(namespace)) {
+ return namespace;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the namespace declared in the {@link XmlRootElement} annotation of the given class,
+ * or the package annotation if none is found in the class.
+ *
+ * @param impl The implementation class, or {@code null}.
+ * @return The namespace, or {@code null} if none.
+ */
+ private static String getNamespace(final Class<?> impl) {
+ if (impl == null) {
+ return null;
+ }
+ final XmlRootElement root = impl.getAnnotation(XmlRootElement.class);
+ if (root != null) {
+ final String namespace = root.namespace().trim();
+ if (!namespace.isEmpty() && !DEFAULT.equals(namespace)) {
+ return namespace;
+ }
+ }
+ return getNamespace(impl.getPackage());
+ }
+
+ /**
+ * 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} of deprecated methods and
+ * some standard methods from the JDK.
+ */
+ private static boolean isIgnored(final Method method) {
+ if (method.isAnnotationPresent(Deprecated.class)) {
+ return true;
+ }
+ final String name = method.getName();
+ if (name.equals("equals") || name.equals("hashCode") || name.equals("doubleValue")) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Tests the annotations on every GeoAPI interfaces and code lists in the {@link #types} array.
+ * More specifically this method tests that:
+ *
+ * <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
+ * have a {@link UML} annotation.</li>
+ * </ul>
+ */
+ @Test
+ public void testInterfaceAnnotations() {
+ for (final Class<?> type : types) {
+ testingMethod = null;
+ testingClass = type.getCanonicalName();
+ UML uml = type.getAnnotation(UML.class);
+ assertNotNull("Missing @UML annotation.", uml);
+ if (!CodeList.class.isAssignableFrom(type)) {
+ for (final Method method : type.getDeclaredMethods()) {
+ testingMethod = method.getName();
+ if (!isIgnored(method)) {
+ uml = method.getAnnotation(UML.class);
+ assertNotNull("Missing @UML annotation.", uml);
+ }
+ }
+ }
+ }
+ done();
+ }
+
+ /**
+ * Tests the annotations in the {@code package-info} files of SIS implementations of the
+ * interfaces enumerated in the {@code #types} array. More specifically this method tests that:
+ *
+ * <ul>
+ * <li>The prefixes declared in the {@link XmlNs} annotations match the
+ * {@linkplain Namespaces#getPreferredPrefix expected prefixes}.</li>
+ * </ul>
+ */
+ @Test
+ public void testPackageAnnotations() {
+ final Set<Package> packages = new HashSet<>();
+ for (final Class<?> type : types) {
+ if (!CodeList.class.isAssignableFrom(type)) {
+ testingClass = type.getCanonicalName();
+ final Class<?> impl = getImplementation(type);
+ if (impl != null) {
+ testingClass = impl.getCanonicalName();
+ final Package p = impl.getPackage();
+ assertNotNull("Missing package information.", p);
+ packages.add(p);
+ }
+ }
+ }
+ for (final Package p : packages) {
+ for (final XmlNs ns : p.getAnnotation(XmlSchema.class).xmlns()) {
+ testingClass = p.getName();
+ final String namespace = ns.namespaceURI();
+ assertEquals("Unexpected namespace prefix.", Namespaces.getPreferredPrefix(namespace, null), ns.prefix());
+ }
+ }
+ done();
+ }
+
+ /**
+ * Tests the annotations on every SIS implementations of the interfaces enumerated
+ * in the {@link #types} array. More specifically this method tests that:
+ *
+ * <ul>
+ * <li>All implementation classes have {@link XmlRootElement} and {@link XmlType} annotations.</li>
+ * <li>The name declared in the {@code XmlType} annotations matches the
+ * {@linkplain #getExpectedTypeForElement 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>
+ * <li>The namespace declared in the {@code XmlRootElement} annotations is not redundant with
+ * the {@link XmlSchema} annotation in the package.</li>
+ * </ul>
+ *
+ * This method does not check the method annotations, since it is {@link #testMethodAnnotations()} job.
+ */
+ @Test
+ @DependsOnMethod("testInterfaceAnnotations")
+ public void testImplementationAnnotations() {
+ for (final Class<?> type : types) {
+ if (CodeList.class.isAssignableFrom(type)) {
+ // Skip code lists, since they are not the purpose of this test.
+ continue;
+ }
+ testingClass = type.getCanonicalName();
+ /*
+ * Get the implementation class, which is mandatory (otherwise the
+ * subclass shall not include the interface in the 'types' array).
+ */
+ final Class<?> impl = getImplementation(type);
+ assertNotNull("No implementation found.", impl);
+ assertNotSame("No implementation found.", type, impl);
+ testingClass = impl.getCanonicalName();
+ /*
+ * Compare the XmlRootElement with the UML annotation, if any. The UML annotation
+ * is mandatory in the default implementation of the 'testInterfaceAnnotations()'
+ * method, but we don't require the UML to be non-null here since this is not the
+ * job of this test method. This is because subclasses may choose to override the
+ * 'testInterfaceAnnotations()' method.
+ */
+ final XmlRootElement root = impl.getAnnotation(XmlRootElement.class);
+ assertNotNull("Missing @XmlRootElement annotation.", root);
+ final UML uml = type.getAnnotation(UML.class);
+ if (uml != null) {
+ assertEquals("Wrong @XmlRootElement.name().", uml.identifier(), root.name());
+ }
+ /*
+ * Check that the namespace is the expected one (according subclass)
+ * and is not redundant with the package @XmlSchema annotation.
+ */
+ assertExpectedNamespace(root.namespace(), impl, uml);
+ /*
+ * Compare the XmlType annotation with the expected value.
+ */
+ final XmlType xmlType = impl.getAnnotation(XmlType.class);
+ assertNotNull("Missing @XmlType annotation.", xmlType);
+ String expected = getExpectedTypeForElement(type, impl);
+ if (expected == null) {
+ expected = DEFAULT;
+ }
+ assertEquals("Wrong @XmlType.name().", expected, xmlType.name());
+ }
+ done();
+ }
+
+ /**
+ * Tests the annotations on every methods of SIS classes.
+ * More specifically this method tests that:
+ *
+ * <ul>
+ * <li>The name declared in {@link XmlElement} matches the UML identifier.</li>
+ * <li>The {@code XmlElement.required()} boolean is consistent with the UML {@linkplain Obligation obligation}.</li>
+ * <li>The namespace declared in {@code XmlElement} is not redundant with the one declared in the package.</li>
+ * </ul>
+ */
+ @Test
+ @DependsOnMethod("testImplementationAnnotations")
+ public void testMethodAnnotations() {
+ for (final Class<?> type : types) {
+ if (CodeList.class.isAssignableFrom(type)) {
+ // Skip code lists, since they are not the purpose of this test.
+ continue;
+ }
+ testingMethod = null;
+ testingClass = type.getCanonicalName();
+ final Class<?> impl = getImplementation(type);
+ if (impl == null) {
+ // Implementation existence are tested by 'testImplementationAnnotations()'.
+ // It is not the purpose of this test to verify again their existence.
+ continue;
+ }
+ testingClass = impl.getCanonicalName();
+ for (final Method method : type.getDeclaredMethods()) {
+ if (isIgnored(method)) {
+ continue;
+ }
+ 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 ");
+ continue;
+ }
+ /*
+ * The UML annotation is mandatory in the default implementation of the
+ * 'testInterfaceAnnotations()' method, but we don't require the UML to
+ * be non-null here since this is not the job of this test method. This
+ * is because subclasses may choose to override the above test method.
+ */
+ if (uml != null) {
+ assertEquals("Wrong @XmlElement.name().", uml.identifier(), element.name());
+ assertEquals("Wrong @XmlElement.required().", uml.obligation() == Obligation.MANDATORY, element.required());
+ }
+ /*
+ * Check that the namespace is the expected one (according subclass)
+ * and is not redundant with the package @XmlSchema annotation.
+ */
+ assertExpectedNamespace(element.namespace(), impl, uml);
+ }
+ }
+ done();
+ }
+
+ /**
+ * Tests the annotations on wrappers returned by {@link #getWrapperFor(Class)}.
+ * More specifically this method tests that:
+ *
+ * <ul>
+ * <li>The wrapper have a getter and a setter method declared in the same class.</li>
+ * <li>The getter method is annotated with {@code @XmlElement} or {@code @XmlElementRef}, but not both</li>
+ * <li>{@code @XmlElementRef} is used only in parent classes, not in leaf classes.</li>
+ * <li>The name declared in {@code @XmlElement} matches the {@code @UML} identifier.</li>
+ * </ul>
+ */
+ @Test
+ public void testWrapperAnnotations() {
+ for (final Class<?> type : types) {
+ testingClass = type.getCanonicalName();
+ /*
+ * Check the annotation on the wrapper, if there is one. If no wrapper is declared
+ * specifically for the current type, check if a wrapper is defined for the parent
+ * interface. In such case, the getElement() method is required to be annotated by
+ * @XmlElementRef, not @XmlElement, in order to let JAXB infer the name from the
+ * actual subclass.
+ */
+ final WrapperClass wrapper;
+ try {
+ wrapper = getWrapperInHierarchy(type);
+ } catch (ClassNotFoundException e) {
+ fail(e.toString());
+ continue;
+ }
+ if (wrapper.type == null) {
+ // If the wrapper is intentionally undefined, skip it.
+ continue;
+ }
+ /*
+ * Now fetch the getter/setter methods, ensure that they are declared in the same class
+ * and verify that exactly one of @XmlElement or @XmlElementRef annotation is declared.
+ */
+ testingClass = wrapper.type.getCanonicalName();
+ final Method getter, setter;
+ try {
+ getter = wrapper.type.getMethod("getElement", (Class<?>[]) null);
+ setter = wrapper.type.getMethod("setElement", getter.getReturnType());
+ } catch (NoSuchMethodException e) {
+ fail(e.toString());
+ continue;
+ }
+ assertEquals("The setter method must be declared in the same class than the " +
+ "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()));
+ final XmlElement element = getter.getAnnotation(XmlElement.class);
+ assertEquals("Expected @XmlElement XOR @XmlElementRef.", (element == null),
+ getter.isAnnotationPresent(XmlElementRef.class));
+ /*
+ * If the annotation is @XmlElement, ensure that XmlElement.name() is equals to
+ * the UML identifier. Then verify that the
+ */
+ if (element != null) {
+ assertFalse("Expected @XmlElementRef.", wrapper.isInherited);
+ final UML uml = type.getAnnotation(UML.class);
+ if (uml != null) { // 'assertNotNull' is 'testInterfaceAnnotations()' job.
+ assertEquals("Wrong @XmlElement.", uml.identifier(), element.name());
+ }
+ final String namespace = assertExpectedNamespace(element.namespace(), wrapper.type, uml);
+ if (!CodeList.class.isAssignableFrom(type)) {
+ final String expected = getNamespace(getImplementation(type));
+ 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.
+ */
+ private void done() {
+ testingClass = null;
+ testingMethod = null;
+ }
+
+ /**
+ * Prints the given message followed by the name of the class being tested.
+ */
+ private void warning(String message) {
+ if (testingClass != null) {
+ final StringBuilder buffer = new StringBuilder(message);
+ buffer.append(testingClass);
+ if (testingMethod != null) {
+ buffer.append('.').append(testingMethod).append("()");
+ }
+ message = buffer.toString();
+ }
+ out.println(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.
+ *
+ * @see #testingClass
+ * @see #testingMethod
+ */
+ @After
+ public final void printFailureLocation() {
+ if (testingClass != null) {
+ warning("TEST FAILURE: ");
+ }
+ }
+}
Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/AnnotationsTestCase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/AnnotationsTestCase.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java?rev=1470608&r1=1470607&r2=1470608&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java [UTF-8] Mon Apr 22 16:32:37 2013
@@ -200,6 +200,20 @@ public final strictfp class TestUtilitie
}
/**
+ * Returns the single element from the given array. If the given array is null or
+ * does not contains exactly one element, then an {@link AssertionError} is thrown.
+ *
+ * @param <E> The type of array elements.
+ * @param array The array from which to get the singleton.
+ * @return The singleton element from the array.
+ */
+ public static <E> E getSingleton(final E[] array) {
+ assertNotNull("Null array.", array);
+ assertEquals("Not a singleton array.", 1, array.length);
+ return array[0];
+ }
+
+ /**
* Returns the single element from the given collection. If the given collection is null
* or does not contains exactly one element, then an {@link AssertionError} is thrown.
*