/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2006-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.referencing.factory; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.LogRecord; import org.opengis.referencing.AuthorityFactory; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.geotools.util.Version; import org.geotools.resources.Classes; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Loggings; import org.geotools.resources.i18n.LoggingKeys; /** * Split a URN into its {@link #type} and {@link #version} parts for {@link URN_AuthorityFactory}. * This class must be immutable in order to avoid the need for synchronization in the authority * factory. * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux */ final class URN_Parser { /** * The begining parts of the URN, typically {@code "urn:ogc:def:"} and {@code "urn:x-ogc:def:"}. * All elements in the array are treated as synonymous. Those parts are up to, but do not * include, de type part ({@code "crs"}, {@code "cs"}, {@code "datum"}, <cite>etc.</cite>). * They must include a trailing (@value #SEPARATOR} character. */ private static final String[] URN_BASES = new String[] { "urn:ogc:def:", "urn:x-ogc:def:" }; /** * The parts separator in the URN. */ private static final char SEPARATOR = ':'; /** * The parsed code as full URN. */ public final String urn; /** * The type part of the URN ({@code "crs"}, {@code "cs"}, {@code "datum"}, <cite>etc</cite>). */ public final URN_Type type; /** * The authority part of the URN (typically {@code "EPSG"}). */ public final String authority; /** * The version part of the URN, or {@code null} if none. */ public final Version version; /** * The code part of the URN. */ public final String code; /** * Parses the specified URN. * * @param urn The URN to parse. * @throws NoSuchAuthorityCodeException if the URN syntax is invalid. * * @todo Implementation should be replaced by some mechanism using {@code GenericName} * (at least the call to {@code String.regionMatches}) otherwise this method will * fails if there is spaces around the separator. */ public URN_Parser(final String urn) throws NoSuchAuthorityCodeException { this.urn = urn; final String code = urn.trim(); String type = urn; // To be really assigned later. for (int i=0; i<URN_BASES.length; i++) { final String urnBase = URN_BASES[i]; final int typeStart = urnBase.length(); if (code.regionMatches(true, 0, urnBase, 0, typeStart)) { final int typeEnd = code.indexOf(SEPARATOR, typeStart); if (typeEnd >= 0) { type = code.substring(typeStart, typeEnd).trim(); final URN_Type candidate = URN_Type.get(type); if (candidate != null) { final int nameEnd = code.indexOf(SEPARATOR, typeEnd + 1); if (nameEnd >= 0) { final int lastEnd = code.lastIndexOf(SEPARATOR); this.version = (lastEnd <= nameEnd) ? null : new Version(code.substring(nameEnd + 1, lastEnd)); this.authority = code.substring(typeEnd + 1, nameEnd).trim(); this.code = code.substring(lastEnd + 1).trim(); this.type = candidate; return; } } } } } throw new NoSuchAuthorityCodeException( Errors.format(ErrorKeys.ILLEGAL_IDENTIFIER_$1, type), "urn:ogc:def", type); } /** * Returns the concatenation of the {@linkplain #authority} and the {@linkplain #code}. */ public String getAuthorityCode() { return authority + SEPARATOR + code; } /** * Checks if the type is compatible with the expected one. This method is used as a safety * by {@code getFooAuthorityFactory(String)} methods in {@link URN_AuthorityFactory}. If a * mismatch is found, a warning is logged but no exception is thrown since it doesn't prevent * the class to work in a predicable way. It is just an indication for the user that his URN * may be wrong. */ final void logWarningIfTypeMismatch(final Class<? extends AuthorityFactory> expected) { if (!expected.isAssignableFrom(type.type)) { // Build a simplified URN, omitting "urn:ogc:def" and version number. final String urn = "..." + SEPARATOR + type + SEPARATOR + authority + SEPARATOR + code; final LogRecord record = Loggings.format(Level.WARNING, LoggingKeys.MISMATCHED_URN_TYPE_$1, urn); // Set the source to the public or protected method. record.setSourceClassName(URN_AuthorityFactory.class.getName()); record.setSourceMethodName("get" + Classes.getShortName(expected)); final Logger logger = AbstractAuthorityFactory.LOGGER; record.setLoggerName(logger.getName()); logger.log(record); } } /** * Returns the URN. */ @Override public String toString() { return urn; } }