001: /*
002: * Primitive Collections for Java.
003: * Copyright (C) 2002, 2003 Søren Bak
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019: package bak.pcj.adapter;
020:
021: import bak.pcj.Adapter;
022: import bak.pcj.CharIterator;
023: import bak.pcj.CharCollection;
024: import bak.pcj.map.ObjectKeyCharMap;
025: import bak.pcj.map.AbstractObjectKeyCharMap;
026: import bak.pcj.map.ObjectKeyCharMapIterator;
027: import bak.pcj.map.MapDefaults;
028: import bak.pcj.map.NoSuchMappingException;
029: import bak.pcj.set.CharSet;
030: import bak.pcj.util.Exceptions;
031:
032: import java.util.Map;
033: import java.util.Set;
034: import java.util.Iterator;
035:
036: /**
037: * This class represents adaptions of Java Collections Framework
038: * maps to primitive maps from object values to char values.
039: * The adapter is implemented as a wrapper around the map.
040: * Thus, changes to the underlying map are reflected by this
041: * map and vice versa.
042: *
043: * <p>
044: * Adapters from JCF maps to primitive map will
045: * fail if the JCF collection contains <tt>null</tt> values or
046: * values of the wrong class. However, adapters are not fast
047: * failing in the case that the underlying map should
048: * contain illegal keys or values. To implement fast failure would require
049: * every operation to check every key and value of the underlying
050: * map before doing anything. Instead validation methods
051: * are provided. They can be called using the assertion facility
052: * in the client code:
053: * <pre>
054: * MapToObjectKeyCharMapAdapter s;
055: * ...
056: * <b>assert</b> s.validate();
057: * </pre>
058: * or by letting the adapter throw an exception on illegal values:
059: * <pre>
060: * MapToObjectKeyCharMapAdapter s;
061: * ...
062: * s.evalidate(); // Throws an exception on illegal values
063: * </pre>
064: * Either way, validation must be invoked directly by the client
065: * code.
066: *
067: * @author Søren Bak
068: * @version 1.1 21-08-2003 19:12
069: * @since 1.1
070: */
071: public class MapToObjectKeyCharMapAdapter extends
072: AbstractObjectKeyCharMap implements ObjectKeyCharMap {
073:
074: /** The underlying map. */
075: protected Map map;
076:
077: /** The value corresponding to the last key found by containsKey(). */
078: protected Character lastValue;
079:
080: /**
081: * Creates a new adaption to a map from object
082: * values to char values.
083: *
084: * @param map
085: * the underlying map. This map must
086: * consist of
087: * values of class
088: * {@link Character Character}. Otherwise a
089: * {@link ClassCastException ClassCastException}
090: * will be thrown by some methods.
091: *
092: * @throws NullPointerException
093: * if <tt>map</tt> is <tt>null</tt>.
094: */
095: public MapToObjectKeyCharMapAdapter(Map map) {
096: if (map == null)
097: Exceptions.nullArgument("map");
098: this .map = map;
099: lastValue = null;
100: }
101:
102: /**
103: * Creates a new adaption to a map from object
104: * values to char values. The map to adapt is optionally validated.
105: *
106: * @param map
107: * the underlying map. This map must
108: * consist of
109: * values of class
110: * {@link Character Character}. Otherwise a
111: * {@link ClassCastException ClassCastException}
112: * will be thrown by some methods.
113: *
114: * @param validate
115: * indicates whether <tt>map</tt> should
116: * be checked for illegal values.
117: *
118: * @throws NullPointerException
119: * if <tt>map</tt> is <tt>null</tt>.
120: *
121: * @throws IllegalStateException
122: * if <tt>validate</tt> is <tt>true</tt> and
123: * <tt>map</tt> contains a <tt>null</tt> value,
124: * or a value that is not of class
125: * {@link Character Character}.
126: */
127: public MapToObjectKeyCharMapAdapter(Map map, boolean validate) {
128: if (map == null)
129: Exceptions.nullArgument("map");
130: this .map = map;
131: lastValue = null;
132: if (validate)
133: evalidate();
134: }
135:
136: public void clear() {
137: map.clear();
138: }
139:
140: public boolean containsKey(Object key) {
141: lastValue = (Character) map.get(key);
142: return lastValue != null;
143: }
144:
145: public boolean containsValue(char value) {
146: return map.containsValue(new Character(value));
147: }
148:
149: public ObjectKeyCharMapIterator entries() {
150: return new ObjectKeyCharMapIterator() {
151: Iterator i = map.entrySet().iterator();
152: Map.Entry lastEntry = null;
153:
154: public boolean hasNext() {
155: return i.hasNext();
156: }
157:
158: public void next() {
159: lastEntry = (Map.Entry) i.next();
160: }
161:
162: public Object getKey() {
163: if (lastEntry == null)
164: Exceptions.noElementToGet();
165: return lastEntry.getKey();
166: }
167:
168: public char getValue() {
169: if (lastEntry == null)
170: Exceptions.noElementToGet();
171: return ((Character) lastEntry.getValue()).charValue();
172: }
173:
174: public void remove() {
175: i.remove();
176: lastEntry = null;
177: }
178: };
179: }
180:
181: public char get(Object key) {
182: Character value = (Character) map.get(key);
183: return value == null ? MapDefaults.defaultChar() : value
184: .charValue();
185: }
186:
187: public Set keySet() {
188: return map.keySet();
189: }
190:
191: public char lget() {
192: if (lastValue == null)
193: Exceptions.noLastElement();
194: return lastValue.charValue();
195: }
196:
197: public char put(Object key, char value) {
198: Character oldValue = (Character) map.put(key, new Character(
199: value));
200: return oldValue == null ? MapDefaults.defaultChar() : oldValue
201: .charValue();
202: }
203:
204: public char remove(Object key) {
205: Character value = (Character) map.remove(key);
206: return value == null ? MapDefaults.defaultChar() : value
207: .charValue();
208: }
209:
210: public int size() {
211: return map.size();
212: }
213:
214: public CharCollection values() {
215: return new CollectionToCharCollectionAdapter(map.values());
216: }
217:
218: public char tget(Object key) {
219: Character value = (Character) map.get(key);
220: if (value == null)
221: Exceptions.noSuchMapping(key);
222: return value.charValue();
223: }
224:
225: /**
226: * Indicates whether the underlying map is valid for
227: * this adapter. For the underlying map to be valid it
228: * can contain no <tt>null</tt>
229: * values and only {@link Character Character} values.
230: *
231: * @return <tt>true</tt> if the underlying map is
232: * valid; returns <tt>false</tt> otherwise.
233: */
234: public boolean validate() {
235: return Adapter.isObjectKeyCharAdaptable(map);
236: }
237:
238: /**
239: * Validates the map underlying this adapter and throws
240: * an exception if it is invalid. For the underlying map to be valid it
241: * can contain no <tt>null</tt>
242: * values and only {@link Character Character} values.
243: *
244: * @throws IllegalStateException
245: * if the underlying map is invalid.
246: */
247: public void evalidate() {
248: if (!validate())
249: Exceptions.cannotAdapt("map");
250: }
251:
252: }
|