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]

Reply via email to