001: /*
002: * Copyright 2003-2005 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.collections.map;
017:
018: import java.io.IOException;
019: import java.io.ObjectInputStream;
020: import java.io.ObjectOutputStream;
021: import java.io.Serializable;
022: import java.util.Iterator;
023: import java.util.Map;
024:
025: import org.apache.commons.collections.Transformer;
026:
027: /**
028: * Decorates another <code>Map</code> to transform objects that are added.
029: * <p>
030: * The Map put methods and Map.Entry setValue method are affected by this class.
031: * Thus objects must be removed or searched for using their transformed form.
032: * For example, if the transformation converts Strings to Integers, you must
033: * use the Integer form to remove objects.
034: * <p>
035: * <strong>Note that TransformedMap is not synchronized and is not thread-safe.</strong>
036: * If you wish to use this map from multiple threads concurrently, you must use
037: * appropriate synchronization. The simplest approach is to wrap this map
038: * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw
039: * exceptions when accessed by concurrent threads without synchronization.
040: * <p>
041: * This class is Serializable from Commons Collections 3.1.
042: *
043: * @since Commons Collections 3.0
044: * @version $Revision: 348013 $ $Date: 2005-11-21 23:24:45 +0000 (Mon, 21 Nov 2005) $
045: *
046: * @author Stephen Colebourne
047: */
048: public class TransformedMap extends AbstractInputCheckedMapDecorator
049: implements Serializable {
050:
051: /** Serialization version */
052: private static final long serialVersionUID = 7023152376788900464L;
053:
054: /** The transformer to use for the key */
055: protected final Transformer keyTransformer;
056: /** The transformer to use for the value */
057: protected final Transformer valueTransformer;
058:
059: /**
060: * Factory method to create a transforming map.
061: * <p>
062: * If there are any elements already in the map being decorated, they
063: * are NOT transformed.
064: * Constrast this with {@link #decorateTransform}.
065: *
066: * @param map the map to decorate, must not be null
067: * @param keyTransformer the transformer to use for key conversion, null means no transformation
068: * @param valueTransformer the transformer to use for value conversion, null means no transformation
069: * @throws IllegalArgumentException if map is null
070: */
071: public static Map decorate(Map map, Transformer keyTransformer,
072: Transformer valueTransformer) {
073: return new TransformedMap(map, keyTransformer, valueTransformer);
074: }
075:
076: /**
077: * Factory method to create a transforming map that will transform
078: * existing contents of the specified map.
079: * <p>
080: * If there are any elements already in the map being decorated, they
081: * will be transformed by this method.
082: * Constrast this with {@link #decorate}.
083: *
084: * @param map the map to decorate, must not be null
085: * @param keyTransformer the transformer to use for key conversion, null means no transformation
086: * @param valueTransformer the transformer to use for value conversion, null means no transformation
087: * @throws IllegalArgumentException if map is null
088: * @since Commons Collections 3.2
089: */
090: public static Map decorateTransform(Map map,
091: Transformer keyTransformer, Transformer valueTransformer) {
092: TransformedMap decorated = new TransformedMap(map,
093: keyTransformer, valueTransformer);
094: if (map.size() > 0) {
095: Map transformed = decorated.transformMap(map);
096: decorated.clear();
097: decorated.getMap().putAll(transformed); // avoids double transformation
098: }
099: return decorated;
100: }
101:
102: //-----------------------------------------------------------------------
103: /**
104: * Constructor that wraps (not copies).
105: * <p>
106: * If there are any elements already in the collection being decorated, they
107: * are NOT transformed.
108: *
109: * @param map the map to decorate, must not be null
110: * @param keyTransformer the transformer to use for key conversion, null means no conversion
111: * @param valueTransformer the transformer to use for value conversion, null means no conversion
112: * @throws IllegalArgumentException if map is null
113: */
114: protected TransformedMap(Map map, Transformer keyTransformer,
115: Transformer valueTransformer) {
116: super (map);
117: this .keyTransformer = keyTransformer;
118: this .valueTransformer = valueTransformer;
119: }
120:
121: //-----------------------------------------------------------------------
122: /**
123: * Write the map out using a custom routine.
124: *
125: * @param out the output stream
126: * @throws IOException
127: * @since Commons Collections 3.1
128: */
129: private void writeObject(ObjectOutputStream out) throws IOException {
130: out.defaultWriteObject();
131: out.writeObject(map);
132: }
133:
134: /**
135: * Read the map in using a custom routine.
136: *
137: * @param in the input stream
138: * @throws IOException
139: * @throws ClassNotFoundException
140: * @since Commons Collections 3.1
141: */
142: private void readObject(ObjectInputStream in) throws IOException,
143: ClassNotFoundException {
144: in.defaultReadObject();
145: map = (Map) in.readObject();
146: }
147:
148: //-----------------------------------------------------------------------
149: /**
150: * Transforms a key.
151: * <p>
152: * The transformer itself may throw an exception if necessary.
153: *
154: * @param object the object to transform
155: * @throws the transformed object
156: */
157: protected Object transformKey(Object object) {
158: if (keyTransformer == null) {
159: return object;
160: }
161: return keyTransformer.transform(object);
162: }
163:
164: /**
165: * Transforms a value.
166: * <p>
167: * The transformer itself may throw an exception if necessary.
168: *
169: * @param object the object to transform
170: * @throws the transformed object
171: */
172: protected Object transformValue(Object object) {
173: if (valueTransformer == null) {
174: return object;
175: }
176: return valueTransformer.transform(object);
177: }
178:
179: /**
180: * Transforms a map.
181: * <p>
182: * The transformer itself may throw an exception if necessary.
183: *
184: * @param map the map to transform
185: * @throws the transformed object
186: */
187: protected Map transformMap(Map map) {
188: if (map.isEmpty()) {
189: return map;
190: }
191: Map result = new LinkedMap(map.size());
192: for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
193: Map.Entry entry = (Map.Entry) it.next();
194: result.put(transformKey(entry.getKey()),
195: transformValue(entry.getValue()));
196: }
197: return result;
198: }
199:
200: /**
201: * Override to transform the value when using <code>setValue</code>.
202: *
203: * @param value the value to transform
204: * @return the transformed value
205: * @since Commons Collections 3.1
206: */
207: protected Object checkSetValue(Object value) {
208: return valueTransformer.transform(value);
209: }
210:
211: /**
212: * Override to only return true when there is a value transformer.
213: *
214: * @return true if a value transformer is in use
215: * @since Commons Collections 3.1
216: */
217: protected boolean isSetValueChecking() {
218: return (valueTransformer != null);
219: }
220:
221: //-----------------------------------------------------------------------
222: public Object put(Object key, Object value) {
223: key = transformKey(key);
224: value = transformValue(value);
225: return getMap().put(key, value);
226: }
227:
228: public void putAll(Map mapToCopy) {
229: mapToCopy = transformMap(mapToCopy);
230: getMap().putAll(mapToCopy);
231: }
232:
233: }
|