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 dependencies
023: import java.io.Serializable;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Locale;
027:
028: // OpenGIS dependencies
029: import org.opengis.util.NameSpace;
030: import org.opengis.util.LocalName;
031: import org.opengis.util.ScopedName;
032: import org.opengis.util.InternationalString;
033:
034: // Geotools dependencies
035: import org.geotools.resources.Utilities;
036:
037: /**
038: * Base class for {@linkplain ScopedName generic scoped} and
039: * {@linkplain LocalName local name} structure for type and attribute
040: * name in the context of name spaces.
041: *
042: * <P>Note: this class has a natural ordering that is inconsistent with equals.
043: * The natural ordering may be case-insensitive and ignore the character
044: * separator between name elements.</P>
045: *
046: * @since 2.1
047: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/util/GenericName.java $
048: * @version $Id: GenericName.java 23632 2006-12-29 22:13:51Z desruisseaux $
049: * @author Martin Desruisseaux
050: *
051: * @see NameFactory
052: */
053: public abstract class GenericName implements
054: org.opengis.util.GenericName, Serializable {
055: /**
056: * Serial number for interoperability with different versions.
057: */
058: private static final long serialVersionUID = 8685047583179337259L;
059:
060: /**
061: * The default separator character.
062: */
063: public static final char DEFAULT_SEPARATOR = ':';
064:
065: /**
066: * Creates a new instance of generic name.
067: */
068: protected GenericName() {
069: }
070:
071: /**
072: * Ensures that the given name is a {@link String} or an {@link InternationalString}.
073: * This is used for subclass constructors.
074: */
075: static CharSequence validate(final CharSequence name) {
076: return (name == null || name instanceof InternationalString) ? name
077: : name.toString();
078: }
079:
080: /**
081: * Returns the scope (name space) in which this name is local. The scope is set on creation
082: * and is not modifiable. The scope of a name determines where a name "starts". For instance,
083: * if a name has a {@linkplain #depth depth} of two ({@code "util.GenericName"}) and is
084: * associated with a {@linkplain NameSpace name space} having the name {@code "org.opengis"},
085: * then the fully qualified name would be {@code "org.opengis.util.GenericName"}.
086: *
087: * @since 2.3
088: */
089: public abstract NameSpace scope();
090:
091: /**
092: * Returns the depth of this name within the namespace hierarchy. This indicates the number
093: * of levels specified by this name. For any {@link LocalName}, it is always one. For a
094: * {@link ScopedName} it is some number greater than or equal to 2.
095: * <p>
096: * The depth is the length of the list returned by the {@link #getParsedNames} method.
097: * As such it is a derived parameter.
098: *
099: * @since 2.3
100: */
101: public abstract int depth();
102:
103: /**
104: * Returns the sequence of {@linkplain LocalName local names} making this generic name.
105: * Each element in this list is like a directory name in a file path name.
106: * The length of this sequence is the generic name depth.
107: */
108: public abstract List getParsedNames();
109:
110: /**
111: * Returns the last element in the sequence of {@linkplain #getParsedNames parsed names}.
112: * For any {@link LocalName}, this is always {@code this}.
113: *
114: * @see LocalName#name
115: * @see ScopedName#name
116: *
117: * @since 2.3
118: */
119: public abstract LocalName name();
120:
121: /**
122: * Returns the separator character. Default to <code>':'</code>.
123: * This method is overridden by {@link org.geotools.util.ScopedName}.
124: */
125: char getSeparator() {
126: return DEFAULT_SEPARATOR;
127: }
128:
129: /**
130: * Returns a string representation of this generic name. This string representation
131: * is local-independant. It contains all elements listed by {@link #getParsedNames}
132: * separated by an arbitrary character (usually {@code :} or {@code /}).
133: * This rule implies that the {@code toString()} method for a
134: * {@linkplain ScopedName scoped name} will contains the scope, while the
135: * {@code toString()} method for the {@linkplain LocalName local version} of
136: * the same name will not contains the scope.
137: */
138: public String toString() {
139: final StringBuffer buffer = new StringBuffer();
140: final List parsedNames = getParsedNames();
141: final char separator = getSeparator();
142: for (final Iterator it = parsedNames.iterator(); it.hasNext();) {
143: if (buffer.length() != 0) {
144: buffer.append(separator);
145: }
146: buffer.append(it.next());
147: }
148: return buffer.toString();
149: }
150:
151: /**
152: * Returns a local-dependent string representation of this generic name. This string
153: * is similar to the one returned by {@link #toString} except that each element has
154: * been localized in the {@linkplain InternationalString#toString(Locale)
155: * specified locale}. If no international string is available, then this method should
156: * returns an implementation mapping to {@link #toString} for all locales.
157: */
158: public InternationalString toInternationalString() {
159: return new International(getParsedNames(), getSeparator());
160: }
161:
162: /**
163: * An international string built from a snapshot of {@link GenericName}.
164: *
165: * @version $Id: GenericName.java 23632 2006-12-29 22:13:51Z desruisseaux $
166: * @author Martin Desruisseaux
167: */
168: private static final class International extends
169: AbstractInternationalString implements Serializable {
170: /**
171: * Serial number for interoperability with different versions.
172: */
173: private static final long serialVersionUID = -4234089612436334148L;
174:
175: /**
176: * The sequence of {@linkplain LocalName local names} making this generic name.
177: * This is the value returned by {@link GenericName#getParsedNames}.
178: */
179: private final List parsedNames;
180:
181: /**
182: * The separator character. This is the value returned by {@link GenericName#getSeparator}.
183: */
184: private final char separator;
185:
186: /**
187: * Constructs a new international string from the specified {@link GenericName} fields.
188: *
189: * @param parsedNames The value returned by {@link GenericName#getParsedNames}.
190: * @param separator The value returned by {@link GenericName#getSeparator}.
191: */
192: public International(final List parsedNames,
193: final char separator) {
194: this .parsedNames = parsedNames;
195: this .separator = separator;
196: }
197:
198: /**
199: * Returns a string representation for the specified locale.
200: */
201: public String toString(final Locale locale) {
202: final StringBuffer buffer = new StringBuffer();
203: for (final Iterator it = parsedNames.iterator(); it
204: .hasNext();) {
205: if (buffer.length() != 0) {
206: buffer.append(separator);
207: }
208: buffer
209: .append(((org.opengis.util.GenericName) it
210: .next()) // Remove cast with 1.5
211: .toInternationalString().toString(
212: locale));
213: }
214: return buffer.toString();
215: }
216:
217: /**
218: * Compares this international string with the specified object for equality.
219: */
220: public boolean equals(final Object object) {
221: if (object != null && object.getClass().equals(getClass())) {
222: final International that = (International) object;
223: return Utilities.equals(this .parsedNames,
224: that.parsedNames)
225: && this .separator == that.separator;
226: }
227: return false;
228: }
229:
230: /**
231: * Returns a hash code value for this international text.
232: */
233: public int hashCode() {
234: return (int) serialVersionUID ^ parsedNames.hashCode();
235: }
236: }
237:
238: /**
239: * Compares this name with the specified object for order. Returns a negative integer,
240: * zero, or a positive integer as this name lexicographically precedes, is equals to,
241: * or follows the specified object. The comparaison is performed in the following
242: * order:
243: * <ul>
244: * <li>Compares each element in the {@linkplain #getParsedNames list of parsed names}. If an
245: * element of this name lexicographically precedes or follows the corresponding element
246: * of the specified name, returns a negative or a positive integer respectively.</li>
247: * <li>If all elements in both names are lexicographically equal, then if this name has less
248: * or more elements than the specified name, returns a negative or a positive integer
249: * respectively.</li>
250: * <li>Otherwise, returns 0.</li>
251: * </ul>
252: */
253: public int compareTo(final Object object) {
254: final org.opengis.util.GenericName that = (org.opengis.util.GenericName) object;
255: final Iterator this Names = this .getParsedNames().iterator();
256: final Iterator thatNames = that.getParsedNames().iterator();
257: while (this Names.hasNext()) {
258: if (!thatNames.hasNext()) {
259: return +1;
260: }
261: final Comparable this Next = (Comparable) this Names.next();
262: final Comparable thatNext = (Comparable) thatNames.next();
263: if (this Next == this && thatNext == that) {
264: // Never-ending loop: usually an implementation error
265: throw new IllegalStateException();
266: }
267: final int compare = this Next.compareTo(thatNext);
268: if (compare != 0) {
269: return compare;
270: }
271: }
272: return thatNames.hasNext() ? -1 : 0;
273: }
274:
275: /**
276: * Compares this generic name with the specified object for equality.
277: */
278: public boolean equals(final Object object) {
279: if (object != null && object.getClass().equals(getClass())) {
280: final GenericName that = (GenericName) object;
281: return Utilities.equals(this .getParsedNames(), that
282: .getParsedNames())
283: && this .getSeparator() == that.getSeparator();
284: }
285: return false;
286: }
287:
288: /**
289: * Returns a hash code value for this generic name.
290: */
291: public int hashCode() {
292: return (int) serialVersionUID ^ getParsedNames().hashCode();
293: }
294: }
|