001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    http://www.geoapi.org
004 *
005 *    Copyright (C) 2011-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.referencing.gigs;
033
034import java.util.Collection;
035import org.opengis.util.Factory;
036import org.opengis.util.FactoryException;
037import org.opengis.util.GenericName;
038import org.opengis.metadata.Identifier;
039import org.opengis.referencing.AuthorityFactory;
040import org.opengis.referencing.IdentifiedObject;
041import org.opengis.test.referencing.ReferencingTestCase;
042import org.opengis.test.Configuration;
043
044import static org.junit.Assert.*;
045
046
047/**
048 * Base class for tests of EPSG definitions (2000 series).
049 * The tests for this series are designed to verify the correctness of geodetic parameters that
050 * are delivered with the software. The comparison to be taken as truth is the EPSG Dataset.
051 *
052 * @param  <T>  the type of objects to test.
053 *
054 * @see org.opengis.test.referencing.AuthorityFactoryTest
055 * @see org.opengis.test.TestSuite
056 *
057 * @author  Martin Desruisseaux (Geomatys)
058 * @version 3.1
059 * @since   3.1
060 */
061public strictfp abstract class AuthorityFactoryTestCase<T> extends GIGSTestCase {
062    /**
063     * The value to give to the {@link #aliases} field for meaning "no alias".
064     */
065    static final String[] NONE = new String[0];
066
067    /**
068     * The EPSG code of the {@code T} instance to test.
069     * This field is set by all test methods before to create and to verify the {@code T} instance.
070     *
071     * <p>This code can be compared to the identifiers returned by {@link IdentifiedObject#getIdentifiers()}.</p>
072     */
073    public int code;
074
075    /**
076     * The name of the {@code T} instance to test, as used in the EPSG dataset.
077     * This field is set by all test methods before to create and to verify the {@code T} instance.
078     *
079     * <p>This name will be compared to the value returned by {@link IdentifiedObject#getName()},
080     * unless {@link #isStandardNameSupported} is {@code false}.</p>
081     */
082    public String name;
083
084    /**
085     * The expected aliases of the {@code T} instance to test, or an empty array if none.
086     * This field is set by all test methods before to create and to verify the {@code T} instance.
087     *
088     * <p>Those aliases will be compared to the values returned by {@link IdentifiedObject#getAlias()},
089     * unless {@link #isStandardAliasSupported} is {@code false}.</p>
090     */
091    public String[] aliases;
092
093    /**
094     * {@code true} if the tested factories support {@linkplain IdentifiedObject#getName() name}.
095     * If {@code true} (the default), then the test methods will ensure that the identified objects
096     * created by the factories declare the same name than the GIGS tests.
097     * If {@code false}, then the names are ignored.
098     */
099    protected boolean isStandardNameSupported;
100
101    /**
102     * {@code true} if the tested factories support {@linkplain IdentifiedObject#getAlias() aliases}.
103     * If {@code true} (the default), then the test methods will ensure that the identified objects
104     * created by the factories declare at least all the aliases enumerated in the GIGS tests - additional aliases,
105     * if any, are ignored. If {@code false}, then the aliases are ignored.
106     */
107    protected boolean isStandardAliasSupported;
108
109    /**
110     * {@code true} if the {@link IdentifiedObject} instances created indirectly by the factories
111     * are expected to have correct identification information.
112     *
113     * <div class="note"><b>Example:</b>
114     * when testing a {@link org.opengis.referencing.crs.CoordinateReferenceSystem} (CRS) object,
115     * the CRS authority code will be verified unconditionally but the authority codes of associated objects
116     * ({@link org.opengis.referencing.datum.GeodeticDatum} or {@link org.opengis.referencing.cs.CoordinateSystem})
117     * will be verified only if this flag is {@code true}.</div>
118     */
119    protected boolean isDependencyIdentificationSupported;
120
121    /**
122     * {@code true} if the factory support creation of deprecated objects.
123     */
124    protected boolean isDeprecatedObjectCreationSupported;
125
126    /**
127     * {@code true} if the tested object is particularly important to E&amp;P industry.
128     * This field is set at the beginning of test methods.
129     */
130    boolean important;
131
132    /**
133     * Creates a new test which will use the given factories to execute.
134     *
135     * @param factories  the factories to be used by the test. Those factories passed verbatim to the
136     *        {@linkplain ReferencingTestCase#ReferencingTestCase(Factory[]) super-class constructor}.
137     */
138    protected AuthorityFactoryTestCase(final AuthorityFactory... factories) {
139        super(factories);
140        @SuppressWarnings("unchecked")
141        final boolean[] isEnabled = getEnabledFlags(
142                Configuration.Key.isStandardNameSupported,
143                Configuration.Key.isStandardAliasSupported,
144                Configuration.Key.isDependencyIdentificationSupported,
145                Configuration.Key.isDeprecatedObjectCreationSupported);
146        isStandardNameSupported             = isEnabled[0];
147        isStandardAliasSupported            = isEnabled[1];
148        isDependencyIdentificationSupported = isEnabled[2];
149        isDeprecatedObjectCreationSupported = isEnabled[3];
150    }
151
152    /**
153     * Returns information about the configuration of the test which has been run.
154     * This method returns a map containing:
155     *
156     * <ul>
157     *   <li>All the following values associated to the {@link org.opengis.test.Configuration.Key} of the same name:
158     *     <ul>
159     *       <li>{@link #isStandardNameSupported}</li>
160     *       <li>{@link #isStandardAliasSupported}</li>
161     *       <li>{@link #isDependencyIdentificationSupported}</li>
162     *       <li>The factories used by the test (provided by subclasses)</li>
163     *     </ul>
164     *   </li>
165     * </ul>
166     *
167     * @return the configuration of the test being run.
168     */
169    @Override
170    public Configuration configuration() {
171        final Configuration op = super.configuration();
172        assertNull(op.put(Configuration.Key.isStandardNameSupported,             isStandardNameSupported));
173        assertNull(op.put(Configuration.Key.isStandardAliasSupported,            isStandardAliasSupported));
174        assertNull(op.put(Configuration.Key.isDependencyIdentificationSupported, isDependencyIdentificationSupported));
175        return op;
176    }
177
178    /**
179     * Returns the instance to be tested. When this method is invoked for the first time, it creates the instance
180     * to test by invoking a {@code createXXX(String)} method from the user-specified {@link AuthorityFactory}
181     * with the current {@link #code} value in argument. The created object is then cached and returned in
182     * subsequent invocations of this method.
183     *
184     * <p>Usually, each test method creates exactly one object. But a few (relatively rare) tests may create
185     * more than one object. In such case, the instance returned by this method may vary.</p>
186     *
187     * @return the instance to test.
188     * @throws FactoryException if an error occurred while creating the identified object.
189     */
190    public abstract T getIdentifiedObject() throws FactoryException;
191
192    /**
193     * Returns a name of the given object that can be compared against the expected name.
194     * The default implementation returns {@code object.getName().getCode()} or {@code null}
195     * if the given object, its name or its code is null.
196     *
197     * <p>Subclasses can override this method when testing an {@link AuthorityFactory} implementation
198     * which is known to use slightly different name than the one used in the EPSG database, or if the
199     * implementation stores the EPSG name as an {@linkplain IdentifiedObject#getAlias() alias} instead
200     * than as the {@linkplain IdentifiedObject#getName() primary name}.</p>
201     *
202     * <div class="note"><b>Example:</b> if an implementation replaces all spaces by underscores,
203     * then a subclass testing that implementation could override this method as below:
204     *
205     * <pre> &#64;Override
206     * protected String getVerifiableName(IdentifiedObject object) {
207     *    return super.getVerifiableName().replace(' ', '_');
208     * }</pre></div>
209     *
210     * Note that if the object names are too different for being compared, then subclasses can also
211     * disable name comparisons by setting {@link #isStandardNameSupported} to {@code false}.
212     *
213     * @param  object  the object from which to get a name than can be verified against the expected name.
214     * @return the name of the given object, eventually modified in order to match the expected name.
215     *
216     * @see #isStandardNameSupported
217     * @see #isStandardAliasSupported
218     */
219    protected String getVerifiableName(final IdentifiedObject object) {
220        return getName(object);
221    }
222
223    /**
224     * Compares the given generic names with the given set of expected aliases.
225     * This method verifies that the given collection contains at least the expected aliases.
226     * However the collection may contain additional aliases, which will be ignored.
227     *
228     * @param message   the prefix of the message to show in case of failure.
229     * @param expected  the expected aliases.
230     * @param aliases   the actual aliases.
231     */
232    static void assertContainsAll(final String message, final String[] expected,
233            final Collection<GenericName> aliases)
234    {
235        assertNotNull(message, aliases);
236next:   for (final String search : expected) {
237            for (final GenericName alias : aliases) {
238                final String tip = alias.tip().toString();
239                if (search.equalsIgnoreCase(tip)) {
240                    continue next;
241                }
242            }
243            fail(message + ": alias not found: " + search);
244        }
245    }
246
247    /**
248     * Ensures that the given collection contains at least one identifier having the given
249     * codespace (ignoring case) and the given code value.
250     *
251     * @param message      the message to show in case of failure.
252     * @param codespace    the code space of identifiers to search.
253     * @param expected     the expected identifier code.
254     * @param identifiers  the actual identifiers.
255     */
256    static void assertContainsCode(final String message, final String codespace, final int expected,
257            final Collection<? extends Identifier> identifiers)
258    {
259        assertNotNull(message, identifiers);
260        int found = 0;
261        for (final Identifier id : identifiers) {
262            if (codespace.equalsIgnoreCase(id.getCodeSpace().trim())) {
263                found++;
264                try {
265                    assertEquals(message, expected, Integer.parseInt(id.getCode()));
266                } catch (NumberFormatException e) {
267                    fail(message + ".getCode(): expected " + expected +
268                            " but got a non-numerical value: " + e);
269                }
270            }
271        }
272        assertEquals(message + ": occurrence of " + codespace + ':' + expected, 1, found);
273    }
274}