001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2007, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.metadata;
017:
018: // J2SE dependencies
019: import java.util.AbstractMap;
020: import java.util.AbstractSet;
021: import java.util.Iterator;
022: import java.util.Map;
023: import java.util.NoSuchElementException;
024: import java.util.Set;
025:
026: // Geotools dependencies
027: import org.geotools.resources.Utilities;
028:
029: /**
030: * A view of a metadata object as a map. Keys are property names and values
031: * are the value returned by the {@code getFoo()} method using reflection.
032: *
033: * @since 2.4
034: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/metadata/PropertyMap.java $
035: * @version $Id: PropertyMap.java 25157 2007-04-12 15:59:03Z desruisseaux $
036: * @author Martin Desruisseaux (Geomatys)
037: *
038: * @see MetadataStandard#asMap
039: */
040: final class PropertyMap extends AbstractMap/*<String,Object>*/{
041: /**
042: * The metadata object to wrap.
043: */
044: private final Object metadata;
045:
046: /**
047: * The accessor to use for the metadata.
048: */
049: private final PropertyAccessor accessor;
050:
051: /**
052: * A view of the mappings contained in this map.
053: */
054: private final Set/*<Map.Entry<String,Object>>*/entrySet;
055:
056: /**
057: * Creates a property map for the specified metadata and accessor.
058: */
059: public PropertyMap(final Object metadata,
060: final PropertyAccessor accessor) {
061: this .metadata = metadata;
062: this .accessor = accessor;
063: entrySet = new Entries();
064: }
065:
066: /**
067: * Returns {@code true} if this map contains no key-value mappings.
068: */
069: public boolean isEmpty() {
070: return entrySet().isEmpty();
071: }
072:
073: /**
074: * Returns {@code true} if this map contains a mapping for the specified key.
075: */
076: public boolean containsKey(final Object key) {
077: return get(key) != null;
078: }
079:
080: /**
081: * Returns the value to which the specified key is mapped, or {@code null}
082: * if this map contains no mapping for the key.
083: */
084: public Object get(final Object key) {
085: final Object value = accessor.get(accessor
086: .indexOf((String) key), metadata);
087: return PropertyAccessor.isEmpty(value) ? null : value;
088: }
089:
090: /**
091: * Associates the specified value with the specified key in this map.
092: *
093: * @throws IllegalArgumentException if the specified property can't be set.
094: */
095: public Object put(final Object key, final Object value)
096: throws IllegalArgumentException {
097: return accessor.set(accessor.indexOf((String) key), metadata,
098: value);
099: }
100:
101: /**
102: * Removes the mapping for a key from this map if it is present.
103: */
104: public Object remove(final Object key) {
105: return put(key, null);
106: }
107:
108: /**
109: * Returns a view of the mappings contained in this map.
110: */
111: public Set/*<Map.Entry<String,Object>>*/entrySet() {
112: return entrySet;
113: }
114:
115: /**
116: * A map entry for a given property.
117: *
118: * @author Martin Desruisseaux
119: */
120: private final class Property/*<String,Object>*/implements
121: Map.Entry {
122: /**
123: * The property index.
124: */
125: final int index;
126:
127: /**
128: * Creates an entry for the given property.
129: */
130: Property(final int index) {
131: this .index = index;
132: }
133:
134: /**
135: * Creates an entry for the given property.
136: */
137: Property(final String property) {
138: index = accessor.indexOf(property);
139: }
140:
141: /**
142: * Returns the key corresponding to this entry.
143: */
144: public Object getKey() {
145: return accessor.name(index);
146: }
147:
148: /**
149: * Returns the value corresponding to this entry.
150: */
151: public Object getValue() {
152: final Object value = accessor.get(index, metadata);
153: return PropertyAccessor.isEmpty(value) ? null : value;
154: }
155:
156: /**
157: * Replaces the value corresponding to this entry with the specified value.
158: */
159: public Object setValue(Object value) {
160: return accessor.set(index, metadata, value);
161: }
162:
163: /**
164: * Compares the specified entry with this one for equality.
165: */
166: public boolean equals(final Map.Entry entry) {
167: return Utilities.equals(getKey(), entry.getKey())
168: && Utilities.equals(getValue(), entry.getValue());
169: }
170:
171: /**
172: * Compares the specified object with this entry for equality.
173: * Criterions are specified by the {@link Map.Entry} contract.
174: */
175: public boolean equals(final Object object) {
176: return (object instanceof Map.Entry)
177: && equals((Map.Entry) object);
178: }
179:
180: /**
181: * Returns the hash code value for this map entry. The
182: * formula is specified by the {@link Map.Entry} contract.
183: */
184: public int hashCode() {
185: Object x = getKey();
186: int code = (x != null) ? x.hashCode() : 0;
187: x = getValue();
188: if (x != null) {
189: code ^= x.hashCode();
190: }
191: return code;
192: }
193: }
194:
195: /**
196: * The iterator over the {@link Property} elements contained in a {@link Entries} set.
197: *
198: * @author Martin Desruisseaux
199: */
200: private final class Iter implements Iterator {
201: /**
202: * The current and the next property, or {@code null} if the iteration is over.
203: */
204: private Property current, next;
205:
206: /**
207: * Creates en iterator.
208: */
209: Iter() {
210: move(0);
211: }
212:
213: /**
214: * Move {@link #next} to the first property with a valid value,
215: * starting at the specified index.
216: */
217: private void move(int index) {
218: final int count = accessor.count();
219: while (index < count) {
220: if (!PropertyAccessor.isEmpty(accessor.get(index,
221: metadata))) {
222: next = new Property(index);
223: return;
224: }
225: index++;
226: }
227: next = null;
228: }
229:
230: /**
231: * Returns {@code true} if the iteration has more elements.
232: */
233: public boolean hasNext() {
234: return next != null;
235: }
236:
237: /**
238: * Returns the next element in the iteration.
239: */
240: public Object next() {
241: if (next != null) {
242: current = next;
243: move(next.index + 1);
244: return current;
245: } else {
246: throw new NoSuchElementException();
247: }
248: }
249:
250: /**
251: * Removes from the underlying collection the last element returned by the iterator.
252: */
253: public void remove() {
254: if (current != null) {
255: current.setValue(null);
256: current = null;
257: } else {
258: throw new IllegalStateException();
259: }
260: }
261: }
262:
263: /**
264: * View of the mapping contained in the map.
265: *
266: * @author Martin Desruisseaux
267: */
268: private final class Entries extends AbstractSet/*<Map.Entry<String,Object>>*/{
269: /**
270: * Creates an entry set.
271: */
272: Entries() {
273: }
274:
275: /**
276: * Returns an iterator over the elements contained in this collection.
277: */
278: public Iterator iterator() {
279: return new Iter();
280: }
281:
282: /**
283: * Returns the number of elements in this collection.
284: */
285: public int size() {
286: return accessor.count(metadata, Integer.MAX_VALUE);
287: }
288:
289: /**
290: * Returns true if this collection contains no elements.
291: */
292: public boolean isEmpty() {
293: return accessor.count(metadata, 1) == 0;
294: }
295:
296: /**
297: * Returns {@code true} if this collection contains the specified element.
298: */
299: public boolean contains(final Object object) {
300: if (object instanceof Map.Entry) {
301: final Map.Entry entry = (Map.Entry) object;
302: final Object key = entry.getKey();
303: if (key instanceof String) {
304: return new Property((String) key).equals(entry);
305: }
306: }
307: return false;
308: }
309: }
310: }
|