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;
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.crs;
018:
019: // J2SE dependencies
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: // OpenGIS dependencies
024: import org.opengis.referencing.IdentifiedObject;
025:
026: // Geotools dependencies
027: import org.geotools.util.DerivedMap;
028:
029: /**
030: * A map without the <code>"conversion."</code> prefix in front of property keys. This
031: * implementation performs a special processing for the <code>{@linkplain #prefix}.name</code>
032: * key: if it doesn't exists, then the plain {@code name} key is used. In other words,
033: * this map inherits the <code>"name"</code> property from the {@linkplain #base} map.
034: *
035: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/crs/UnprefixedMap.java $
036: * @version $Id: UnprefixedMap.java 20874 2006-08-07 10:00:01Z jgarnett $
037: * @author Martin Desruisseaux
038: *
039: * @since 2.0
040: */
041: final class UnprefixedMap extends DerivedMap {
042: /**
043: * The prefix to remove for this map.
044: */
045: private final String prefix;
046:
047: /**
048: * {@code true} if the <code>{@linkplain #prefix}.name</code> property exists
049: * in the {@linkplain #base base} map. This class will inherit the name and alias
050: * from the {@linkplain #base base} map only if this field is set to {@code false}.
051: */
052: private final boolean hasName, hasAlias;
053:
054: /**
055: * Creates a new unprefixed map from the specified base map and prefix.
056: *
057: * @param base The base map.
058: * @param prefix The prefix to remove from the keys in the base map.
059: */
060: public UnprefixedMap(final Map base, final String prefix) {
061: super (base);
062: this .prefix = prefix.trim();
063: final String nameKey = this .prefix + IdentifiedObject.NAME_KEY;
064: final String aliasKey = this .prefix
065: + IdentifiedObject.ALIAS_KEY;
066: boolean hasName = false;
067: boolean hasAlias = false;
068: for (final Iterator it = base.keySet().iterator(); it.hasNext();) {
069: final String candidate = it.next().toString().trim();
070: if (keyMatches(nameKey, candidate)) {
071: hasName = true;
072: if (hasAlias)
073: break;
074: } else if (keyMatches(aliasKey, candidate)) {
075: hasAlias = true;
076: if (hasName)
077: break;
078: }
079: }
080: this .hasName = hasName;
081: this .hasAlias = hasAlias;
082: }
083:
084: /**
085: * Remove the prefix from the specified key. If the key doesn't begins with
086: * the prefix, then this method returns {@code null}.
087: *
088: * @param key A key from the {@linkplain #base} map.
089: * @return The key that this view should contains instead of {@code key},
090: * or {@code null}.
091: */
092: protected Object baseToDerived(final Object key) {
093: final int length = prefix.length();
094: final String textualKey = key.toString().trim();
095: if (textualKey.regionMatches(true, 0, prefix, 0, length)) {
096: return textualKey.substring(length).trim();
097: }
098: if (isPlainKey(textualKey)) {
099: return textualKey;
100: }
101: return null;
102: }
103:
104: /**
105: * Add the prefix to the specified key.
106: *
107: * @param key A key in this map.
108: * @return The key stored in the {@linkplain #base} map.
109: */
110: protected Object derivedToBase(final Object key) {
111: final String textualKey = key.toString().trim();
112: if (isPlainKey(textualKey)) {
113: return textualKey;
114: }
115: return prefix + textualKey;
116: }
117:
118: /**
119: * Returns {@code true} if the specified candidate is <code>"name"</code>
120: * or <code>"alias"</code> without prefix. Key starting with <code>"name_"</code>
121: * or <code>"alias_"</code> are accepted as well.
122: */
123: private boolean isPlainKey(final String key) {
124: return (!hasName && keyMatches(IdentifiedObject.NAME_KEY, key))
125: || (!hasAlias && keyMatches(IdentifiedObject.ALIAS_KEY,
126: key));
127: }
128:
129: /**
130: * Returns {@code true} if the specified candidate matched
131: * the specified key name.
132: */
133: private static boolean keyMatches(final String key,
134: final String candidate) {
135: final int length = key.length();
136: return candidate.regionMatches(true, 0, key, 0, length)
137: && (candidate.length() == length || candidate
138: .charAt(length) == '_');
139: }
140: }
|