001: /*
002: * Copyright 2003-2004 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.Map;
023:
024: import org.apache.commons.collections.Factory;
025: import org.apache.commons.collections.Transformer;
026: import org.apache.commons.collections.functors.FactoryTransformer;
027:
028: /**
029: * Decorates another <code>Map</code> to create objects in the map on demand.
030: * <p>
031: * When the {@link #get(Object)} method is called with a key that does not
032: * exist in the map, the factory is used to create the object. The created
033: * object will be added to the map using the requested key.
034: * <p>
035: * For instance:
036: * <pre>
037: * Factory factory = new Factory() {
038: * public Object create() {
039: * return new Date();
040: * }
041: * }
042: * Map lazy = Lazy.map(new HashMap(), factory);
043: * Object obj = lazy.get("NOW");
044: * </pre>
045: *
046: * After the above code is executed, <code>obj</code> will contain
047: * a new <code>Date</code> instance. Furthermore, that <code>Date</code>
048: * instance is mapped to the "NOW" key in the map.
049: * <p>
050: * <strong>Note that LazyMap is not synchronized and is not thread-safe.</strong>
051: * If you wish to use this map from multiple threads concurrently, you must use
052: * appropriate synchronization. The simplest approach is to wrap this map
053: * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw
054: * exceptions when accessed by concurrent threads without synchronization.
055: * <p>
056: * This class is Serializable from Commons Collections 3.1.
057: *
058: * @since Commons Collections 3.0
059: * @version $Revision: 348007 $ $Date: 2005-11-21 22:52:57 +0000 (Mon, 21 Nov 2005) $
060: *
061: * @author Stephen Colebourne
062: * @author Paul Jack
063: */
064: public class LazyMap extends AbstractMapDecorator implements Map,
065: Serializable {
066:
067: /** Serialization version */
068: private static final long serialVersionUID = 7990956402564206740L;
069:
070: /** The factory to use to construct elements */
071: protected final Transformer factory;
072:
073: /**
074: * Factory method to create a lazily instantiated map.
075: *
076: * @param map the map to decorate, must not be null
077: * @param factory the factory to use, must not be null
078: * @throws IllegalArgumentException if map or factory is null
079: */
080: public static Map decorate(Map map, Factory factory) {
081: return new LazyMap(map, factory);
082: }
083:
084: /**
085: * Factory method to create a lazily instantiated map.
086: *
087: * @param map the map to decorate, must not be null
088: * @param factory the factory to use, must not be null
089: * @throws IllegalArgumentException if map or factory is null
090: */
091: public static Map decorate(Map map, Transformer factory) {
092: return new LazyMap(map, factory);
093: }
094:
095: //-----------------------------------------------------------------------
096: /**
097: * Constructor that wraps (not copies).
098: *
099: * @param map the map to decorate, must not be null
100: * @param factory the factory to use, must not be null
101: * @throws IllegalArgumentException if map or factory is null
102: */
103: protected LazyMap(Map map, Factory factory) {
104: super (map);
105: if (factory == null) {
106: throw new IllegalArgumentException(
107: "Factory must not be null");
108: }
109: this .factory = FactoryTransformer.getInstance(factory);
110: }
111:
112: /**
113: * Constructor that wraps (not copies).
114: *
115: * @param map the map to decorate, must not be null
116: * @param factory the factory to use, must not be null
117: * @throws IllegalArgumentException if map or factory is null
118: */
119: protected LazyMap(Map map, Transformer factory) {
120: super (map);
121: if (factory == null) {
122: throw new IllegalArgumentException(
123: "Factory must not be null");
124: }
125: this .factory = factory;
126: }
127:
128: //-----------------------------------------------------------------------
129: /**
130: * Write the map out using a custom routine.
131: *
132: * @param out the output stream
133: * @throws IOException
134: * @since Commons Collections 3.1
135: */
136: private void writeObject(ObjectOutputStream out) throws IOException {
137: out.defaultWriteObject();
138: out.writeObject(map);
139: }
140:
141: /**
142: * Read the map in using a custom routine.
143: *
144: * @param in the input stream
145: * @throws IOException
146: * @throws ClassNotFoundException
147: * @since Commons Collections 3.1
148: */
149: private void readObject(ObjectInputStream in) throws IOException,
150: ClassNotFoundException {
151: in.defaultReadObject();
152: map = (Map) in.readObject();
153: }
154:
155: //-----------------------------------------------------------------------
156: public Object get(Object key) {
157: // create value for key if key is not currently in the map
158: if (map.containsKey(key) == false) {
159: Object value = factory.transform(key);
160: map.put(key, value);
161: return value;
162: }
163: return map.get(key);
164: }
165:
166: // no need to wrap keySet, entrySet or values as they are views of
167: // existing map entries - you can't do a map-style get on them.
168: }
|