001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * Copyright © 2008-2024 Open Geospatial Consortium, Inc. 004 * http://www.geoapi.org 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.opengis.test.util; 019 020import java.util.Map; 021import java.util.Locale; 022import java.util.HashMap; 023import java.util.regex.Pattern; 024 025import org.opengis.util.*; 026import org.opengis.test.TestCase; 027import org.opengis.test.Configuration; 028 029import org.junit.jupiter.api.Test; 030 031import static org.junit.jupiter.api.Assertions.*; 032import static org.junit.jupiter.api.Assumptions.*; 033import static org.opengis.test.Assertions.assertContains; 034 035 036/** 037 * Tests {@linkplain GenericName generic name} and related objects from the {@code org.opengis.util} package. 038 * Name instances are created using a {@link NameFactory} given at construction time. 039 * 040 * <h2>Usage example:</h2> 041 * in order to specify their factories and run the tests in a JUnit framework, implementers can 042 * define a subclass in their own test suite as in the example below: 043 * 044 * {@snippet lang="java" : 045 * import org.opengis.test.util.NameTest; 046 * 047 * public class MyTest extends NameTest { 048 * public MyTest() { 049 * super(new MyNameFactory()); 050 * } 051 * }} 052 * 053 * @author Martin Desruisseaux (Geomatys) 054 * @version 3.1 055 * @since 2.2 056 */ 057@SuppressWarnings("strictfp") // Because we still target Java 11. 058public strictfp class NameTest extends TestCase { 059 /** 060 * The message when a test is disabled because no name factory has been found. 061 */ 062 private static final String NO_FACTORY = "No name factory found."; 063 064 /** 065 * The factory to be used for testing {@linkplain GenericName generic name} instances, 066 * or {@code null} if none. 067 */ 068 protected final NameFactory factory; 069 070 /** 071 * {@code true} if the {@link InternationalString} implementations created by the 072 * {@linkplain #factory} can support more than one {@linkplain Locale locale}. If 073 * {@code false}, then the factory method may retain only one locale among the set 074 * of user-provided localized strings. 075 */ 076 protected boolean isMultiLocaleSupported; 077 078 /** 079 * {@code true} if the {@link GenericName} implementations created by the {@linkplain #factory} 080 * can use different syntax rules in different part of their name. 081 * 082 * <ul> 083 * <li>If {@code true}, then URI using different separators in different parts of their name are supported. 084 * <div class="note"><b>Example:</b> {@code ":"}, {@code "."}, {@code "/"} and {@code "#"} in 085 * {@code "http://www.opengis.net/gml/srs/epsg.xml#4326"}.</div></li> 086 * <li>If {@code false}, then only a single rule can be applied to the name as a whole. 087 * <div class="note"><b>Example:</b> only the {@code ":"} separator is used in 088 * {@code "urn:ogc:def:crs:epsg:4326"}.</div></li> 089 * </ul> 090 */ 091 protected boolean isMixedNameSyntaxSupported; 092 093 /** 094 * Creates a new test using the given factory. If the given factory is {@code null}, 095 * then the tests will be skipped. 096 * 097 * @param factory the factory to be used for creation of instances to be tested. 098 */ 099 @SuppressWarnings("this-escape") 100 public NameTest(final NameFactory factory) { 101 this.factory = factory; 102 final boolean[] isEnabled = getEnabledFlags( 103 Configuration.Key.isMultiLocaleSupported, 104 Configuration.Key.isMixedNameSyntaxSupported); 105 isMultiLocaleSupported = isEnabled[0]; 106 isMixedNameSyntaxSupported = isEnabled[1]; 107 } 108 109 /** 110 * Returns information about the configuration of the test which has been run. 111 * This method returns a map containing: 112 * 113 * <ul> 114 * <li>All the following values associated to the {@link org.opengis.test.Configuration.Key} of the same name: 115 * <ul> 116 * <li>{@link #isMultiLocaleSupported}</li> 117 * <li>{@link #isMixedNameSyntaxSupported}</li> 118 * </ul> 119 * </li> 120 * </ul> 121 * 122 * @return {@inheritDoc} 123 */ 124 @Override 125 public Configuration configuration() { 126 final Configuration op = super.configuration(); 127 assertNull(op.put(Configuration.Key.isMultiLocaleSupported, isMultiLocaleSupported)); 128 assertNull(op.put(Configuration.Key.isMixedNameSyntaxSupported, isMixedNameSyntaxSupported)); 129 return op; 130 } 131 132 /** 133 * Creates a namespace having the given name and separator. 134 * 135 * @param name 136 * the name of the namespace to be returned. This argument can be created using 137 * <code>{@linkplain #createGenericName createGenericName}(null, parsedNames)</code>. 138 * @param headSeparator 139 * the separator to insert between the namespace and the {@linkplain AbstractName#head head}. 140 * For HTTP namespace, it is {@code "://"}. For URN namespace, it is typically {@code ":"}. 141 * @param separator 142 * the separator to insert between {@linkplain AbstractName#getParsedNames parsed names} in that namespace. 143 * For HTTP namespace, it is {@code "."}. For URN namespace, it is typically {@code ":"}. 144 * @return a namespace having the given name and separator. 145 */ 146 private NameSpace createNameSpace(final GenericName name, 147 final String headSeparator, final String separator) 148 { 149 final Map<String,String> properties = new HashMap<>(4); 150 properties.put("separator", separator); 151 properties.put("separator.head", headSeparator); 152 return factory.createNameSpace(name, properties); 153 } 154 155 /** 156 * Tests the creation of "My documents" folder name. 157 * This test uses the following factory methods: 158 * 159 * <ul> 160 * <li>{@link NameFactory#createInternationalString(Map)}</li> 161 * </ul> 162 */ 163 @Test 164 public void testInternationalString() { 165 assumeTrue(factory != null, NO_FACTORY); 166 final Map<Locale,String> names = new HashMap<>(); 167 names.put(Locale.ENGLISH, "My documents"); 168 names.put(Locale.FRENCH, "Mes documents"); 169 InternationalString localized = factory.createInternationalString(names); 170 validators.validate(localized); 171 if (isMultiLocaleSupported) { 172 configurationTip = Configuration.Key.isMultiLocaleSupported; 173 for (final Map.Entry<Locale,String> entry : names.entrySet()) { 174 assertEquals(entry.getValue(), localized.toString(entry.getKey()), 175 "toString(Locale) should returns the value given to the factory method."); 176 } 177 configurationTip = null; 178 } 179 assertContains(names.values(), localized.toString(), 180 "toString() should returns one of the values given to the factory method."); 181 } 182 183 /** 184 * Tests the creation of {@code "EPSG:4326"} as local names. First the {@code "EPSG"} 185 * namespace is created. Then a {@code "4326"} local name is created in that namespace. 186 * This test uses the following factory methods: 187 * 188 * <ul> 189 * <li>{@link NameFactory#createLocalName(NameSpace, CharSequence)}</li> 190 * <li>{@link NameFactory#createNameSpace(GenericName, Map)}</li> 191 * </ul> 192 */ 193 @Test 194 public void testLocalName() { 195 assumeTrue(factory != null, NO_FACTORY); 196 final String EPSG = "EPSG"; 197 final LocalName authority = factory.createLocalName(null, EPSG); 198 validators.validate(authority); 199 assertTrue(authority.scope().isGlobal()); 200 assertEquals(EPSG, authority.toString()); 201 assertEquals(EPSG, authority.toInternationalString().toString()); 202 203 final NameSpace ns = createNameSpace(authority, ":", ":"); 204 validators.validate(ns); 205 assertEquals(authority, ns.name()); 206 207 final String WGS84 = "4326"; 208 final LocalName code = factory.createLocalName(ns, WGS84); 209 validators.validate(code); 210 assertEquals(ns, code.scope()); 211 assertEquals(WGS84, code.toString()); 212 assertEquals(EPSG + ':' + WGS84, code.toFullyQualifiedName().toString()); 213 } 214 215 /** 216 * Tests the creation of {@code "urn:ogc:def:crs:epsg:4326"} as a scoped name. 217 * This test uses the following factory methods: 218 * 219 * <ul> 220 * <li>{@link NameFactory#createGenericName(NameSpace, CharSequence[])}</li> 221 * </ul> 222 */ 223 @Test 224 public void testScopedName() { 225 assumeTrue(factory != null, NO_FACTORY); 226 final String[] parsed = new String[] { 227 "urn","ogc","def","crs","epsg","4326" 228 }; 229 GenericName name = factory.createGenericName(null, parsed); 230 validators.validate(name); 231 232 assertEquals(name, name.toFullyQualifiedName(), 233 "Name should be already fully qualified."); 234 235 assertTrue(Pattern.matches("urn\\p{Punct}ogc\\p{Punct}def\\p{Punct}crs\\p{Punct}epsg\\p{Punct}4326", 236 name.toString()), 237 "Fully qualified name should be \"urn:ogc:def:crs:epsg:4326\" (separator may vary)."); 238 239 assertEquals(6, name.depth(), "Depth shall be counted from the global namespace."); 240 241 for (int i=parsed.length; --i>=0;) { 242 name = name.tip(); 243 validators.validate(name); 244 assertEquals(parsed[i], name.toString()); 245 name = name.scope().name(); 246 } 247 } 248 249 /** 250 * Tests the parsing 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#createLocalName(NameSpace, CharSequence)}</li> 255 * <li>{@link NameFactory#createNameSpace(GenericName, Map)}</li> 256 * <li>{@link NameFactory#parseGenericName(NameSpace, CharSequence)}</li> 257 * </ul> 258 */ 259 @Test 260 public void testParsedURN() { 261 assumeTrue(factory != null, NO_FACTORY); 262 final LocalName urn = factory.createLocalName(null, "urn"); 263 validators.validate(urn); 264 final NameSpace ns = createNameSpace(urn, ":", ":"); 265 validators.validate(ns); 266 final GenericName name = factory.parseGenericName(ns, "ogc:def:crs:epsg:4326"); 267 validators.validate(name); 268 269 assertEquals(5, name.depth(), "Depth shall be counted from the \"urn\" namespace."); 270 assertEquals("ogc:def:crs:epsg:4326", name.toString()); 271 assertEquals("urn:ogc:def:crs:epsg:4326", name.toFullyQualifiedName().toString()); 272 } 273 274 /** 275 * Tests the parsing of {@code "http://www.opengis.net/gml/srs/epsg.xml#4326"} as a local name. 276 * This test uses the following factory methods: 277 * 278 * <ul> 279 * <li>{@link NameFactory#createLocalName(NameSpace, CharSequence)}</li> 280 * <li>{@link NameFactory#createNameSpace(GenericName, Map)}</li> 281 * <li>{@link NameFactory#parseGenericName(NameSpace, CharSequence)}</li> 282 * </ul> 283 * 284 * This tests is executed only if {@link #isMixedNameSyntaxSupported} is {@code true}. 285 */ 286 @Test 287 public void testParsedHTTP() { 288 assumeTrue(factory != null, NO_FACTORY); 289 assumeTrue(isMixedNameSyntaxSupported, "URL in generic name is not supported by the tested implementation."); 290 configurationTip = Configuration.Key.isMixedNameSyntaxSupported; 291 GenericName name = factory.createLocalName(null, "http"); 292 assertEquals(1, name.depth()); 293 assertEquals("http", name.head().toString()); 294 assertEquals("http", name.tip().toString()); 295 assertEquals("http", name.toString()); 296 NameSpace ns = createNameSpace(name, "://", "."); 297 validators.validate(ns); 298 299 name = factory.parseGenericName(ns, "www.opengis.net"); 300 assertEquals(3, name.depth()); 301 assertEquals("www", name.head().toString()); 302 assertEquals("net", name.tip().toString()); 303 assertEquals("www.opengis.net", name.toString()); 304 ns = createNameSpace(name, "/", "/"); 305 validators.validate(ns); 306 307 name = factory.parseGenericName(ns, "gml/srs/epsg.xml"); 308 assertEquals(3, name.depth()); 309 assertEquals("gml", name.head().toString()); 310 assertEquals("epsg.xml", name.tip().toString()); 311 assertEquals("gml/srs/epsg.xml", name.toString()); 312 ns = createNameSpace(name, "#", ":"); 313 validators.validate(ns); 314 315 name = factory.createLocalName(ns, "4326"); 316 assertEquals(1, name.depth()); 317 assertEquals("4326", name.head().toString()); 318 assertEquals("4326", name.tip().toString()); 319 assertEquals("4326", name.toString()); 320 validators.validate(name); 321 322 assertEquals("4326", name.toString()); 323 name = name.toFullyQualifiedName(); 324 assertEquals("http://www.opengis.net/gml/srs/epsg.xml#4326", name.toString()); 325 assertEquals(8, name.depth()); 326 assertEquals("http", name.head().toString()); 327 assertEquals("4326", name.tip().toString()); 328 } 329}