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.Iterator;
023: import java.util.Map;
024:
025: import org.apache.commons.collections.Predicate;
026:
027: /**
028: * Decorates another <code>Map</code> to validate that additions
029: * match a specified predicate.
030: * <p>
031: * This map exists to provide validation for the decorated map.
032: * It is normally created to decorate an empty map.
033: * If an object cannot be added to the map, an IllegalArgumentException is thrown.
034: * <p>
035: * One usage would be to ensure that no null keys are added to the map.
036: * <pre>Map map = PredicatedSet.decorate(new HashMap(), NotNullPredicate.INSTANCE, null);</pre>
037: * <p>
038: * <strong>Note that PredicatedMap is not synchronized and is not thread-safe.</strong>
039: * If you wish to use this map from multiple threads concurrently, you must use
040: * appropriate synchronization. The simplest approach is to wrap this map
041: * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw
042: * exceptions when accessed by concurrent threads without synchronization.
043: * <p>
044: * This class is Serializable from Commons Collections 3.1.
045: *
046: * @since Commons Collections 3.0
047: * @version $Revision: 348007 $ $Date: 2005-11-21 22:52:57 +0000 (Mon, 21 Nov 2005) $
048: *
049: * @author Stephen Colebourne
050: * @author Paul Jack
051: */
052: public class PredicatedMap extends AbstractInputCheckedMapDecorator
053: implements Serializable {
054:
055: /** Serialization version */
056: private static final long serialVersionUID = 7412622456128415156L;
057:
058: /** The key predicate to use */
059: protected final Predicate keyPredicate;
060: /** The value predicate to use */
061: protected final Predicate valuePredicate;
062:
063: /**
064: * Factory method to create a predicated (validating) map.
065: * <p>
066: * If there are any elements already in the list being decorated, they
067: * are validated.
068: *
069: * @param map the map to decorate, must not be null
070: * @param keyPredicate the predicate to validate the keys, null means no check
071: * @param valuePredicate the predicate to validate to values, null means no check
072: * @throws IllegalArgumentException if the map is null
073: */
074: public static Map decorate(Map map, Predicate keyPredicate,
075: Predicate valuePredicate) {
076: return new PredicatedMap(map, keyPredicate, valuePredicate);
077: }
078:
079: //-----------------------------------------------------------------------
080: /**
081: * Constructor that wraps (not copies).
082: *
083: * @param map the map to decorate, must not be null
084: * @param keyPredicate the predicate to validate the keys, null means no check
085: * @param valuePredicate the predicate to validate to values, null means no check
086: * @throws IllegalArgumentException if the map is null
087: */
088: protected PredicatedMap(Map map, Predicate keyPredicate,
089: Predicate valuePredicate) {
090: super (map);
091: this .keyPredicate = keyPredicate;
092: this .valuePredicate = valuePredicate;
093:
094: Iterator it = map.entrySet().iterator();
095: while (it.hasNext()) {
096: Map.Entry entry = (Map.Entry) it.next();
097: Object key = entry.getKey();
098: Object value = entry.getValue();
099: validate(key, value);
100: }
101: }
102:
103: //-----------------------------------------------------------------------
104: /**
105: * Write the map out using a custom routine.
106: *
107: * @param out the output stream
108: * @throws IOException
109: * @since Commons Collections 3.1
110: */
111: private void writeObject(ObjectOutputStream out) throws IOException {
112: out.defaultWriteObject();
113: out.writeObject(map);
114: }
115:
116: /**
117: * Read the map in using a custom routine.
118: *
119: * @param in the input stream
120: * @throws IOException
121: * @throws ClassNotFoundException
122: * @since Commons Collections 3.1
123: */
124: private void readObject(ObjectInputStream in) throws IOException,
125: ClassNotFoundException {
126: in.defaultReadObject();
127: map = (Map) in.readObject();
128: }
129:
130: //-----------------------------------------------------------------------
131: /**
132: * Validates a key value pair.
133: *
134: * @param key the key to validate
135: * @param value the value to validate
136: * @throws IllegalArgumentException if invalid
137: */
138: protected void validate(Object key, Object value) {
139: if (keyPredicate != null && keyPredicate.evaluate(key) == false) {
140: throw new IllegalArgumentException(
141: "Cannot add key - Predicate rejected it");
142: }
143: if (valuePredicate != null
144: && valuePredicate.evaluate(value) == false) {
145: throw new IllegalArgumentException(
146: "Cannot add value - Predicate rejected it");
147: }
148: }
149:
150: /**
151: * Override to validate an object set into the map via <code>setValue</code>.
152: *
153: * @param value the value to validate
154: * @throws IllegalArgumentException if invalid
155: * @since Commons Collections 3.1
156: */
157: protected Object checkSetValue(Object value) {
158: if (valuePredicate.evaluate(value) == false) {
159: throw new IllegalArgumentException(
160: "Cannot set value - Predicate rejected it");
161: }
162: return value;
163: }
164:
165: /**
166: * Override to only return true when there is a value transformer.
167: *
168: * @return true if a value predicate is in use
169: * @since Commons Collections 3.1
170: */
171: protected boolean isSetValueChecking() {
172: return (valuePredicate != null);
173: }
174:
175: //-----------------------------------------------------------------------
176: public Object put(Object key, Object value) {
177: validate(key, value);
178: return map.put(key, value);
179: }
180:
181: public void putAll(Map mapToCopy) {
182: Iterator it = mapToCopy.entrySet().iterator();
183: while (it.hasNext()) {
184: Map.Entry entry = (Map.Entry) it.next();
185: Object key = entry.getKey();
186: Object value = entry.getValue();
187: validate(key, value);
188: }
189: map.putAll(mapToCopy);
190: }
191:
192: }
|