001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    http://www.geoapi.org
004 *
005 *    Copyright (C) 2012-2019 Open Geospatial Consortium, Inc.
006 *    All Rights Reserved. http://www.opengeospatial.org/ogc/legal
007 *
008 *    Permission to use, copy, and modify this software and its documentation, with
009 *    or without modification, for any purpose and without fee or royalty is hereby
010 *    granted, provided that you include the following on ALL copies of the software
011 *    and documentation or portions thereof, including modifications, that you make:
012 *
013 *    1. The full text of this NOTICE in a location viewable to users of the
014 *       redistributed or derivative work.
015 *    2. Notice of any changes or modifications to the OGC files, including the
016 *       date changes were made.
017 *
018 *    THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE
019 *    NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
020 *    TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
021 *    THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
022 *    PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
023 *
024 *    COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
025 *    CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
026 *
027 *    The name and trademarks of copyright holders may NOT be used in advertising or
028 *    publicity pertaining to the software without specific, written prior permission.
029 *    Title to copyright in this software and any associated documentation will at all
030 *    times remain with copyright holders.
031 */
032package org.opengis.test.coverage.image;
033
034import javax.imageio.ImageReader;
035import javax.imageio.ImageWriter;
036import javax.imageio.spi.ImageReaderSpi;
037import javax.imageio.spi.ImageWriterSpi;
038import javax.imageio.spi.ImageReaderWriterSpi;
039import javax.imageio.metadata.IIOMetadataFormat;
040import javax.imageio.metadata.IIOMetadataFormatImpl;
041
042import org.opengis.test.Validator;
043import org.opengis.test.ValidatorContainer;
044
045import static org.opengis.test.Assert.*;
046
047
048/**
049 * Validators for implementations of {@link java.awt.image} or {@link javax.imageio} services.
050 *
051 * @author  Martin Desruisseaux (Geomatys)
052 * @version 3.1
053 * @since   3.1
054 */
055public class ImageValidator extends Validator {
056    /**
057     * Creates a new validator instance.
058     *
059     * @param container  the set of validators to use for validating other kinds of objects
060     *                   (see {@linkplain #container field javadoc}).
061     */
062    public ImageValidator(final ValidatorContainer container) {
063        super(container, "org.opengis.test.coverage.image");
064    }
065
066    /**
067     * Validates the given provider of image readers.
068     * First, this method verifies that mandatory elements are non-null, arrays are non-empty
069     * (Image I/O specification requires them to be {@code null} rather than empty), and class
070     * names are valid. Next, this method invokes {@link #validate(IIOMetadataFormat)} for each
071     * metadata format (which can be null).
072     *
073     * @param provider  the provider to validate, or {@code null} if none.
074     */
075    public void validate(final ImageReaderSpi provider) {
076        if (provider != null) {
077            validateProvider(provider, ImageReader.class);
078            final Class<?>[] inputTypes = provider.getInputTypes();
079            mandatory("ImageReaderSpi: shall have an inputTypes array.", inputTypes);
080            validateArray("inputTypes", inputTypes);
081            final String[] imageWriterSpiNames = provider.getImageWriterSpiNames();
082            validateArray("imageWriterSpiNames", imageWriterSpiNames);
083            if (imageWriterSpiNames != null) {
084                final ClassLoader loader = provider.getClass().getClassLoader();
085                for (int i=0; i<imageWriterSpiNames.length; i++) {
086                    final String field = "imageWriterSpiNames[" + i + ']';
087                    final String className = imageWriterSpiNames[i];
088                    assertNotNull(field + " can not be null.", className);
089                    validateClass(field, ImageWriterSpi.class, loader, className);
090                }
091            }
092        }
093    }
094
095    /**
096     * Validates the given provider of image writers.
097     * First, this method verifies that mandatory elements are non-null, arrays are non-empty
098     * (Image I/O specification requires them to be {@code null} rather than empty), and class
099     * names are valid. Next, this method invokes {@link #validate(IIOMetadataFormat)} for each
100     * metadata format (which can be null).
101     *
102     * @param provider  the provider to validate, or {@code null} if none.
103     */
104    public void validate(final ImageWriterSpi provider) {
105        if (provider != null) {
106            validateProvider(provider, ImageWriter.class);
107            final Class<?>[] outputTypes = provider.getOutputTypes();
108            mandatory("ImageWriterSpi: shall have an outputTypes array.", outputTypes);
109            validateArray("outputTypes", outputTypes);
110            final String[] imageReaderSpiNames = provider.getImageReaderSpiNames();
111            validateArray("imageReaderSpiNames", imageReaderSpiNames);
112            if (imageReaderSpiNames != null) {
113                final ClassLoader loader = provider.getClass().getClassLoader();
114                for (int i=0; i<imageReaderSpiNames.length; i++) {
115                    final String field = "imageReaderSpiNames[" + i + ']';
116                    final String className = imageReaderSpiNames[i];
117                    assertNotNull(field + " can not be null.", className);
118                    validateClass(field, ImageReaderSpi.class, loader, className);
119                }
120            }
121        }
122    }
123
124    /**
125     * Validates the given image reader or image writer provider.
126     *
127     * @param spi  the provider to validate, or {@code null} if none.
128     */
129    private void validateProvider(final ImageReaderWriterSpi spi, final Class<?> pluginType) {
130        mandatory("ImageReaderWriterSpi: shall have a vendorName string.", spi.getVendorName());
131        mandatory("ImageReaderWriterSpi: shall have a version string.",    spi.getVersion());
132        final String[] formatNames = spi.getFormatNames();
133        mandatory("ImageReaderWriterSpi: shall have a formatNames array.", formatNames);
134        validateArray("formatNames",  formatNames);
135        validateArray("fileSuffixes", spi.getFileSuffixes());
136        validateArray("MIMETypes",    spi.getMIMETypes());
137        validateClass("pluginClassName", pluginType, spi.getClass().getClassLoader(), spi.getPluginClassName());
138        validateMetadataFormatName("Stream", spi.getNativeStreamMetadataFormatName(), spi.getExtraStreamMetadataFormatNames());
139        validateMetadataFormatName("Image",  spi.getNativeImageMetadataFormatName(),  spi.getExtraImageMetadataFormatNames());
140        /*
141         * Ensures that a IIOMetadataFormat can be instantiated for each declared format name.
142         * Then, invokes validate(IIOMetadataFormat) for each format. Note that the format are
143         * allowed to be null according Image I/O specification.
144         */
145        if (spi.isStandardStreamMetadataFormatSupported()) {
146            assertSame("Expected the standard metadata format instance.",
147                    IIOMetadataFormatImpl.getStandardFormatInstance(),
148                    spi.getStreamMetadataFormat(IIOMetadataFormatImpl.standardMetadataFormatName));
149        }
150        if (spi.isStandardImageMetadataFormatSupported()) {
151            assertSame("Expected the standard metadata format instance.",
152                    IIOMetadataFormatImpl.getStandardFormatInstance(),
153                    spi.getImageMetadataFormat(IIOMetadataFormatImpl.standardMetadataFormatName));
154        }
155        String formatName = spi.getNativeStreamMetadataFormatName();
156        if (formatName != null) {
157            validate(spi.getStreamMetadataFormat(formatName));
158        }
159        formatName = spi.getNativeImageMetadataFormatName();
160        if (formatName != null) {
161            validate(spi.getImageMetadataFormat(formatName));
162        }
163        String[] names = spi.getExtraStreamMetadataFormatNames();
164        if (names != null) {
165            for (final String name : names) {
166                validate(spi.getStreamMetadataFormat(name));
167            }
168        }
169        names = spi.getExtraImageMetadataFormatNames();
170        if (names != null) {
171            for (final String name : names) {
172                validate(spi.getImageMetadataFormat(name));
173            }
174        }
175    }
176
177    /**
178     * Validates the image or stream metadata format names.
179     * This method ensures that there is no duplicated values.
180     */
181    private static void validateMetadataFormatName(final String type, String nativeMetadataFormatName,
182            final String[] extraMetadataFormatNames)
183    {
184        if (nativeMetadataFormatName != null) {
185            nativeMetadataFormatName = nativeMetadataFormatName.trim();
186            assertFalse("The native" + type + "MetadataFormatName value can not be equal to \"" +
187                    IIOMetadataFormatImpl.standardMetadataFormatName + "\".",
188                    IIOMetadataFormatImpl.standardMetadataFormatName.equalsIgnoreCase(nativeMetadataFormatName));
189        }
190        if (extraMetadataFormatNames != null) {
191            final String field = "extra" + type + "MetadataFormatNames";
192            validateArray(field, extraMetadataFormatNames);
193            for (int i=0; i<extraMetadataFormatNames.length; i++) {
194                final String formatName = extraMetadataFormatNames[i].trim();
195                assertFalse("The " + field + '[' + i + "] value can not be equal to \"" +
196                        IIOMetadataFormatImpl.standardMetadataFormatName + "\".",
197                        IIOMetadataFormatImpl.standardMetadataFormatName.equalsIgnoreCase(formatName));
198                if (nativeMetadataFormatName != null) {
199                    assertFalse("The " + field + '[' + i + "] value can not be equal to \"" +
200                            nativeMetadataFormatName + "\" since it is already declared as the native format name.",
201                            nativeMetadataFormatName.equalsIgnoreCase(formatName));
202                }
203            }
204        }
205    }
206
207    /**
208     * Ensures that the given array complies with the conditions of Java Image I/O.
209     * The array can be either null or non-empty; empty arrays are illegal.
210     * Then ensures that there is no duplicated value.
211     *
212     * @param field  the field name, used in case of errors.
213     * @param array  the array to validate, or {@code null} if none.
214     */
215    private static void validateArray(final String field, final Object[] array) {
216        if (array != null) {
217            assertStrictlyPositive("The " + field + " array shall be either null or non-empty.", array.length);
218            for (int i=0; i<array.length; i++) {
219                final Object element = array[i];
220                assertNotNull(field + '[' + i + ']', element);
221                for (int j=i; ++i<array.length;) {
222                    if (element.equals(array[i])) {
223                        fail(field + '[' + i + "] and [" + j + "] duplicate the \"" + element + "\" value.");
224                    }
225                }
226            }
227        }
228    }
229
230    /**
231     * Ensure that given class exists and is an instance of the given type.
232     *
233     * @param field         the name of the tested field.
234     * @param expectedType  the expected base class.
235     * @param loader        the loader to use for loading the class.
236     * @param classname     the name of the class to test.
237     */
238    private void validateClass(final String field, final Class<?> expectedType,
239            final ClassLoader loader, final String classname)
240    {
241        mandatory("ImageReaderWriterSpi: shall have a " + field + " string.", classname);
242        if (classname != null) try {
243            final Class<?> actual = Class.forName(classname, false, loader);
244            assertTrue(actual.getCanonicalName() + " is not an instance of " +
245                    expectedType.getSimpleName() + '.', expectedType.isAssignableFrom(actual));
246        } catch (ClassNotFoundException e) {
247            throw new AssertionError("Class \"" + classname + "\" declared in " + field + " was not found.", e);
248        }
249    }
250
251    /**
252     * Validates the given metadata format.
253     *
254     * @param format  the metadata format to validate, or {@code null} if none.
255     */
256    public void validate(final IIOMetadataFormat format) {
257        // Not yet implemented.
258    }
259}