001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * Copyright © 2004-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.util; 019 020import java.util.List; 021import org.opengis.annotation.UML; 022import org.opengis.annotation.Classifier; 023import org.opengis.annotation.Stereotype; 024 025import static org.opengis.annotation.Obligation.*; 026import static org.opengis.annotation.Specification.*; 027 028 029/** 030 * A sequence of identifiers rooted within the context of a {@linkplain NameSpace namespace}. 031 * The job of a "name" is to associate that name with an {@link java.lang.Object}. 032 * For example, {@code GenericName} instances could be keys in a {@link java.util.HashMap}, 033 * in which case the namespace is materialized by the {@code HashMap}. 034 * Names are often used in the context of reading data from various formats such as XML, shapefiles or netCDF, 035 * which have different constraints for names in their namespaces. When reading data from a file, 036 * names are often used for identifying attributes in records. In such case, specialized types are used: 037 * 038 * <ul> 039 * <li>{@link TypeName} is the name of a {@link RecordType}.</li> 040 * <li>{@link MemberName} is the name of an attribute in a {@link Record} or {@link RecordType}.</li> 041 * </ul> 042 * 043 * Names can be {@linkplain #toFullyQualifiedName() fully qualified} (e.g. {@code "urn:ogc:def:crs:EPSG::4326"}) or 044 * relative to a {@linkplain #scope() scope} (e.g. {@code "EPSG::4326"} in the {@code "urn:ogc:def:crs"} namespace). 045 * All names have the ability to provide a {@linkplain #getParsedNames() parsed} version of themselves. 046 * In the following illustration, each line is one possible construction for {@code "urn:crs:epsg:4326"} 047 * (taken as an abridged form of above URN for this example only). For each construction: 048 * 049 * <ul> 050 * <li>the part without colored background is the {@link #scope()} and is invisible to all other methods 051 * except {@code toFullyQualifiedName()};</li> 052 * <li>the first column shows the visible part of the name in a green background;</li> 053 * <li>the second and third columns show the 054 * ({@linkplain #head() head}:{@linkplain ScopedName#tail() tail}) and 055 * ({@linkplain ScopedName#path() path}:{@linkplain #tip() tip}) components, respectively.</li> 056 * </ul> 057 * 058 * <blockquote> 059 * <table class="ogc" style="margin-top:21px; margin-bottom:45px; border-spacing:40px 0"> 060 * <caption>Various representations of a generic name</caption> 061 * <tr> 062 * <th style="background-color:inherit">scope:<span style="background:LawnGreen">name</span></th> 063 * <th style="background-color:inherit"><span style="background:LightSkyBlue">head</span>:<span style="background:Yellow">tail</span></th> 064 * <th style="background-color:inherit"><span style="background:LightSkyBlue">path</span>:<span style="background:Yellow">tip</span></th> 065 * <th style="background-color:inherit">Type</th> 066 * </tr><tr> 067 * <td><code><span style="background:LawnGreen">urn:crs:epsg:4326</span></code></td> 068 * <td><code><span style="background:LightSkyBlue">urn:</span><span style="background:Yellow">crs:epsg:4326</span></code></td> 069 * <td><code><span style="background:LightSkyBlue">urn:crs:epsg:</span><span style="background:Yellow">4326</span></code></td> 070 * <td>{@link org.opengis.util.ScopedName} with {@linkplain org.opengis.util.NameSpace#isGlobal() global namespace}</td> 071 * </tr><tr> 072 * <td><code>urn:<span style="background:LawnGreen">crs:epsg:4326</span></code></td> 073 * <td><code>urn:<span style="background:LightSkyBlue">crs:</span><span style="background:Yellow">epsg:4326</span></code></td> 074 * <td><code>urn:<span style="background:LightSkyBlue">crs:epsg:</span><span style="background:Yellow">4326</span></code></td> 075 * <td>{@link org.opengis.util.ScopedName}</td> 076 * </tr><tr> 077 * <td><code>urn:crs:<span style="background:LawnGreen">epsg:4326</span></code></td> 078 * <td><code>urn:crs:<span style="background:LightSkyBlue">epsg:</span><span style="background:Yellow">4326</span></code></td> 079 * <td><code>urn:crs:<span style="background:LightSkyBlue">epsg:</span><span style="background:Yellow">4326</span></code></td> 080 * <td>{@link org.opengis.util.ScopedName}</td> 081 * </tr><tr> 082 * <td><code>urn:crs:epsg:<span style="background:LawnGreen">4326</span></code></td> 083 * <td><code>urn:crs:epsg:<span style="background:LightSkyBlue">4326</span></code></td> 084 * <td><code>urn:crs:epsg:<span style="background:Yellow">4326</span></code></td> 085 * <td>{@link org.opengis.util.LocalName}</td> 086 * </tr> 087 * </table> 088 * </blockquote> 089 * 090 * <h2>Comparison with files in a filesystem</h2> 091 * This {@code GenericName} interface is similar to a file path in a file system relative to a default directory. 092 * It can be compared to the standard {@link java.nio.file.Path} interface in the JDK: 093 * 094 * <blockquote><table class="ogc" style="white-space: nowrap"> 095 * <caption>Equivalence between {@code GenericName} and {@code java.nio.file.Path}</caption> 096 * <tr> 097 * <th>GeoAPI {@code Name} method</th> 098 * <th>Equivalent Java I/O method</th> 099 * </tr><tr> 100 * <td>{@link #scope()}</td> 101 * <td>Default directory</td> 102 * </tr><tr> 103 * <td>{@link ScopedName#path()}</td> 104 * <td>{@link java.nio.file.Path#getParent() Path.getParent()}</td> 105 * </tr><tr> 106 * <td>{@link #tip()}</td> 107 * <td>{@link java.nio.file.Path#getFileName() Path.getFileName()}</td> 108 * </tr><tr> 109 * <td>{@link #toFullyQualifiedName()}</td> 110 * <td>{@link java.nio.file.Path#toAbsolutePath() Path.toAbsolutePath()}</td> 111 * </tr><tr> 112 * <td>{@link #depth()}</td> 113 * <td>{@link java.nio.file.Path#getNameCount() Path.getNameCount()}</td> 114 * </tr><tr> 115 * <td>{@link #getParsedNames()}</td> 116 * <td>{@link java.nio.file.Path#iterator() Path.iterator()}</td> 117 * </tr><tr> 118 * <td>{@link #compareTo(GenericName)}</td> 119 * <td>{@link java.nio.file.Path#compareTo Path.compareTo(Path)}</td> 120 * </tr><tr> 121 * <td>{@link #toString()}</td> 122 * <td>{@link java.nio.file.Path#toString() Path.toString()}</td> 123 * </tr> 124 * </table></blockquote> 125 * 126 * <h2>Comparison with Java Content Repository (JCR) names</h2> 127 * In the Java standard {@link javax.xml.namespace.QName} class and in the Java Content Repository (JCR) specification, 128 * a name is an ordered pair of (<var>Name space</var>, <var>Local part</var>) strings. A JCR name can take two lexical 129 * forms: <dfn>expanded form</dfn> and <dfn>qualified form</dfn>. Those names are defined as: 130 * 131 * <blockquote><table class="ogc" style="white-space: nowrap"> 132 * <caption>Equivalence between JCR name and {@code GenericName}</caption> 133 * <tr> 134 * <th>JCR name definition</th> 135 * <th class="sep" colspan="2">GeoAPI equivalence</th> 136 * </tr><tr> 137 * <td><code>ExpandedName ::= '{' Namespace '}' LocalPart</code></td> 138 * <td class="sep"><code>GenericName.{@linkplain #scope() scope()}.name().toString()</code></td> 139 * <td>= JCR {@code Namespace}</td> 140 * </tr><tr> 141 * <td></td> 142 * <td class="sep"><code>GenericName.{@linkplain #toString() toString()}</code></td> 143 * <td>= JCR {@code LocalPart}</td> 144 * </tr><tr> 145 * <td class="hsep"><code>QualifiedName ::= [Prefix ':'] LocalPart</code></td> 146 * <td class="hsep sep"><code>ScopedName.{@linkplain #scope() scope()}</code></td> 147 * <td class="hsep">= {@linkplain NameSpace#isGlobal() global namespace}</td> 148 * </tr><tr> 149 * <td></td> 150 * <td class="sep"><code>ScopedName.{@linkplain ScopedName#head() head()}.toString()</code></td> 151 * <td>= JCR {@code Prefix}</td> 152 * </tr><tr> 153 * <td></td> 154 * <td class="sep"><code>ScopedName.{@linkplain ScopedName#tail() tail()}.toString()</code></td> 155 * <td>= JCR {@code LocalPart}</td> 156 * </tr> 157 * </table></blockquote> 158 * 159 * @author Martin Desruisseaux (IRD) 160 * @author Bryce Nordgren (USDA) 161 * @version 3.0 162 * @since 1.0 163 * 164 * @see javax.naming.Name 165 * @see NameFactory#createGenericName(NameSpace, CharSequence[]) 166 * @see NameFactory#parseGenericName(NameSpace, CharSequence) 167 */ 168@Classifier(Stereotype.ABSTRACT) // This is said in the text (not the UML) of ISO 19103. 169@UML(identifier="GenericName", specification=ISO_19103) 170public interface GenericName extends Comparable<GenericName> { 171 /** 172 * Returns the scope (name space) in which this name is local. 173 * All names carry an association with their scope in which they are considered local, 174 * but the scope can be the {@linkplain NameSpace#isGlobal() global namespace}. 175 * The scope of a name determines where a name starts. 176 * The scope is set on creation and is not modifiable. 177 * 178 * <p>In the {@linkplain GenericName overview illustration}, 179 * the scopes are the blue elements in the <var>scope</var>.<var>this</var> column.</p> 180 * 181 * <div class="note"><b>Example:</b> 182 * for a {@linkplain #toFullyQualifiedName() fully qualified name} {@code "org.opengis.util.Record"}, 183 * if this instance is the {@code "util.Record"} name, then the scope of this instance 184 * has the {@code "org.opengis"} {@linkplain NameSpace#name() name}. 185 * </div> 186 * 187 * <div class="note"><b>Analogy:</b> 188 * this method is similar in purpose to the current directory of a file system. 189 * </div> 190 * 191 * @return the scope of this name. 192 */ 193 @UML(identifier="scope", obligation=MANDATORY, specification=ISO_19103) 194 NameSpace scope(); 195 196 /** 197 * Indicates the number of levels specified by this name. The depth is the {@linkplain List#size() size} 198 * of the list returned by the {@link #getParsedNames()} method. As such it is a derived parameter. For 199 * any {@link LocalName}, it is always one. For a {@link ScopedName} it is some number greater than or 200 * equal to 2. 201 * 202 * <div class="note"><b>Example:</b> 203 * if {@code this} name is {@code "urn:ogc:def:crs:EPSG:8.2:4326"} with {@code ':'} as separator, 204 * then this method shall return {@code 7}. If this name is {@code "EPSG:8.2:4326"} in the 205 * {@code "urn:ogc:def:crs"} scope, then this method shall return {@code 3}. 206 * </div> 207 * 208 * <div class="note"><b>Analogy:</b> 209 * this method is similar in purpose to: 210 * <ul> 211 * <li>the {@link java.nio.file.Path#getNameCount() Path.getNameCount()} method in Java I/O;</li> 212 * <li>the {@link javax.naming.Name#size() Name.size()} method from the <cite>Java Naming and Directory Interface</cite>.</li> 213 * </ul></div> 214 * 215 * @return the depth of this name. 216 */ 217 @UML(identifier="depth", obligation=MANDATORY, specification=ISO_19103) 218 int depth(); 219 220 /** 221 * Returns the sequence of {@linkplain LocalName local names} making this generic name. 222 * The length of this sequence is the {@linkplain #depth() depth}. It does not include 223 * the {@linkplain #scope() scope}. 224 * 225 * <p>In the {@linkplain GenericName overview illustration}, 226 * the parsed names are the list of elements in yellow part of the <var>scope</var>.<var>this</var> column.</p> 227 * 228 * <div class="note"><b>Example:</b> 229 * If {@code this} name is {@code "urn:ogc:def:crs:EPSG::4326"}, then this method shall returns a list 230 * containing {@code {"urn", "ogc", "def", "crs", "EPSG", "", "4326}} elements in that iteration order. 231 * If this name is {@code "EPSG::4326"} in scope {@code "urn:ogc:def:crs"}, then this method shall 232 * returns a list containing only {@code {"EPSG", "", "4326"}} elements. 233 * </div> 234 * 235 * <div class="note"><b>Analogy:</b> 236 * this method is similar in purpose to: 237 * <ul> 238 * <li>the {@link java.nio.file.Path#iterator() Path.iterator()} method in Java I/O;</li> 239 * <li>the {@link javax.naming.Name#getAll() Name.getAll()} method from the <cite>Java Naming and Directory Interface</cite>.</li> 240 * </ul></div> 241 * 242 * @return the local names making this generic name, without the {@linkplain #scope() scope}. 243 * Shall never be {@code null} neither empty. 244 */ 245 @UML(identifier="parsedName", obligation=MANDATORY, specification=ISO_19103) 246 List<? extends LocalName> getParsedNames(); 247 248 /** 249 * Returns the first element in the sequence of {@linkplain #getParsedNames() parsed names}. 250 * For any {@link LocalName}, this is always {@code this}. 251 * 252 * <p>In the {@linkplain GenericName overview illustration}, 253 * the heads are the blue elements in the <var>head</var>.<var>tail</var> column.</p> 254 * 255 * <div class="note"><b>Example:</b> 256 * if {@code this} name is {@code "urn:ogc:def:crs:EPSG::4326"}, then this method shall returns {@code "urn"}. 257 * </div> 258 * 259 * <div class="note"><b>Analogy:</b> 260 * this method is similar in purpose to: 261 * <ul> 262 * <li><code>{@linkplain java.nio.file.Path#getName(int) Path.getName}(0)</code> from Java I/O;</li> 263 * <li><code>{@linkplain javax.naming.Name#get(int) Name.get}(0)</code> 264 * from the <cite>Java Naming and Directory Interface</cite>.</li> 265 * </ul></div> 266 * 267 * @return the first element in the list of {@linkplain #getParsedNames() parsed names}. 268 * 269 * @departure generalization 270 * ISO defines this method in <code>ScopedName</code> only. GeoAPI defines it in the base 271 * class since <code>LocalName</code> can return a sensible value for it. This reduces the 272 * need for casts. 273 */ 274 @UML(identifier="ScopedName.head", obligation=MANDATORY, specification=ISO_19103) 275 LocalName head(); 276 277 /** 278 * Returns the last element in the sequence of {@linkplain #getParsedNames() parsed names}. 279 * For any {@link LocalName}, this is always {@code this}. 280 * 281 * <p>In the {@linkplain GenericName overview illustration}, 282 * the tips are the yellow elements in the <var>head</var>.<var>tail</var> column.</p> 283 * 284 * <div class="note"><b>Example:</b> 285 * if {@code this} name is {@code "urn:ogc:def:crs:EPSG::4326"} (no matter its 286 * {@linkplain #scope scope}), then this method shall returns {@code "4326"}. 287 * </div> 288 * 289 * <div class="note"><b>Analogy:</b> 290 * this method is similar in purpose to: 291 * <ul> 292 * <li>the {@link java.io.File#getName() File.getName()} or 293 * {@link java.nio.file.Path#getFileName() Path.getFileName()} method in Java I/O;</li> 294 * <li><code>{@linkplain javax.naming.Name#get(int) Name.get}(size-1)</code> 295 * from the <cite>Java Naming and Directory Interface</cite>.</li> 296 * </ul></div> 297 * 298 * @return the last element in the list of {@linkplain #getParsedNames() parsed names}. 299 * 300 * @departure easeOfUse 301 * This method is not part of ISO specification. It does not provide any additional 302 * information compared to that accessible though the standard methods defined by 303 * ISO, but provides easier to access frequently requested information. 304 */ 305 LocalName tip(); 306 307 /** 308 * Returns a view of this name as a fully-qualified name. The {@linkplain #scope() scope} 309 * of a fully qualified name must be {@linkplain NameSpace#isGlobal() global}. If the scope 310 * of this name is already global, then this method shall returns {@code this}. 311 * 312 * <div class="note"><b>Example:</b> 313 * if {@code this} name is {@code "EPSG::4326"} ({@linkplain #depth() depth} of 3) and its 314 * {@linkplain #scope() scope} is {@code "urn:ogc:def:crs"}, then the fully qualified name 315 * is {@code "urn:ogc:def:crs:EPSG::4326"}. 316 * </div> 317 * 318 * <div class="note"><b>Analogy:</b> 319 * this method is similar in purpose to the {@link java.io.File#getAbsoluteFile() File.getAbsoluteFile()} 320 * or {@link java.nio.file.Path#toAbsolutePath() Path.toAbsolutePath()} methods in Java I/O. 321 * </div> 322 * 323 * @return the fully-qualified name (never {@code null}). 324 * 325 * @departure easeOfUse 326 * This method is not part of ISO specification. It does not provide any additional 327 * information compared to that accessible though the standard methods defined by 328 * ISO, but makes easier to access frequently requested information. 329 */ 330 GenericName toFullyQualifiedName(); 331 332 /** 333 * Returns this name expanded with the specified scope. One may represent this operation 334 * as a concatenation of the specified {@code scope} with {@code this}. In pseudo-code, 335 * the following relationships must hold (the last one is specific to {@link ScopedName}): 336 * 337 * <ul> 338 * <li><code>push(</code><var>foo</var><code> : LocalName).{@linkplain #head()}</code> 339 * = <var>foo</var></li> 340 * 341 * <li><code>push(</code><var>foo</var><code> : LocalName).{@linkplain ScopedName#tail() tail()}</code> 342 * = <var>this</var></li> 343 * 344 * <li><code>push(</code><var>foo</var><code> : GenericName).{@linkplain #scope()}</code> 345 * = <var>foo</var>.{@link #scope()}</li> 346 * 347 * <li><code>push(</code><var>foo</var><code> : GenericName).{@linkplain #getParsedNames()}</code> 348 * = <var>foo</var>.<code>getParsedNames().addAll(</code><var>this</var>.<code>getParsedNames())</code></li> 349 * </ul> 350 * 351 * <div class="note"><b>Example:</b> 352 * if {@code this} name is {@code "EPSG::4326"} and the given {@code scope} argument is {@code "urn:ogc:def:crs"}, 353 * then {@code this.push(scope)} shall returns {@code "urn:ogc:def:crs:EPSG::4326"}. 354 * </div> 355 * 356 * <div class="note"><b>Analogy:</b> 357 * this method is similar in purpose to 358 * <code>{@linkplain javax.naming.Name#addAll(int,javax.naming.Name) Name.addAll}(0, name)</code> 359 * from the <cite>Java Naming and Directory Interface</cite>. 360 * </div> 361 * 362 * @param scope the name to use as prefix. 363 * @return a concatenation of the given scope with this name. 364 */ 365 @UML(identifier="push", obligation=MANDATORY, specification=ISO_19103) 366 ScopedName push(GenericName scope); 367 368 /** 369 * Compares this name with another name for order. 370 * The recommended ordering for generic names is to {@linkplain String#compareTo(String) compare lexicographically} 371 * each element in the {@linkplain #getParsedNames() list of parsed names}. 372 * Specific attributes of the name, such as how it treats case, may affect the ordering. 373 * 374 * <div class="note"><b>Analogy:</b> 375 * this method is similar in purpose to the {@link java.io.File#compareTo File.compareTo(File)} or 376 * {@link java.nio.file.Path#compareTo Path.compareTo(Path)} methods in Java I/O. 377 * </div> 378 * 379 * @param other the other object to be compared to this name. 380 * @return a negative integer, zero, or a positive integer as this name is lexicographically 381 * less than, equal to, or greater than the specified name. 382 */ 383 @Override 384 int compareTo(GenericName other); 385 386 /** 387 * Returns a string representation of this generic name. This string representation is local-independent. 388 * It contains all elements listed by {@link #getParsedNames()} separated by a namespace-dependent character 389 * (usually {@code '.'}, {@code ':'} or {@code '/'}). 390 * This rule implies that the result may or may not be fully qualified. 391 * 392 * <p>Special cases:</p> 393 * <ul> 394 * <li><code>{@linkplain #toFullyQualifiedName()}.toString()</code> is guaranteed to 395 * formats the {@linkplain #scope() scope} (if any) before this name.</li> 396 * <li><code>{@linkplain #tip()}.toString()</code> is guaranteed to <strong>not</strong> 397 * formats any scope.</li> 398 * </ul> 399 * 400 * <p>In the {@link LocalName} sub-type, this method maps to the {@code aName} ISO 19103 attribute. 401 * In the {@link ScopedName} sub-type, this method maps to the {@code scopedName} ISO 19103 attribute.</p> 402 * 403 * <div class="note"><b>Analogy:</b> 404 * this method is similar in purpose to the {@link java.io.File#toString() File.toString()} or 405 * {@link java.nio.file.Path#toString() Path.toString()} methods in Java I/O. 406 * </div> 407 * 408 * @return the local-independent string representation of this name. 409 */ 410 @Override 411 String toString(); 412 413 /** 414 * Returns a local-dependent string representation of this generic name. This string 415 * is similar to the one returned by {@link #toString()} except that each element has 416 * been localized in the {@linkplain InternationalString#toString(java.util.Locale) 417 * specified locale}. If no international string is available, then this method shall 418 * returns an implementation mapping to {@link #toString()} for all locales. 419 * 420 * <div class="note"><b>Example:</b> 421 * an implementation may want to localize the {@code "My Documents"} directory name 422 * into {@code "Mes Documents"} on French installation of Windows operating system. 423 * </div> 424 * 425 * @return a localizable string representation of this name. 426 * 427 * @departure extension 428 * This method is not part of the ISO specification. It has been added to provide 429 * a way to localize the name. 430 */ 431 InternationalString toInternationalString(); 432}