001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010:
011: package org.mmbase.util;
012:
013: import java.util.*;
014:
015: /**
016: * Combines to Maps to one new map. One map is 'leading' and determins wich keys are mapped. The second map can override values, if it contains the same mapping.
017: * There are several ways to describe what must happen with <em>changes</em> on the map.
018: * You can e.g. maintain original values of a map <code>values</code> like so:
019: <pre>
020: Map originals = new HashMap();
021: Map wrapper = new LinkMap(values, originals, LinkMap.Changes.CONSERVE);
022:
023: wrapper.put(key, value);
024:
025: Object newValue = values.get(key);
026: Object originalValue = originals.get(key);
027: assert originalValue == wrapper.get(key);
028: </pre>
029: * Changes on a map can be made temporay by wrapping it like this:
030: <pre>
031: Map wrapper = new LinkMap(values, new HashMap(), LinkMap.Changes.SECOND);
032: wrapper.put(key, value);
033: Object newValue = wrapper.get(key);
034: Object oldValue = values.get(key);
035: assert value == newValue;
036:
037: </pre>
038: *
039: * @author Michiel Meeuwissen
040: * @version $Id: LinkMap.java,v 1.3 2007/12/05 20:40:01 michiel Exp $
041: * @since MMBase-1.9
042: */
043: public class LinkMap<K, V> extends AbstractMap<K, V> {
044:
045: /**
046: * Enum for the parameter of the constructor {@link LinkMap#LinkMap(Map, Map, Changes)}
047: */
048: public enum Changes {
049: /**
050: * Changes are reflected in the 'first' map only. So they remain invisible if second maps contains the same key.
051: */
052: FIRST,
053: /**
054: * Changes are reflected in the 'second' map, map1 remains unmodified.
055: */
056: SECOND,
057: /**
058: * Changes are reflected in the both maps
059: */
060: BOTH,
061: /**
062: * Changes are reflected in the first map, but before that, the <em>old</em> value is copied to the second map (unless a mapping is alreayd in that map).
063: * This effectively creates a unmodifiable map, but the wrapped map is modified anyways.
064: */
065: CONSERVE,
066: /**
067: * No changes are allowed. The map behaves as an unmodifiable map (throwing {@link UnsupportedOperationException})
068: */
069: NONE;
070: }
071:
072: private final Changes changes;
073: private final Map<K, V> map1;
074: private final Map<K, V> map2;
075:
076: /**
077: * Creates a (modifiable) Linked Map. What precisely happens on modification is ruled by the <code>c</code> parameter.
078: * @see Changes#FIRST
079: * @see Changes#SECOND
080: * @see Changes#BOTH
081: * @see Changes#CONSERVE
082: * @see Changes#NONE
083: */
084: public LinkMap(Map<K, V> m1, Map<K, V> m2, Changes c) {
085: map1 = m1;
086: map2 = m2;
087: changes = c;
088: }
089:
090: /**
091: * Creates an unmodifiable Linked Map
092: */
093: public LinkMap(Map<K, V> m1, Map<K, V> m2) {
094: this (m1, m2, Changes.NONE);
095: }
096:
097: public Set<Map.Entry<K, V>> entrySet() {
098: return new AbstractSet<Map.Entry<K, V>>() {
099: public Iterator<Map.Entry<K, V>> iterator() {
100: final Iterator<Map.Entry<K, V>> i = map1.entrySet()
101: .iterator();
102: return new Iterator<Map.Entry<K, V>>() {
103: public boolean hasNext() {
104: return i.hasNext();
105: }
106:
107: public Map.Entry<K, V> next() {
108: final Map.Entry<K, V> entry1 = i.next();
109: final K key = entry1.getKey();
110: return new Map.Entry<K, V>() {
111: public K getKey() {
112: return key;
113: }
114:
115: public V getValue() {
116: if (map2.containsKey(key)) {
117: return map2.get(key);
118: } else {
119: return entry1.getValue();
120: }
121: }
122:
123: public V setValue(V v) {
124: return LinkMap.this .put(key, v);
125: }
126: };
127: }
128:
129: public void remove() {
130: throw new UnsupportedOperationException();
131: }
132: };
133: }
134:
135: public int size() {
136: return map1.size();
137: }
138: };
139: }
140:
141: public int size() {
142: return map1.size();
143: }
144:
145: public V get(Object key) {
146: if (map2.containsKey(key)) {
147: return map2.get(key);
148: } else {
149: return map1.get(key);
150: }
151: }
152:
153: public V put(K key, V v) {
154: V r = get(key);
155: switch (changes) {
156: case FIRST:
157: map1.put(key, v);
158: break;
159: case SECOND:
160: map2.put(key, v);
161: break;
162: case BOTH:
163: map1.put(key, v);
164: map2.put(key, v);
165: break;
166: case CONSERVE:
167: if (!map2.containsKey(key)) {
168: map2.put(key, r);
169: }
170: map1.put(key, v);
171: break;
172: case NONE:
173: throw new UnsupportedOperationException();
174: }
175: return r;
176: }
177:
178: public boolean containsKey(Object key) {
179: return map1.containsKey(key);
180: }
181:
182: public static void main(String[] args) {
183: System.out.println("Please run with -ea");
184: Map<String, String> values = new HashMap<String, String>();
185: values.put("a", "A");
186: values.put("b", "B");
187:
188: System.out.println("values: " + values);
189: {
190: final String key = "b";
191: final String value = "C";
192: Map<String, String> originals = new HashMap<String, String>();
193: Map<String, String> wrapper = new LinkMap<String, String>(
194: values, originals, LinkMap.Changes.CONSERVE);
195:
196: wrapper.put(key, value);
197:
198: Object newValue = values.get(key);
199: Object originalValue = originals.get(key);
200: assert originalValue == wrapper.get(key);
201: assert newValue == value;
202: assert originalValue.equals("B");
203: System.out.println("wrapper: " + wrapper);
204: System.out.println("originals: " + originals);
205: System.out.println("values: " + values);
206: }
207:
208: {
209: final String key = "b";
210: final String value = "D";
211: Map<String, String> wrapper = new LinkMap<String, String>(
212: values, new HashMap<String, String>(),
213: LinkMap.Changes.SECOND);
214: wrapper.put(key, value);
215: Object newValue = wrapper.get(key);
216: Object oldValue = values.get(key);
217: assert value == newValue;
218: assert newValue.equals("D");
219: assert oldValue.equals("C");
220: System.out.println("wrapper: " + wrapper);
221: System.out.println("values: " + values);
222: }
223:
224: }
225: }
|