/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.metadata; import java.util.Collection; import java.util.Set; import java.util.HashSet; import org.opengis.util.CodeList; import org.opengis.metadata.MetaData; import org.opengis.metadata.extent.VerticalExtent; import org.opengis.metadata.citation.OnLineResource; import org.opengis.metadata.citation.CitationFactory; import org.opengis.metadata.content.ImagingCondition; import org.opengis.metadata.content.CoverageContentType; import org.opengis.metadata.maintenance.ScopeDescription; import org.opengis.metadata.identification.AggregateInformation; import org.opengis.metadata.identification.RepresentativeFraction; import org.geotools.resources.Classes; import org.geotools.util.CheckedCollection; import org.geotools.metadata.iso.MetaDataImpl; import org.junit.*; import static org.junit.Assert.*; /** * Tests every implementation in the {@link org.geotools.metadata.iso} package. * * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (Geomatys) * * @todo Current implementation relies on {@link MetaData} dependencies. This is probably * not enough; we should provide an explicit list of metadata interface. */ public final class ISOTest { /** * {@code true} for displaying debugging informations. */ private static final boolean VERBOSE = false; /** * Root package for interfaces, with trailing dot. */ private static final String INTERFACE_PACKAGE = "org.opengis.metadata."; /** * Root package for implementations, with trailing dot. */ private static final String IMPLEMENTATION_PACKAGE = "org.geotools.metadata.iso."; /** * Suffix for implementation classes. */ private static final String IMPLEMENTATION_SUFFIX = "Impl"; /** * List of GeoAPI interfaces to test. This list is not exclusive, since this test suite * will automatically scans for dependencies even if an interface do not appears in this * list. This list should not contains any {@link CodeList}. */ private static final Class<?>[] TEST = new Class[] { org.opengis.metadata.ApplicationSchemaInformation.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.PortrayalCatalogueReference.class, org.opengis.metadata.SpatialAttributeSupplement.class, org.opengis.metadata.citation.Address.class, org.opengis.metadata.citation.Citation.class, org.opengis.metadata.citation.CitationDate.class, org.opengis.metadata.citation.CitationFactory.class, org.opengis.metadata.citation.Contact.class, org.opengis.metadata.citation.OnLineResource.class, org.opengis.metadata.citation.ResponsibleParty.class, org.opengis.metadata.citation.Series.class, org.opengis.metadata.citation.Telephone.class, org.opengis.metadata.constraint.Constraints.class, org.opengis.metadata.constraint.LegalConstraints.class, org.opengis.metadata.constraint.SecurityConstraints.class, org.opengis.metadata.content.Band.class, org.opengis.metadata.content.ContentInformation.class, org.opengis.metadata.content.CoverageDescription.class, org.opengis.metadata.content.FeatureCatalogueDescription.class, org.opengis.metadata.content.ImageDescription.class, org.opengis.metadata.content.RangeDimension.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.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.BrowseGraphic.class, org.opengis.metadata.identification.DataIdentification.class, org.opengis.metadata.identification.Identification.class, org.opengis.metadata.identification.Keywords.class, org.opengis.metadata.identification.RepresentativeFraction.class, org.opengis.metadata.identification.Resolution.class, org.opengis.metadata.identification.ServiceIdentification.class, org.opengis.metadata.identification.Usage.class, org.opengis.metadata.lineage.Lineage.class, org.opengis.metadata.lineage.ProcessStep.class, org.opengis.metadata.lineage.Source.class, org.opengis.metadata.maintenance.MaintenanceInformation.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.DataQuality.class, org.opengis.metadata.quality.DomainConsistency.class, org.opengis.metadata.quality.Element.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.spatial.Dimension.class, org.opengis.metadata.spatial.GeometricObjects.class, org.opengis.metadata.spatial.Georectified.class, org.opengis.metadata.spatial.Georeferenceable.class, org.opengis.metadata.spatial.GridSpatialRepresentation.class, org.opengis.metadata.spatial.SpatialRepresentation.class, org.opengis.metadata.spatial.VectorSpatialRepresentation.class }; /** * GeoAPI interfaces that are know to be unimplemented at this stage. */ private static final Class<?>[] UNIMPLEMENTED = new Class[] { AggregateInformation.class, CoverageContentType.class, ImagingCondition.class, CitationFactory.class, // SHOULD THIS INTERFACE REALLY EXISTS IN GEOAPI? RepresentativeFraction.class, // Implemented on top of 'Number'. VerticalExtent.class, // Inconsistent 'verticalCRS' type in GeoAPI interface. ScopeDescription.class, // Only partially implemented (no references to Features). OnLineResource.class // No 'setProtocol' method. }; /** * Ensures that the {@link #TEST} array do not contains code list. */ @Test public void testNoCodeList() { for (int i=0; i<TEST.length; i++) { final Class type = TEST[i]; assertFalse(type.getName(), CodeList.class.isAssignableFrom(type)); } } /** * Tests all dependencies starting from the {@link MetaDataImpl} class. */ @Test public void testDependencies() { assertNull(getImplementation(Number.class)); assertSame(MetaDataImpl.class, getImplementation(MetaData.class)); final Set<Class<?>> done = new HashSet<Class<?>>(); for (int i=0; i<TEST.length; i++) { final Class<?> type = TEST[i]; final Class<?> impl = getImplementation(type); if (impl == null) { if (isImplemented(type)) { fail(type.getName() + " is not implemented."); } continue; } assertSetters(new PropertyAccessor(impl, type), done); } if (VERBOSE) { System.out.println(done); } } /** * Recursively ensures that the specified metadata implementation has * setters for every methods. */ private static void assertSetters(final PropertyAccessor accessor, final Set<Class<?>> done) { if (done.add(accessor.type)) { /* * Tries to instantiate the implementation. Every implementation should have a * no-args constructor, and their instantiation should never fail. Note that * this dummy will also be of some help later in this test. */ final Object dummyInstance; final boolean isImplemented = isImplemented(accessor.type); if (isImplemented) try { dummyInstance = accessor.implementation.getConstructor((Class[]) null). newInstance((Object[]) null); } catch (Exception e) { fail(e.toString()); return; } else { dummyInstance = null; } /* * Iterates over all properties defined in the interface, * and checks for the existences of a setter method. */ final String classname = Classes.getShortName(accessor.type) + '.'; final int count = accessor.count(); for (int i=0; i<count; i++) { final String name = accessor.name(i); assertNotNull(String.valueOf(i), name); final String fullname = classname + name; assertEquals(fullname, i, accessor.indexOf(name)); if (!isImplemented) { continue; } // We can not continue below this point for // implementations that are only partial. assertTrue(fullname, accessor.isWritable(i)); /* * Get the property type. In the special case where the property type * is a collection, get an empty collection from the implementation. * This is needed in order to get the element type in the collection. */ Class<?> type = accessor.type(i); if (Collection.class.isAssignableFrom(type)) { final Object example = accessor.get(i, dummyInstance); if (example instanceof CheckedCollection) { type = ((CheckedCollection) example).getElementType(); } } final Class<?> impl = getImplementation(type); if (impl != null) { assertSetters(new PropertyAccessor(impl, type), done); } } } } /** * Returns the implementation class for the specified interface class, * or {@code null} if none. */ private static Class<?> getImplementation(final Class<?> type) { if (!CodeList.class.isAssignableFrom(type)) { String name = type.getName(); if (name.startsWith(INTERFACE_PACKAGE)) { name = IMPLEMENTATION_PACKAGE + name.substring(INTERFACE_PACKAGE.length()) + IMPLEMENTATION_SUFFIX; try { return Class.forName(name); } catch (ClassNotFoundException e) { /* * Found a class which is not implemented. Before to report an error, * check if it is part of the list of known unimplemented interfaces. */ if (isImplemented(type)) { fail(e.toString()); } } } } return null; } /** * Returns {@code true} if the specified type is not in the list of * known unimplemented types. */ private static boolean isImplemented(final Class<?> type) { for (int i=0; i<UNIMPLEMENTED.length; i++) { if (type.equals(UNIMPLEMENTED[i])) { return false; } } return true; } }