scolebourne 2004/05/03 14:48:49
Modified: collections/src/java/org/apache/commons/collections/map
TransformedMap.java PredicatedMap.java
collections RELEASE-NOTES.html
Added: collections/src/java/org/apache/commons/collections/map
AbstractInputCheckedMapDecorator.java
Log:
Add AbstractInputCheckedMapDecorator
Revision Changes Path
1.9 +26 -109
jakarta-commons/collections/src/java/org/apache/commons/collections/map/TransformedMap.java
Index: TransformedMap.java
===================================================================
RCS file:
/home/cvs/jakarta-commons/collections/src/java/org/apache/commons/collections/map/TransformedMap.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- TransformedMap.java 9 Apr 2004 10:36:01 -0000 1.8
+++ TransformedMap.java 3 May 2004 21:48:49 -0000 1.9
@@ -19,16 +19,10 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
-import java.lang.reflect.Array;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import java.util.Set;
import org.apache.commons.collections.Transformer;
-import org.apache.commons.collections.collection.AbstractCollectionDecorator;
-import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
-import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
/**
* Decorates another <code>Map</code> to transform objects that are added.
@@ -46,7 +40,7 @@
* @author Stephen Colebourne
*/
public class TransformedMap
- extends AbstractMapDecorator
+ extends AbstractInputCheckedMapDecorator
implements Serializable {
/** Serialization version */
@@ -117,6 +111,10 @@
}
//-----------------------------------------------------------------------
+ // The transformKey/transformValue/transformMap methods exist for backwards
+ // compatability - in an ideal world, they wouldn't and the superclass
+ // methods checkPutKey/checkPutValue would be overridden instead
+
/**
* Transforms a key.
* <p>
@@ -156,7 +154,7 @@
* @throws the transformed object
*/
protected Map transformMap(Map map) {
- Map result = new HashMap(map.size());
+ Map result = new LinkedMap(map.size());
for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next();
result.put(transformKey(entry.getKey()),
transformValue(entry.getValue()));
@@ -164,6 +162,25 @@
return result;
}
+ /**
+ * Override to transform the value when using <code>setValue</code>.
+ *
+ * @param value the value to transform
+ * @return the transformed value
+ */
+ protected Object checkSetValue(Object value) {
+ return valueTransformer.transform(value);
+ }
+
+ /**
+ * Override to only return true when there is a value transformer.
+ *
+ * @return true if a value transformer is in use
+ */
+ protected boolean isSetValueChecking() {
+ return (valueTransformer != null);
+ }
+
//-----------------------------------------------------------------------
public Object put(Object key, Object value) {
key = transformKey(key);
@@ -174,106 +191,6 @@
public void putAll(Map mapToCopy) {
mapToCopy = transformMap(mapToCopy);
getMap().putAll(mapToCopy);
- }
-
- public Set entrySet() {
- if (valueTransformer == null) {
- return map.entrySet();
- }
- return new TransformedMapEntrySet(map.entrySet(), valueTransformer);
- }
-
-
- //-----------------------------------------------------------------------
- /**
- * Implementation of an entry set that uses a transforming map entry.
- */
- static class TransformedMapEntrySet extends AbstractCollectionDecorator
implements Set {
-
- /** The transformer to use */
- private final Transformer valueTransformer;
-
- protected TransformedMapEntrySet(Set set, Transformer valueTransformer) {
- super(set);
- this.valueTransformer = valueTransformer;
- }
-
- public Iterator iterator() {
- return new TransformedMapEntrySetIterator(collection.iterator(),
valueTransformer);
- }
-
- public Object[] toArray() {
- Object[] array = collection.toArray();
- for (int i = 0; i < array.length; i++) {
- array[i] = new TransformedMapEntry((Map.Entry) array[i],
valueTransformer);
- }
- return array;
- }
-
- public Object[] toArray(Object array[]) {
- Object[] result = array;
- if (array.length > 0) {
- // we must create a new array to handle multi-threaded situations
- // where another thread could access data before we decorate it
- result = (Object[])
Array.newInstance(array.getClass().getComponentType(), 0);
- }
- result = collection.toArray(result);
- for (int i = 0; i < result.length; i++) {
- result[i] = new TransformedMapEntry((Map.Entry) result[i],
valueTransformer);
- }
-
- // check to see if result should be returned straight
- if (result.length > array.length) {
- return result;
- }
-
- // copy back into input array to fulfil the method contract
- System.arraycopy(result, 0, array, 0, result.length);
- if (array.length > result.length) {
- array[result.length] = null;
- }
- return array;
- }
- }
-
- /**
- * Implementation of an entry set iterator.
- */
- static class TransformedMapEntrySetIterator extends AbstractIteratorDecorator {
-
- /** The transformer to use */
- private final Transformer valueTransformer;
-
- protected TransformedMapEntrySetIterator(Iterator iterator, Transformer
valueTransformer) {
- super(iterator);
- this.valueTransformer = valueTransformer;
- }
-
- public Object next() {
- Map.Entry entry = (Map.Entry) iterator.next();
- return new TransformedMapEntry(entry, valueTransformer);
- }
- }
-
- /**
- * Implementation of a map entry that transforms additions.
- */
- static class TransformedMapEntry extends AbstractMapEntryDecorator {
-
- /** The transformer to use */
- private final Transformer valueTransformer;
-
- protected TransformedMapEntry(Map.Entry entry, Transformer
valueTransformer) {
- super(entry);
- this.valueTransformer = valueTransformer;
- }
-
- public Object setValue(Object object) {
- if (valueTransformer != null) {
- object = valueTransformer.transform(object);
- }
- return entry.setValue(object);
- }
}
}
1.10 +36 -107
jakarta-commons/collections/src/java/org/apache/commons/collections/map/PredicatedMap.java
Index: PredicatedMap.java
===================================================================
RCS file:
/home/cvs/jakarta-commons/collections/src/java/org/apache/commons/collections/map/PredicatedMap.java,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- PredicatedMap.java 9 Apr 2004 10:36:01 -0000 1.9
+++ PredicatedMap.java 3 May 2004 21:48:49 -0000 1.10
@@ -19,15 +19,10 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
-import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.Map;
-import java.util.Set;
import org.apache.commons.collections.Predicate;
-import org.apache.commons.collections.collection.AbstractCollectionDecorator;
-import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
-import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
/**
* Decorates another <code>Map</code> to validate that additions
@@ -45,7 +40,7 @@
* @author Paul Jack
*/
public class PredicatedMap
- extends AbstractMapDecorator
+ extends AbstractInputCheckedMapDecorator
implements Serializable {
/** Serialization version */
@@ -94,6 +89,13 @@
}
}
+ /**
+ * Validates a key value pair.
+ *
+ * @param key the key to validate
+ * @param value the value to validate
+ * @throws IllegalArgumentException if invalid
+ */
protected void validate(Object key, Object value) {
if (keyPredicate != null && keyPredicate.evaluate(key) == false) {
throw new IllegalArgumentException("Cannot add key - Predicate rejected
it");
@@ -130,6 +132,33 @@
}
//-----------------------------------------------------------------------
+ // The validate method exists for backwards compatability - in an ideal
+ // world, it wouldn't and the superclass methods checkPutKey/checkPutValue
+ // would be overridden instead
+
+ /**
+ * Override to validate an object set into the map via <code>setValue</code>.
+ *
+ * @param value the value to validate
+ * @throws IllegalArgumentException if invalid
+ */
+ protected Object checkSetValue(Object value) {
+ if (valuePredicate.evaluate(value) == false) {
+ throw new IllegalArgumentException("Cannot set value - Predicate
rejected it");
+ }
+ return value;
+ }
+
+ /**
+ * Override to only return true when there is a value transformer.
+ *
+ * @return true if a value predicate is in use
+ */
+ protected boolean isSetValueChecking() {
+ return (valuePredicate != null);
+ }
+
+ //-----------------------------------------------------------------------
public Object put(Object key, Object value) {
validate(key, value);
return map.put(key, value);
@@ -144,106 +173,6 @@
validate(key, value);
}
map.putAll(mapToCopy);
- }
-
- public Set entrySet() {
- if (valuePredicate == null) {
- return map.entrySet();
- }
- return new PredicatedMapEntrySet(map.entrySet(), valuePredicate);
- }
-
-
- //-----------------------------------------------------------------------
- /**
- * Implementation of an entry set that checks (predicates) additions.
- */
- static class PredicatedMapEntrySet extends AbstractCollectionDecorator
implements Set {
-
- /** The predicate to use */
- private final Predicate valuePredicate;
-
- protected PredicatedMapEntrySet(Set set, Predicate valuePred) {
- super(set);
- this.valuePredicate = valuePred;
- }
-
- public Iterator iterator() {
- return new PredicatedMapEntrySetIterator(collection.iterator(),
valuePredicate);
- }
-
- public Object[] toArray() {
- Object[] array = collection.toArray();
- for (int i = 0; i < array.length; i++) {
- array[i] = new PredicatedMapEntry((Map.Entry) array[i],
valuePredicate);
- }
- return array;
- }
-
- public Object[] toArray(Object array[]) {
- Object[] result = array;
- if (array.length > 0) {
- // we must create a new array to handle multi-threaded situations
- // where another thread could access data before we decorate it
- result = (Object[])
Array.newInstance(array.getClass().getComponentType(), 0);
- }
- result = collection.toArray(result);
- for (int i = 0; i < result.length; i++) {
- result[i] = new PredicatedMapEntry((Map.Entry) result[i],
valuePredicate);
- }
-
- // check to see if result should be returned straight
- if (result.length > array.length) {
- return result;
- }
-
- // copy back into input array to fulfil the method contract
- System.arraycopy(result, 0, array, 0, result.length);
- if (array.length > result.length) {
- array[result.length] = null;
- }
- return array;
- }
- }
-
- /**
- * Implementation of an entry set iterator.
- */
- static class PredicatedMapEntrySetIterator extends AbstractIteratorDecorator {
-
- /** The predicate to use */
- private final Predicate valuePredicate;
-
- protected PredicatedMapEntrySetIterator(Iterator iterator, Predicate
valuePredicate) {
- super(iterator);
- this.valuePredicate = valuePredicate;
- }
-
- public Object next() {
- Map.Entry entry = (Map.Entry) iterator.next();
- return new PredicatedMapEntry(entry, valuePredicate);
- }
- }
-
- /**
- * Implementation of a map entry that checks (predicates) additions.
- */
- static class PredicatedMapEntry extends AbstractMapEntryDecorator {
-
- /** The predicate to use */
- private final Predicate predicate;
-
- protected PredicatedMapEntry(Map.Entry entry, Predicate valuePredicate) {
- super(entry);
- this.predicate = valuePredicate;
- }
-
- public Object setValue(Object obj) {
- if (predicate != null && predicate.evaluate(obj) == false) {
- throw new IllegalArgumentException("Cannot set value - Predicate
rejected it");
- }
- return entry.setValue(obj);
- }
}
}
1.1
jakarta-commons/collections/src/java/org/apache/commons/collections/map/AbstractInputCheckedMapDecorator.java
Index: AbstractInputCheckedMapDecorator.java
===================================================================
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections.map;
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
import org.apache.commons.collections.set.AbstractSetDecorator;
/**
* An abstract base class that simplifies the task of creating map decorators.
* <p>
* The Map API is very difficult to decorate correctly, and involves implementing
* lots of different classes. This class exists to provide a simpler API.
* <p>
* Special hook methods are provided that are called when objects are added to
* the map. By overriding these methods, the input can be validated or manipulated.
* In addition to the main map methods, the entrySet is also affected, which is
* the hardest part of writing map implementations.
*
* @since Commons Collections 3.1
* @version $Revision: 1.1 $ $Date: 2004/05/03 21:48:49 $
*
* @author Stephen Colebourne
*/
public class AbstractInputCheckedMapDecorator
extends AbstractMapDecorator {
/**
* Constructor only used in deserialization, do not use otherwise.
*/
protected AbstractInputCheckedMapDecorator() {
super();
}
/**
* Constructor that wraps (not copies).
*
* @param map the map to decorate, must not be null
* @throws IllegalArgumentException if map is null
*/
protected AbstractInputCheckedMapDecorator(Map map) {
super(map);
}
//-----------------------------------------------------------------------
/**
* Hook method called when a key is being added to the map using
* <code>put</code> or <code>putAll</code>.
* <p>
* An implementation may validate the key and throw an exception
* or it may transform the key into another object.
* The key may already exist in the map.
* <p>
* This implementation returns the input key.
*
* @param key the key to check
* @throws UnsupportedOperationException if the map may not be changed by
put/putAll
* @throws IllegalArgumentException if the specified key is invalid
* @throws ClassCastException if the class of the specified key is invalid
* @throws NullPointerException if the specified key is null and nulls are
invalid
*/
protected Object checkPutKey(Object key) {
return key;
}
/**
* Hook method called when a new value is being added to the map using
* <code>put</code> or <code>putAll</code>.
* <p>
* An implementation may validate the value and throw an exception
* or it may transform the value into another object.
* <p>
* This implementation returns the input value.
*
* @param value the value to check
* @throws UnsupportedOperationException if the map may not be changed by
put/putAll
* @throws IllegalArgumentException if the specified value is invalid
* @throws ClassCastException if the class of the specified value is invalid
* @throws NullPointerException if the specified value is null and nulls are
invalid
*/
protected Object checkPutValue(Object value) {
return value;
}
/**
* Hook method called when a value is being set using <code>setValue</code>.
* <p>
* An implementation may validate the value and throw an exception
* or it may transform the value into another object.
* <p>
* This implementation returns the input value.
*
* @param value the value to check
* @throws UnsupportedOperationException if the map may not be changed by
setValue
* @throws IllegalArgumentException if the specified value is invalid
* @throws ClassCastException if the class of the specified value is invalid
* @throws NullPointerException if the specified value is null and nulls are
invalid
*/
protected Object checkSetValue(Object value) {
return value;
}
/**
* Hook method called to determine if <code>checkSetValue</code> has any effect.
* <p>
* An implementation should return false if the <code>checkSetValue</code> method
* has no effect as this optimises the implementation.
* <p>
* This implementation returns <code>true</code>.
*
* @param value the value to check
*/
protected boolean isSetValueChecking() {
return true;
}
/**
* Checks each element in the specified map, creating a new map.
* <p>
* This method is used by <code>putAll</code> to check all the elements
* before adding them to the map.
* <p>
* This implementation builds a <code>LinkedMap</code> to preserve the order
* of the input map.
*
* @param map the map to transform
* @throws the transformed object
*/
protected Map checkMap(Map map) {
Map result = new LinkedMap(map.size());
for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next();
result.put(checkPutKey(entry.getKey()), checkPutValue(entry.getValue()));
}
return result;
}
//-----------------------------------------------------------------------
public Object put(Object key, Object value) {
key = checkPutKey(key);
value = checkPutValue(value);
return getMap().put(key, value);
}
public void putAll(Map mapToCopy) {
if (mapToCopy.size() == 0) {
return;
} else {
mapToCopy = checkMap(mapToCopy);
getMap().putAll(mapToCopy);
}
}
public Set entrySet() {
if (isSetValueChecking()) {
return new EntrySet(map.entrySet(), this);
} else {
return map.entrySet();
}
}
//-----------------------------------------------------------------------
/**
* Implementation of an entry set that checks additions via setValue.
*/
static class EntrySet extends AbstractSetDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {
super(set);
this.parent = parent;
}
public Iterator iterator() {
return new EntrySetIterator(collection.iterator(), parent);
}
public Object[] toArray() {
Object[] array = collection.toArray();
for (int i = 0; i < array.length; i++) {
array[i] = new MapEntry((Map.Entry) array[i], parent);
}
return array;
}
public Object[] toArray(Object array[]) {
Object[] result = array;
if (array.length > 0) {
// we must create a new array to handle multi-threaded situations
// where another thread could access data before we decorate it
result = (Object[])
Array.newInstance(array.getClass().getComponentType(), 0);
}
result = collection.toArray(result);
for (int i = 0; i < result.length; i++) {
result[i] = new MapEntry((Map.Entry) result[i], parent);
}
// check to see if result should be returned straight
if (result.length > array.length) {
return result;
}
// copy back into input array to fulfil the method contract
System.arraycopy(result, 0, array, 0, result.length);
if (array.length > result.length) {
array[result.length] = null;
}
return array;
}
}
/**
* Implementation of an entry set iterator that checks additions via setValue.
*/
static class EntrySetIterator extends AbstractIteratorDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected EntrySetIterator(Iterator iterator,
AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}
public Object next() {
Map.Entry entry = (Map.Entry) iterator.next();
return new MapEntry(entry, parent);
}
}
/**
* Implementation of a map entry that checks additions via setValue.
*/
static class MapEntry extends AbstractMapEntryDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent)
{
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}
}
1.45 +2 -1 jakarta-commons/collections/RELEASE-NOTES.html
Index: RELEASE-NOTES.html
===================================================================
RCS file: /home/cvs/jakarta-commons/collections/RELEASE-NOTES.html,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -r1.44 -r1.45
--- RELEASE-NOTES.html 3 May 2004 15:13:30 -0000 1.44
+++ RELEASE-NOTES.html 3 May 2004 21:48:49 -0000 1.45
@@ -19,7 +19,7 @@
<p>
This release focusses on bug fixes and minor enhancements.
-No interface changes, or deprecations have occurred.
+No deprecations have occurred.
<hr />
@@ -32,6 +32,7 @@
<li>MapBackedSet - Set created by decorating a map</li>
<li>ReferenceIdentityMap - Similar to ReferenceMap, but matching keys and values by
identity [26503]</li>
<li>AbstractReferenceMap - New base class for reference maps [26503]</li>
+<li>AbstractInputCheckedMapDecorator - New base class for map decorators that
validate or alter input</li>
</ul>
<center><h3>ENHANCEMENTS</h3></center>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]