001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2007, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.metadata;
017:
018: import java.util.Collection;
019: import java.util.Set;
020: import java.util.HashSet;
021: import junit.framework.Test;
022: import junit.framework.TestCase;
023: import junit.framework.TestSuite;
024: import org.opengis.metadata.citation.CitationFactory;
025: import org.opengis.metadata.content.CoverageContentType;
026: import org.opengis.metadata.content.ImagingCondition;
027:
028: import org.opengis.util.CodeList;
029: import org.opengis.metadata.MetaData;
030: import org.opengis.metadata.quality.Element;
031: import org.opengis.metadata.extent.VerticalExtent;
032: import org.opengis.metadata.citation.OnLineResource;
033: import org.opengis.metadata.maintenance.ScopeDescription;
034: import org.opengis.metadata.identification.AggregateInformation;
035: import org.opengis.metadata.identification.RepresentativeFraction;
036:
037: import org.geotools.resources.Utilities;
038: import org.geotools.util.CheckedCollection;
039: import org.geotools.metadata.iso.MetaDataImpl;
040:
041: /**
042: * Tests every implementation in the {@link org.geotools.metadata.iso} package.
043: *
044: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/test/java/org/geotools/metadata/ISOTest.java $
045: * @version $Id: ISOTest.java 25193 2007-04-18 13:37:38Z desruisseaux $
046: * @author Martin Desruisseaux (Geomatys)
047: *
048: * @todo Current implementation relies on {@link MetaData} dependencies. This is probably
049: * not enough; we should provide an explicit list of metadata interface.
050: */
051: public class ISOTest extends TestCase {
052: /**
053: * {@code true} for displaying debugging informations.
054: */
055: private static final boolean VERBOSE = false;
056:
057: /**
058: * Root package for interfaces, with trailing dot.
059: */
060: private static final String INTERFACE_PACKAGE = "org.opengis.metadata.";
061:
062: /**
063: * Root package for implementations, with trailing dot.
064: */
065: private static final String IMPLEMENTATION_PACKAGE = "org.geotools.metadata.iso.";
066:
067: /**
068: * Suffix for implementation classes.
069: */
070: private static final String IMPLEMENTATION_SUFFIX = "Impl";
071:
072: /**
073: * List of GeoAPI interfaces to test. This list is not exclusive, since this test suite
074: * will automatically scans for dependencies even if an interface do not appears in this
075: * list. This list should not contains any {@link CodeList}.
076: */
077: private static final Class[] TEST = new Class[] {
078: org.opengis.metadata.ApplicationSchemaInformation.class,
079: org.opengis.metadata.ExtendedElementInformation.class,
080: org.opengis.metadata.FeatureTypeList.class,
081: org.opengis.metadata.Identifier.class,
082: org.opengis.metadata.MetaData.class,
083: org.opengis.metadata.MetadataExtensionInformation.class,
084: org.opengis.metadata.PortrayalCatalogueReference.class,
085: org.opengis.metadata.SpatialAttributeSupplement.class,
086: org.opengis.metadata.citation.Address.class,
087: org.opengis.metadata.citation.Citation.class,
088: org.opengis.metadata.citation.CitationDate.class,
089: org.opengis.metadata.citation.CitationFactory.class,
090: org.opengis.metadata.citation.Contact.class,
091: org.opengis.metadata.citation.OnLineResource.class,
092: org.opengis.metadata.citation.ResponsibleParty.class,
093: org.opengis.metadata.citation.Series.class,
094: org.opengis.metadata.citation.Telephone.class,
095: org.opengis.metadata.constraint.Constraints.class,
096: org.opengis.metadata.constraint.LegalConstraints.class,
097: org.opengis.metadata.constraint.SecurityConstraints.class,
098: org.opengis.metadata.content.Band.class,
099: org.opengis.metadata.content.ContentInformation.class,
100: org.opengis.metadata.content.CoverageDescription.class,
101: org.opengis.metadata.content.FeatureCatalogueDescription.class,
102: org.opengis.metadata.content.ImageDescription.class,
103: org.opengis.metadata.content.RangeDimension.class,
104: org.opengis.metadata.distribution.DigitalTransferOptions.class,
105: org.opengis.metadata.distribution.Distribution.class,
106: org.opengis.metadata.distribution.Distributor.class,
107: org.opengis.metadata.distribution.Format.class,
108: org.opengis.metadata.distribution.Medium.class,
109: org.opengis.metadata.distribution.StandardOrderProcess.class,
110: org.opengis.metadata.extent.BoundingPolygon.class,
111: org.opengis.metadata.extent.Extent.class,
112: org.opengis.metadata.extent.GeographicBoundingBox.class,
113: org.opengis.metadata.extent.GeographicDescription.class,
114: org.opengis.metadata.extent.GeographicExtent.class,
115: org.opengis.metadata.extent.SpatialTemporalExtent.class,
116: org.opengis.metadata.extent.TemporalExtent.class,
117: org.opengis.metadata.extent.VerticalExtent.class,
118: org.opengis.metadata.identification.AggregateInformation.class,
119: org.opengis.metadata.identification.BrowseGraphic.class,
120: org.opengis.metadata.identification.DataIdentification.class,
121: org.opengis.metadata.identification.Identification.class,
122: org.opengis.metadata.identification.Keywords.class,
123: org.opengis.metadata.identification.RepresentativeFraction.class,
124: org.opengis.metadata.identification.Resolution.class,
125: org.opengis.metadata.identification.ServiceIdentification.class,
126: org.opengis.metadata.identification.Usage.class,
127: org.opengis.metadata.lineage.Lineage.class,
128: org.opengis.metadata.lineage.ProcessStep.class,
129: org.opengis.metadata.lineage.Source.class,
130: org.opengis.metadata.maintenance.MaintenanceInformation.class,
131: org.opengis.metadata.maintenance.ScopeDescription.class,
132: org.opengis.metadata.quality.AbsoluteExternalPositionalAccuracy.class,
133: org.opengis.metadata.quality.AccuracyOfATimeMeasurement.class,
134: org.opengis.metadata.quality.Completeness.class,
135: org.opengis.metadata.quality.CompletenessCommission.class,
136: org.opengis.metadata.quality.CompletenessOmission.class,
137: org.opengis.metadata.quality.ConceptualConsistency.class,
138: org.opengis.metadata.quality.ConformanceResult.class,
139: org.opengis.metadata.quality.DataQuality.class,
140: org.opengis.metadata.quality.DomainConsistency.class,
141: org.opengis.metadata.quality.Element.class,
142: org.opengis.metadata.quality.FormatConsistency.class,
143: org.opengis.metadata.quality.GriddedDataPositionalAccuracy.class,
144: org.opengis.metadata.quality.LogicalConsistency.class,
145: org.opengis.metadata.quality.NonQuantitativeAttributeAccuracy.class,
146: org.opengis.metadata.quality.PositionalAccuracy.class,
147: org.opengis.metadata.quality.QuantitativeAttributeAccuracy.class,
148: org.opengis.metadata.quality.QuantitativeResult.class,
149: org.opengis.metadata.quality.RelativeInternalPositionalAccuracy.class,
150: org.opengis.metadata.quality.Result.class,
151: org.opengis.metadata.quality.Scope.class,
152: org.opengis.metadata.quality.TemporalAccuracy.class,
153: org.opengis.metadata.quality.TemporalConsistency.class,
154: org.opengis.metadata.quality.TemporalValidity.class,
155: org.opengis.metadata.quality.ThematicAccuracy.class,
156: org.opengis.metadata.quality.ThematicClassificationCorrectness.class,
157: org.opengis.metadata.quality.TopologicalConsistency.class,
158: org.opengis.metadata.spatial.Dimension.class,
159: org.opengis.metadata.spatial.GeometricObjects.class,
160: org.opengis.metadata.spatial.Georectified.class,
161: org.opengis.metadata.spatial.Georeferenceable.class,
162: org.opengis.metadata.spatial.GridSpatialRepresentation.class,
163: org.opengis.metadata.spatial.SpatialRepresentation.class,
164: org.opengis.metadata.spatial.VectorSpatialRepresentation.class };
165:
166: /**
167: * GeoAPI interfaces that are know to be unimplemented at this stage.
168: */
169: private static final Class[] UNIMPLEMENTED = new Class[] {
170: AggregateInformation.class, CoverageContentType.class,
171: ImagingCondition.class, CitationFactory.class, // SHOULD THIS INTERFACE REALLY EXISTS IN GEOAPI?
172: RepresentativeFraction.class, // Implemented on top of 'Number'.
173: VerticalExtent.class, // Inconsistent 'verticalCRS' type in GeoAPI interface.
174: ScopeDescription.class, // Only partially implemented (no references to Features).
175: OnLineResource.class // No 'setProtocol' method.
176: };
177:
178: /**
179: * Run the suit from the command line.
180: */
181: public static void main(final String[] args) {
182: junit.textui.TestRunner.run(suite());
183: }
184:
185: /**
186: * Returns the test suite.
187: */
188: public static Test suite() {
189: return new TestSuite(ISOTest.class);
190: }
191:
192: /**
193: * Constructs a test case with the given name.
194: */
195: public ISOTest(final String name) {
196: super (name);
197: }
198:
199: /**
200: * Ensures that the {@link #TEST} array do not contains code list.
201: */
202: public void testNoCodeList() {
203: for (int i = 0; i < TEST.length; i++) {
204: final Class type = TEST[i];
205: assertFalse(type.getName(), CodeList.class
206: .isAssignableFrom(type));
207: }
208: }
209:
210: /**
211: * Tests all dependencies starting from the {@link MetaDataImpl} class.
212: */
213: public void testDependencies() {
214: assertNull(getImplementation(Number.class));
215: assertSame(MetaDataImpl.class,
216: getImplementation(MetaData.class));
217: final Set done = new HashSet();
218: for (int i = 0; i < TEST.length; i++) {
219: final Class type = TEST[i];
220: final Class impl = getImplementation(type);
221: if (impl == null) {
222: if (isImplemented(type)) {
223: fail(type.getName() + " is not implemented.");
224: }
225: continue;
226: }
227: assertSetters(new PropertyAccessor(impl, type), done);
228: }
229: if (VERBOSE) {
230: System.out.println(done);
231: }
232: }
233:
234: /**
235: * Recursively ensures that the specified metadata implementation has
236: * setters for every methods.
237: */
238: private static void assertSetters(final PropertyAccessor accessor,
239: final Set/*<Class>*/done) {
240: if (done.add(accessor.type)) {
241: /*
242: * Tries to instantiate the implementation. Every implementation should have a
243: * no-args constructor, and their instantiation should never fail. Note that
244: * this dummy will also be of some help later in this test.
245: */
246: final Object dummyInstance;
247: final boolean isImplemented = isImplemented(accessor.type);
248: if (isImplemented)
249: try {
250: dummyInstance = accessor.implementation
251: .getConstructor((Class[]) null)
252: .newInstance((Object[]) null);
253: } catch (Exception e) {
254: fail(e.toString());
255: return;
256: }
257: else {
258: dummyInstance = null;
259: }
260: /*
261: * Iterates over all properties defined in the interface,
262: * and checks for the existences of a setter method.
263: */
264: final String classname = Utilities
265: .getShortName(accessor.type) + '.';
266: final int count = accessor.count();
267: for (int i = 0; i < count; i++) {
268: final String name = accessor.name(i);
269: assertNotNull(String.valueOf(i), name);
270: final String fullname = classname + name;
271: assertEquals(fullname, i, accessor.indexOf(name));
272: if (!isImplemented) {
273: continue;
274: }
275: // We can not continue below this point for
276: // implementations that are only partial.
277: assertTrue(fullname, accessor.isWritable(i));
278: /*
279: * Get the property type. In the special case where the property type
280: * is a collection, get an empty collection from the implementation.
281: * This is needed in order to get the element type in the collection.
282: */
283: Class type = accessor.type(i);
284: if (Collection.class.isAssignableFrom(type)) {
285: final Object example = accessor.get(i,
286: dummyInstance);
287: if (example instanceof CheckedCollection) {
288: type = ((CheckedCollection) example)
289: .getElementType();
290: }
291: }
292: final Class impl = getImplementation(type);
293: if (impl != null) {
294: assertSetters(new PropertyAccessor(impl, type),
295: done);
296: }
297: }
298: }
299: }
300:
301: /**
302: * Returns the implementation class for the specified interface class,
303: * or {@code null} if none.
304: */
305: private static Class getImplementation(final Class type) {
306: if (!CodeList.class.isAssignableFrom(type)) {
307: String name = type.getName();
308: if (name.startsWith(INTERFACE_PACKAGE)) {
309: name = IMPLEMENTATION_PACKAGE
310: + name.substring(INTERFACE_PACKAGE.length())
311: + IMPLEMENTATION_SUFFIX;
312: try {
313: return Class.forName(name);
314: } catch (ClassNotFoundException e) {
315: /*
316: * Found a class which is not implemented. Before to report an error,
317: * check if it is part of the list of known unimplemented interfaces.
318: */
319: if (isImplemented(type)) {
320: fail(e.toString());
321: }
322: }
323: }
324: }
325: return null;
326: }
327:
328: /**
329: * Returns {@code true} if the specified type is not in the list of
330: * known unimplemented types.
331: */
332: private static boolean isImplemented(final Class type) {
333: for (int i = 0; i < UNIMPLEMENTED.length; i++) {
334: if (type.equals(UNIMPLEMENTED[i])) {
335: return false;
336: }
337: }
338: return true;
339: }
340: }
|