001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2004, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * This package contains documentation from OpenGIS specifications.
018: * OpenGIS consortium's work is fully acknowledged here.
019: */
020: package org.geotools.util;
021:
022: // J2SE direct dependencies
023: import java.util.Collections;
024: import java.util.List;
025: import java.util.Locale;
026:
027: // OpenGIS dependencies
028: import org.opengis.util.GenericName;
029: import org.opengis.util.InternationalString;
030: import org.opengis.util.NameSpace;
031: import org.opengis.util.ScopedName;
032:
033: // Geotools dependencies
034: import org.geotools.resources.Utilities;
035:
036: /**
037: * Identifier within a name space for a local object. This could be the target object of the
038: * {@link GenericName}, or a pointer to another name space (with a new {@link GenericName})
039: * one step closer to the target of the identifier.
040: *
041: * @since 2.1
042: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/util/LocalName.java $
043: * @version $Id: LocalName.java 22443 2006-10-27 20:47:22Z desruisseaux $
044: * @author Martin Desruisseaux
045: *
046: * @see NameFactory
047: */
048: public class LocalName extends org.geotools.util.GenericName implements
049: org.opengis.util.LocalName {
050: /**
051: * Serial number for interoperability with different versions.
052: */
053: private static final long serialVersionUID = -5627125375582385822L;
054:
055: /**
056: * The view of this object as a scoped name.
057: */
058: private final ScopedName asScopedName;
059:
060: /**
061: * The name, either as a {@link String} or an {@link InternationalString}.
062: */
063: private final CharSequence name;
064:
065: /**
066: * The name as a string.
067: * If not provided, will be built only when first needed.
068: */
069: private transient String asString;
070:
071: /**
072: * The name as an international string.
073: * If not provided, will be built only when first needed.
074: */
075: private transient InternationalString asInternationalString;
076:
077: /**
078: * The sequence of local name for this {@linkplain GenericName generic name}.
079: * Since this object is itself a locale name, this list is always a singleton
080: * containing only {@code this}. It will be built only when first needed.
081: */
082: private transient List parsedNames;
083:
084: /**
085: * Constructs a local name from the specified string with no scope.
086: * If the specified name is an {@link InternationalString}, then the
087: * <code>{@linkplain InternationalString#toString(Locale) toString}(null)</code>
088: * method will be used in order to fetch an unlocalized name. Otherwise, the
089: * <code>{@linkplain CharSequence#toString toString}()</code> method will be used.
090: *
091: * @param name The local name (never {@code null}).
092: */
093: public LocalName(final CharSequence name) {
094: this (null, name);
095: }
096:
097: /**
098: * Constructs a local name from the specified international string.
099: *
100: * This constructor is not public since it can't be used from outside
101: * of {@link org.geotools.util.ScopedName} constructor (otherwise some
102: * methods in this class may have the wrong semantic).
103: *
104: * @param asScopedName The view of this object as a scoped name.
105: * @param name The local name (never {@code null}).
106: */
107: LocalName(final ScopedName asScopedName, final CharSequence name) {
108: this .asScopedName = asScopedName;
109: this .name = validate(name);
110: AbstractInternationalString.ensureNonNull("name", name);
111: }
112:
113: /**
114: * Returns the scope (name space) of this generic name.
115: * This method is protected from overriding by the user.
116: */
117: private GenericName getInternalScope() {
118: if (asScopedName != null) {
119: final NameSpace scope = asScopedName.scope();
120: if (scope != null) {
121: return scope.name();
122: }
123: }
124: return null;
125: }
126:
127: /**
128: * Returns the scope (name space) of this generic name.
129: *
130: * @deprecated Replaced by {@link #scope}.
131: */
132: public GenericName getScope() {
133: return getInternalScope();
134: }
135:
136: /**
137: * Returns the scope (name space) in which this name is local. The scope is set on creation
138: * and is not modifiable. The scope of a name determines where a name "starts". For instance,
139: * if a name has a {@linkplain #depth depth} of two ({@code "util.GenericName"}) and is
140: * associated with a {@linkplain NameSpace name space} having the name {@code "org.opengis"},
141: * then the fully qualified name would be {@code "org.opengis.util.GenericName"}.
142: *
143: * @since 2.3
144: *
145: * @todo Need to be revisited once GeoAPI 2.1 is seetle down.
146: */
147: public NameSpace scope() {
148: return (asScopedName != null) ? asScopedName.scope() : null;
149: }
150:
151: /**
152: * Returns the depth, which is always 1 for a local name.
153: *
154: * @since 2.3
155: */
156: public int depth() {
157: return 1;
158: }
159:
160: /**
161: * Returns the sequence of local name for this {@linkplain GenericName generic name}.
162: * Since this object is itself a locale name, this method always returns a singleton
163: * containing only {@code this}.
164: */
165: public List getParsedNames() {
166: // No need to sychronize: it is not a big deal if this object is built twice.
167: if (parsedNames == null) {
168: parsedNames = Collections.singletonList(this );
169: }
170: return parsedNames;
171: }
172:
173: /**
174: * Returns a view of this object as a local name. Since this object is already
175: * a local name, this method always returns {@code this}.
176: *
177: * @deprecated Renamed as {@link #name()}.
178: */
179: public org.opengis.util.LocalName asLocalName() {
180: return this ;
181: }
182:
183: /**
184: * Returns {@code this} since this object is already a local name.
185: *
186: * @since 2.3
187: */
188: public org.opengis.util.LocalName name() {
189: return this ;
190: }
191:
192: /**
193: * Returns a view of this object as a scoped name,
194: * or {@code null} if this name has no scope.
195: *
196: * @deprecated Replaced by {@link #toFullyQualifiedName}.
197: */
198: public ScopedName asScopedName() {
199: return asScopedName;
200: }
201:
202: /**
203: * Returns a view of this name as a fully-qualified name. The {@linkplain #scope scope}
204: * of a fully qualified name must be {@linkplain NameSpace#isGlobal global}. This method
205: * never returns {@code null}.
206: *
207: * @since 2.3
208: *
209: * @todo Need to be revisited once GeoAPI 2.1 is seetle down.
210: */
211: public GenericName toFullyQualifiedName() {
212: if (asScopedName == null) {
213: throw new UnsupportedOperationException(
214: "Not yet implemented");
215: }
216: return asScopedName;
217: }
218:
219: /**
220: * Returns this name expanded with the specified scope. One may represent this operation
221: * as a concatenation of the specified {@code name} with {@code this}. In pseudo-code,
222: * the following relationships must hold:
223: * <p>
224: * <ul>
225: * <li><code>push(<var>name</var>).getParsedList() ==
226: * <var>name</var>.getParsedList().addAll({@linkplain #getParsedNames()})</code></li>
227: * <li><code>push(<var>name</var>).scope() == <var>name</var>.{@linkplain #scope()}</code></li>
228: * </ul>
229: * <p>
230: * <strong>Note:</strong> Those conditions can be understood in terms of the Java
231: * {@link Object#equals equals} method instead of the Java identity comparator {@code ==}.
232: *
233: * @since 2.3
234: *
235: * @todo Not yet implemented.
236: */
237: public ScopedName push(GenericName scope) {
238: throw new UnsupportedOperationException("Not yet implemented");
239: }
240:
241: /**
242: * Returns a locale-independant string representation of this local name.
243: * This string do not includes the scope, which is consistent with the
244: * {@linkplain #getParsedNames parsed names} definition.
245: */
246: public String toString() {
247: if (asString == null) {
248: if (name instanceof InternationalString) {
249: // We really want the 'null' locale, not the system default one.
250: asString = ((InternationalString) name).toString(null);
251: } else {
252: asString = name.toString();
253: }
254: }
255: return asString;
256: }
257:
258: /**
259: * Returns a local-dependent string representation of this locale name.
260: */
261: public InternationalString toInternationalString() {
262: if (asInternationalString == null) {
263: if (name instanceof InternationalString) {
264: asInternationalString = (InternationalString) name;
265: } else {
266: asInternationalString = new SimpleInternationalString(
267: name.toString());
268: }
269: }
270: return asInternationalString;
271: }
272:
273: /**
274: * Compares this name with the specified object for order. Returns a negative integer,
275: * zero, or a positive integer as this name lexicographically precedes, is equals to,
276: * or follows the specified object. The comparaison is case-insensitive.
277: */
278: public int compareTo(final Object object) {
279: return toString().compareToIgnoreCase(object.toString());
280: }
281:
282: /**
283: * Compares this local name with the specified object for equality.
284: */
285: public boolean equals(final Object object) {
286: if (object == this ) {
287: return true;
288: }
289: if (object != null && object.getClass().equals(getClass())) {
290: final LocalName that = (LocalName) object;
291: // Do not use 'asScopedName' in order to avoid never-ending loop.
292: return Utilities.equals(this .getInternalScope(), that
293: .getInternalScope())
294: && Utilities.equals(this .name, that.name);
295: }
296: return false;
297: }
298:
299: /**
300: * Returns a hash code value for this local name.
301: */
302: public int hashCode() {
303: int code = (int) serialVersionUID;
304: // Do not use 'asScopedName' in order to avoid never-ending loop.
305: if (name != null)
306: code ^= name.hashCode();
307: return code;
308: }
309: }
|