001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    http://www.geoapi.org
004 *
005 *    Copyright (C) 2008-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.util;
033
034import java.util.Map;
035import java.util.List;
036import java.util.Locale;
037import java.util.HashMap;
038import java.util.regex.Pattern;
039
040import org.opengis.util.*;
041import org.opengis.test.TestCase;
042import org.opengis.test.Configuration;
043
044import org.junit.Test;
045import org.junit.runner.RunWith;
046import org.junit.runners.Parameterized;
047
048import static org.junit.Assume.*;
049import static org.opengis.test.Assert.*;
050
051
052/**
053 * Tests {@linkplain GenericName generic name} and related objects from the {@code org.opengis.util}
054 * package. Name instances are created using a {@link NameFactory} given at construction time.
055 *
056 * <div class="note"><b>Usage example:</b>
057 * in order to specify their factories and run the tests in a JUnit framework, implementors can
058 * define a subclass in their own test suite as in the example below:
059 *
060 * <blockquote><pre>import org.junit.runner.RunWith;
061 *import org.junit.runners.JUnit4;
062 *import org.opengis.test.util.NameTest;
063 *
064 *&#64;RunWith(JUnit4.class)
065 *public class MyTest extends NameTest {
066 *    public MyTest() {
067 *        super(new MyNameFactory());
068 *    }
069 *}</pre></blockquote>
070 * </div>
071 *
072 * @see org.opengis.test.TestSuite
073 *
074 * @author  Martin Desruisseaux (Geomatys)
075 * @version 3.1
076 * @since   2.2
077 */
078@RunWith(Parameterized.class)
079public strictfp class NameTest extends TestCase {
080    /**
081     * The factory to be used for testing {@linkplain GenericName generic name} instances,
082     * or {@code null} if none.
083     */
084    protected final NameFactory factory;
085
086    /**
087     * {@code true} if the {@link InternationalString} implementations created by the
088     * {@linkplain #factory} can support more than one {@linkplain Locale locale}. If
089     * {@code false}, then the factory method may retain only one locale among the set
090     * of user-provided localized strings.
091     */
092    protected boolean isMultiLocaleSupported;
093
094    /**
095     * {@code true} if the {@link GenericName} implementations created by the {@linkplain #factory}
096     * can use different syntax rules in different part of their name.
097     *
098     * <ul>
099     *   <li>If {@code true}, then URI using different separators in different parts of their name are supported.
100     *       <div class="note"><b>Example:</b> {@code ":"}, {@code "."}, {@code "/"} and {@code "#"} in
101     *       {@code "http://www.opengis.net/gml/srs/epsg.xml#4326"}.</div></li>
102     *   <li>If {@code false}, then only a single rule can be applied to the name as a whole.
103     *       <div class="note"><b>Example:</b> only the {@code ":"} separator is used in
104     *       {@code "urn:ogc:def:crs:epsg:4326"}.</div></li>
105     * </ul>
106     */
107    protected boolean isMixedNameSyntaxSupported;
108
109    /**
110     * Returns a default set of factories to use for running the tests. Those factories are given
111     * in arguments to the constructor when this test class is instantiated directly by JUnit (for
112     * example as a {@linkplain org.junit.runners.Suite.SuiteClasses suite} element), instead than
113     * subclassed by the implementor. The factories are fetched as documented in the
114     * {@link #factories(Class[])} javadoc.
115     *
116     * @return the default set of arguments to be given to the {@code NameTest} constructor.
117     *
118     * @since 3.1
119     */
120    @Parameterized.Parameters
121    @SuppressWarnings("unchecked")
122    public static List<Factory[]> factories() {
123        return factories(NameFactory.class);
124    }
125
126    /**
127     * Creates a new test using the given factory. If the given factory is {@code null},
128     * then the tests will be skipped.
129     *
130     * @param factory  the factory to be used for creation of instances to be tested.
131     */
132    public NameTest(final NameFactory factory) {
133        super(factory);
134        this.factory = factory;
135        @SuppressWarnings("unchecked")
136        final boolean[] isEnabled = getEnabledFlags(
137                Configuration.Key.isMultiLocaleSupported,
138                Configuration.Key.isMixedNameSyntaxSupported);
139        isMultiLocaleSupported     = isEnabled[0];
140        isMixedNameSyntaxSupported = isEnabled[1];
141    }
142
143    /**
144     * Returns information about the configuration of the test which has been run.
145     * This method returns a map containing:
146     *
147     * <ul>
148     *   <li>All the following values associated to the {@link org.opengis.test.Configuration.Key} of the same name:
149     *     <ul>
150     *       <li>{@link #isMultiLocaleSupported}</li>
151     *       <li>{@link #isMixedNameSyntaxSupported}</li>
152     *     </ul>
153     *   </li>
154     * </ul>
155     *
156     * @return {@inheritDoc}
157     */
158    @Override
159    public Configuration configuration() {
160        final Configuration op = super.configuration();
161        assertNull(op.put(Configuration.Key.isMultiLocaleSupported,     isMultiLocaleSupported));
162        assertNull(op.put(Configuration.Key.isMixedNameSyntaxSupported, isMixedNameSyntaxSupported));
163        return op;
164    }
165
166    /**
167     * Creates a namespace having the given name and separator.
168     *
169     * @param name
170     *          the name of the namespace to be returned. This argument can be created using
171     *          <code>{@linkplain #createGenericName createGenericName}(null, parsedNames)</code>.
172     * @param headSeparator
173     *          the separator to insert between the namespace and the {@linkplain AbstractName#head head}.
174     *          For HTTP namespace, it is {@code "://"}. For URN namespace, it is typically {@code ":"}.
175     * @param separator
176     *          the separator to insert between {@linkplain AbstractName#getParsedNames parsed names} in that namespace.
177     *          For HTTP namespace, it is {@code "."}. For URN namespace, it is typically {@code ":"}.
178     * @return a namespace having the given name and separator.
179     */
180    private NameSpace createNameSpace(final GenericName name,
181            final String headSeparator, final String separator)
182    {
183        final Map<String,String> properties = new HashMap<>(4);
184        properties.put("separator", separator);
185        properties.put("separator.head", headSeparator);
186        return factory.createNameSpace(name, properties);
187    }
188
189    /**
190     * Tests the creation of "My documents" folder name.
191     * This test uses the following factory methods:
192     *
193     * <ul>
194     *   <li>{@link NameFactory#createInternationalString(Map)}</li>
195     * </ul>
196     */
197    @Test
198    public void testInternationalString() {
199        assumeNotNull(factory);
200        final Map<Locale,String> names = new HashMap<>();
201        names.put(Locale.ENGLISH, "My documents");
202        names.put(Locale.FRENCH,  "Mes documents");
203        InternationalString localized = factory.createInternationalString(names);
204        validators.validate(localized);
205        if (isMultiLocaleSupported) {
206            configurationTip = Configuration.Key.isMultiLocaleSupported;
207            for (final Map.Entry<Locale,String> entry : names.entrySet()) {
208                assertEquals("toString(Locale) should returns the value given to the factory method.",
209                        entry.getValue(), localized.toString(entry.getKey()));
210            }
211            configurationTip = null;
212        }
213        assertContains("toString() should returns one of the values given to the factory method.",
214                names.values(), localized.toString());
215    }
216
217    /**
218     * Tests the creation of {@code "EPSG:4326"} as local names. First the {@code "EPSG"}
219     * namespace is created. Then a {@code "4326"} local name is created in that namespace.
220     * This test uses the following factory methods:
221     *
222     * <ul>
223     *   <li>{@link NameFactory#createLocalName(NameSpace, CharSequence)}</li>
224     *   <li>{@link NameFactory#createNameSpace(GenericName, Map)}</li>
225     * </ul>
226     */
227    @Test
228    public void testLocalName() {
229        assumeNotNull(factory);
230        final String EPSG = "EPSG";
231        final LocalName authority = factory.createLocalName(null, EPSG);
232        validators.validate(authority);
233        assertTrue(authority.scope().isGlobal());
234        assertEquals(EPSG, authority.toString());
235        assertEquals(EPSG, authority.toInternationalString().toString());
236
237        final NameSpace ns = createNameSpace(authority, ":", ":");
238        validators.validate(ns);
239        assertEquals(authority, ns.name());
240
241        final String WGS84 = "4326";
242        final LocalName code = factory.createLocalName(ns, WGS84);
243        validators.validate(code);
244        assertEquals(ns, code.scope());
245        assertEquals(WGS84, code.toString());
246        assertEquals(EPSG + ':' + WGS84, code.toFullyQualifiedName().toString());
247    }
248
249    /**
250     * Tests the creation of {@code "urn:ogc:def:crs:epsg:4326"} as a scoped name.
251     * This test uses the following factory methods:
252     *
253     * <ul>
254     *   <li>{@link NameFactory#createGenericName(NameSpace, CharSequence[])}</li>
255     * </ul>
256     */
257    @Test
258    public void testScopedName() {
259        assumeNotNull(factory);
260        final String[] parsed = new String[] {
261            "urn","ogc","def","crs","epsg","4326"
262        };
263        GenericName name = factory.createGenericName(null, parsed);
264        validators.validate(name);
265
266        assertEquals("Name should be already fully qualified.",
267                name, name.toFullyQualifiedName());
268
269        assertTrue("Fully qualified name should be \"urn:ogc:def:crs:epsg:4326\" (separator may vary).",
270                Pattern.matches("urn\\p{Punct}ogc\\p{Punct}def\\p{Punct}crs\\p{Punct}epsg\\p{Punct}4326",
271                name.toString()));
272
273        assertEquals("Depth shall be counted from the global namespace.", 6, name.depth());
274
275        for (int i=parsed.length; --i>=0;) {
276            name = name.tip();
277            validators.validate(name);
278            assertEquals(parsed[i], name.toString());
279            name = name.scope().name();
280        }
281    }
282
283    /**
284     * Tests the parsing of {@code "urn:ogc:def:crs:epsg:4326"} as a scoped name.
285     * This test uses the following factory methods:
286     *
287     * <ul>
288     *   <li>{@link NameFactory#createLocalName(NameSpace, CharSequence)}</li>
289     *   <li>{@link NameFactory#createNameSpace(GenericName, Map)}</li>
290     *   <li>{@link NameFactory#parseGenericName(NameSpace, CharSequence)}</li>
291     * </ul>
292     */
293    @Test
294    public void testParsedURN() {
295        assumeNotNull(factory);
296        final LocalName urn = factory.createLocalName(null, "urn");
297        validators.validate(urn);
298        final NameSpace ns = createNameSpace(urn, ":", ":");
299        validators.validate(ns);
300        final GenericName name = factory.parseGenericName(ns, "ogc:def:crs:epsg:4326");
301        validators.validate(name);
302
303        assertEquals("Depth shall be counted from the \"urn\" namespace.", 5, name.depth());
304        assertEquals("ogc:def:crs:epsg:4326", name.toString());
305        assertEquals("urn:ogc:def:crs:epsg:4326", name.toFullyQualifiedName().toString());
306    }
307
308    /**
309     * Tests the parsing of {@code "http://www.opengis.net/gml/srs/epsg.xml#4326"} as a local name.
310     * This test uses the following factory methods:
311     *
312     * <ul>
313     *   <li>{@link NameFactory#createLocalName(NameSpace, CharSequence)}</li>
314     *   <li>{@link NameFactory#createNameSpace(GenericName, Map)}</li>
315     *   <li>{@link NameFactory#parseGenericName(NameSpace, CharSequence)}</li>
316     * </ul>
317     *
318     * This tests is executed only if {@link #isMixedNameSyntaxSupported} is {@code true}.
319     */
320    @Test
321    public void testParsedHTTP() {
322        assumeNotNull(factory);
323        assumeTrue(isMixedNameSyntaxSupported);
324        configurationTip = Configuration.Key.isMixedNameSyntaxSupported;
325        GenericName name = factory.createLocalName(null, "http");
326        assertEquals(1, name.depth());
327        assertEquals("http", name.head().toString());
328        assertEquals("http", name.tip().toString());
329        assertEquals("http", name.toString());
330        NameSpace ns = createNameSpace(name, "://", ".");
331        validators.validate(ns);
332
333        name = factory.parseGenericName(ns, "www.opengis.net");
334        assertEquals(3, name.depth());
335        assertEquals("www", name.head().toString());
336        assertEquals("net", name.tip().toString());
337        assertEquals("www.opengis.net", name.toString());
338        ns = createNameSpace(name, "/", "/");
339        validators.validate(ns);
340
341        name = factory.parseGenericName(ns, "gml/srs/epsg.xml");
342        assertEquals(3, name.depth());
343        assertEquals("gml", name.head().toString());
344        assertEquals("epsg.xml", name.tip().toString());
345        assertEquals("gml/srs/epsg.xml", name.toString());
346        ns = createNameSpace(name, "#", ":");
347        validators.validate(ns);
348
349        name = factory.createLocalName(ns, "4326");
350        assertEquals(1, name.depth());
351        assertEquals("4326", name.head().toString());
352        assertEquals("4326", name.tip().toString());
353        assertEquals("4326", name.toString());
354        validators.validate(name);
355
356        assertEquals("4326", name.toString());
357        name = name.toFullyQualifiedName();
358        assertEquals("http://www.opengis.net/gml/srs/epsg.xml#4326", name.toString());
359        assertEquals(8, name.depth());
360        assertEquals("http", name.head().toString());
361        assertEquals("4326", name.tip().toString());
362    }
363}