001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2006, Geomatys
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;
010: * version 2.1 of the License.
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: package org.geotools.referencing.factory;
018:
019: // J2SE dependencies
020: import java.util.Iterator;
021: import java.util.Collection;
022: import java.util.logging.Level;
023: import java.util.logging.LogRecord;
024:
025: // OpenGIS dependencies
026: import org.opengis.metadata.citation.Citation;
027: import org.opengis.referencing.AuthorityFactory;
028: import org.opengis.referencing.NoSuchAuthorityCodeException;
029: import org.opengis.referencing.cs.CSAuthorityFactory;
030: import org.opengis.referencing.crs.CRSAuthorityFactory;
031: import org.opengis.referencing.datum.DatumAuthorityFactory;
032:
033: // Geotools dependencies
034: import org.geotools.util.Version;
035: import org.geotools.factory.Hints;
036: import org.geotools.factory.Factory;
037: import org.geotools.resources.Utilities;
038: import org.geotools.resources.i18n.Errors;
039: import org.geotools.resources.i18n.ErrorKeys;
040: import org.geotools.resources.i18n.Logging;
041: import org.geotools.resources.i18n.LoggingKeys;
042: import org.geotools.metadata.iso.citation.CitationImpl;
043:
044: /**
045: * Split a URN into its {@link #type} and {@link #version} parts for {@link URN_AuthorityFactory}.
046: * This class must be immutable in order to avoid the need for synchronization in the authority
047: * factory.
048: *
049: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/factory/URN_Parser.java $
050: * @version $Id: URN_Parser.java 24435 2007-02-16 03:32:10Z desruisseaux $
051: * @author Martin Desruisseaux
052: */
053: final class URN_Parser {
054: /**
055: * The begining parts of the URN, typically {@code "urn:ogc:def:"} and {@code "urn:x-ogc:def:"}.
056: * All elements in the array are treated as synonymous. Those parts are up to, but do not
057: * include, de type part ({@code "crs"}, {@code "cs"}, {@code "datum"}, <cite>etc.</cite>).
058: * They must include a trailing (@value #SEPARATOR} character.
059: */
060: private static final String[] URN_BASES = new String[] {
061: "urn:ogc:def:", "urn:x-ogc:def:" };
062:
063: /**
064: * The parts separator in the URN.
065: */
066: private static final char SEPARATOR = ':';
067:
068: /**
069: * The parsed code as full URN.
070: */
071: public final String urn;
072:
073: /**
074: * The type part of the URN ({@code "crs"}, {@code "cs"}, {@code "datum"}, <cite>etc</cite>).
075: */
076: public final URN_Type type;
077:
078: /**
079: * The authority part of the URN (typically {@code "EPSG"}).
080: */
081: public final String authority;
082:
083: /**
084: * The version part of the URN, or {@code null} if none.
085: */
086: public final Version version;
087:
088: /**
089: * The code part of the URN.
090: */
091: public final String code;
092:
093: /**
094: * Parses the specified URN.
095: *
096: * @param urn The URN to parse.
097: * @throws NoSuchAuthorityCodeException if the URN syntax is invalid.
098: *
099: * @todo Implementation should be replaced by some mechanism using {@code GenericName}
100: * (at least the call to {@code String.regionMatches}) otherwise this method will
101: * fails if there is spaces around the separator.
102: */
103: public URN_Parser(final String urn)
104: throws NoSuchAuthorityCodeException {
105: this .urn = urn;
106: final String code = urn.trim();
107: String type = urn; // To be really assigned later.
108: for (int i = 0; i < URN_BASES.length; i++) {
109: final String urnBase = URN_BASES[i];
110: final int typeStart = urnBase.length();
111: if (code.regionMatches(true, 0, urnBase, 0, typeStart)) {
112: final int typeEnd = code.indexOf(SEPARATOR, typeStart);
113: if (typeEnd >= 0) {
114: type = code.substring(typeStart, typeEnd).trim();
115: final URN_Type candidate = URN_Type.get(type);
116: if (candidate != null) {
117: final int nameEnd = code.indexOf(SEPARATOR,
118: typeEnd + 1);
119: if (nameEnd >= 0) {
120: final int lastEnd = code
121: .lastIndexOf(SEPARATOR);
122: this .version = (lastEnd <= nameEnd) ? null
123: : new Version(code.substring(
124: nameEnd + 1, lastEnd));
125: this .authority = code.substring(
126: typeEnd + 1, nameEnd).trim();
127: this .code = code.substring(lastEnd + 1)
128: .trim();
129: this .type = candidate;
130: return;
131: }
132: }
133: }
134: }
135: }
136: throw new NoSuchAuthorityCodeException(Errors.format(
137: ErrorKeys.ILLEGAL_IDENTIFIER_$1, type), "urn:ogc:def",
138: type);
139: }
140:
141: /**
142: * Returns the concatenation of the {@linkplain #authority} and the {@linkplain #code}.
143: */
144: public String getAuthorityCode() {
145: return authority + SEPARATOR + code;
146: }
147:
148: /**
149: * Checks if the type is compatible with the expected one. This method is used as a safety
150: * by {@code getFooAuthorityFactory(String)} methods in {@link URN_AuthorityFactory}. If a
151: * mismatch is found, a warning is logged but no exception is thrown since it doesn't prevent
152: * the class to work in a predicable way. It is just an indication for the user that his URN
153: * may be wrong.
154: */
155: final void logWarningIfTypeMismatch(
156: final Class/*<? extends AuthorityFactory>*/expected) {
157: if (!expected.isAssignableFrom(type.type)) {
158: // Build a simplified URN, omitting "urn:ogc:def" and version number.
159: final String urn = "..." + SEPARATOR + type + SEPARATOR
160: + authority + SEPARATOR + code;
161: final LogRecord record = Logging.format(Level.WARNING,
162: LoggingKeys.MISMATCHED_URN_TYPE_$1, urn);
163: // Set the source to the public or protected method.
164: record.setSourceClassName(URN_AuthorityFactory.class
165: .getName());
166: record.setSourceMethodName("get"
167: + Utilities.getShortName(expected));
168: AbstractAuthorityFactory.LOGGER.log(record);
169: }
170: }
171:
172: /**
173: * Returns the URN.
174: */
175: public String toString() {
176: return urn;
177: }
178: }
|