It doesn't seem as if anyone has submitted this patch into the CVS 
repository for testing... Is this any way to encourage new participants?

Chris

>From: "christopher marshall" <[EMAIL PROTECTED]>
>Reply-To: "Jakarta Commons Developers List" 
><[EMAIL PROTECTED]>
>To: [EMAIL PROTECTED]
>Subject: PATCH : org.apache.commons.collections.SoftRefHashMap
>Date: Fri, 17 May 2002 16:03:33 +0000
>
>All -
>
>Please let me know if this is OK as I haven't submitted code before.
>
>Patches fix the SoftRefHashMap so that the "entrySet()" and "values()"
>methods are backed by the underlying map and the keys whose referent-values
>have been cleared will be automatically (and performant-ly) purged upon
>writes to the map. I have deprecated the "purge()" method and 
>re-implemented
>it to call "processQueue()", which is a more performant way of purging the
>Map as it goes straight to the cleared values without iterating through the
>whole Map.
>
>The Map also has the new method "getIfAbsentPut(Object key, ObjectFactory
>fac)" which takes a new paramter of type
>org.apache.common.collections.ObjectFactory (an Interface). It aids the use
>of this map as a cache (see below).
>
>I feel that the idea to extend the class with "createReference()"
>overimplemented is a bad one, and have removed the functionality. Among
>other things, it is difficult to see how this would be implemented here
>without unnecessary complications - considering the functionality is
>questionable (to extend SoftRefHashMap to actually be a WeakRefHashMap?) I
>think that this is justified.
>
>I include two files org.apache.commons.collections.SoftRefHashMap and
>org.apache.commons.collections.ObjectFactory.
>
>The idea of Object factory is so that the Map can be used effectively as a
>Cache; suppose that a User wishes to store (expensively retrieved) database
>entries in the Map which must always be retrieved (whether the referent
>value has since been GC-ed or not). The user code looks like
>
>//
>public UserObject apiGetUser(final String userId) {
>  return (UserObject) map.getIfAbsentPut(userId, new ObjectFactory() {
>     public Object create() {
>         //expensive database access to return the
>         //required Object
>         Object user = expensiveDBAccess( userId );
>         return user;
>     }
>  });
>}
>//
>
>The ObjectFactory.create() will only be invoked (and the expensive 
>operation
>executed) should the value have been cleared from the Map (ie. GC-d). The
>factory method will be invoked and the Object stored in the map for the 
>key.
>
>Anyway here is the source code...
>
>
>*****************************************
>
>
>/**
>*
>* The Apache Software License, Version 1.1
>*
>* Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
>* reserved.
>*
>* Redistribution and use in source and binary forms, with or without
>* modification, are permitted provided that the following conditions
>* are met:
>*
>* 1. Redistributions of source code must retain the above copyright
>*    notice, this list of conditions and the following disclaimer.
>*
>* 2. Redistributions in binary form must reproduce the above copyright
>*    notice, this list of conditions and the following disclaimer in
>*    the documentation and/or other materials provided with the
>*    distribution.
>*
>* 3. The end-user documentation included with the redistribution, if
>*    any, must include the following acknowlegement:
>*       "This product includes software developed by the
>*        Apache Software Foundation (http://www.apache.org/)."
>*    Alternately, this acknowlegement may appear in the software itself,
>*    if and wherever such third-party acknowlegements normally appear.
>*
>* 4. The names "The Jakarta Project", "Commons", and "Apache Software
>*    Foundation" must not be used to endorse or promote products derived
>*    from this software without prior written permission. For written
>*    permission, please contact [EMAIL PROTECTED]
>*
>* 5. Products derived from this software may not be called "Apache"
>*    nor may "Apache" appear in their names without prior written
>*    permission of the Apache Group.
>*
>* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
>* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
>* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>* SUCH DAMAGE.
>* ====================================================================
>*
>* This software consists of voluntary contributions made by many
>* individuals on behalf of the Apache Software Foundation.  For more
>* information on the Apache Software Foundation, please see
>* <http://www.apache.org/>.
>*
>*/
>package org.apache.commons.collections;
>
>/**
>* This interface provides an abstract Object factory.
>* @author Chris Marshall
>* @version 1.0
>*/
>
>public interface ObjectFactory {
>  /**
>   * return an Object.
>   */
>  public Object create();
>}
>
>*******************************************************************
>
>
>/*
>*
>* The Apache Software License, Version 1.1
>*
>* Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
>* reserved.
>*
>* Redistribution and use in source and binary forms, with or without
>* modification, are permitted provided that the following conditions
>* are met:
>*
>* 1. Redistributions of source code must retain the above copyright
>*    notice, this list of conditions and the following disclaimer.
>*
>* 2. Redistributions in binary form must reproduce the above copyright
>*    notice, this list of conditions and the following disclaimer in
>*    the documentation and/or other materials provided with the
>*    distribution.
>*
>* 3. The end-user documentation included with the redistribution, if
>*    any, must include the following acknowlegement:
>*       "This product includes software developed by the
>*        Apache Software Foundation (http://www.apache.org/)."
>*    Alternately, this acknowlegement may appear in the software itself,
>*    if and wherever such third-party acknowlegements normally appear.
>*
>* 4. The names "The Jakarta Project", "Commons", and "Apache Software
>*    Foundation" must not be used to endorse or promote products derived
>*    from this software without prior written permission. For written
>*    permission, please contact [EMAIL PROTECTED]
>*
>* 5. Products derived from this software may not be called "Apache"
>*    nor may "Apache" appear in their names without prior written
>*    permission of the Apache Group.
>*
>* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
>* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
>* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>* SUCH DAMAGE.
>* ====================================================================
>*
>* This software consists of voluntary contributions made by many
>* individuals on behalf of the Apache Software Foundation.  For more
>* information on the Apache Software Foundation, please see
>* <http://www.apache.org/>.
>*
>*/
>package org.apache.commons.collections;
>
>import java.lang.ref.*;
>import java.lang.reflect.*;
>import java.util.*;
>
>/** <p>
>  * HashMap with SoftReference links to values which allows the values of
>the Map
>  * to be garbage collected by the JVM if it becomes low on memory.
>  * Derive from this class and
>  * override the factory method <code>createReference()</code> method to
>make
>  * a Map wrapped in other types of Reference.
>  * </p>
>  *
>  * <p>
>  * A synchronized version can be obtained with:
>  * <code>Collections.synchronizedMap( theMapToSynchronize )</code>
>  * </p>
>  *
>  * <p>
>  * <b>WARNING</b> the values() and entrySet() methods require optimisation
>  * like the standard {@link HashMap} implementations so that iteration
>  * over this Map is efficient.
>  * </p>
>  *
>  * @author  James.Dodd
>  * @author <a href="mailto:[EMAIL PROTECTED]";>James Strachan</a>
>  */
>public class SoftRefHashMap implements Map {
>
>    /** The wrapped HashMap */
>    private Map hashMap = new HashMap();
>    private ReferenceQueue queue = new ReferenceQueue();
>
>    // Inner Classes
>    // 
>---------------------------------------------------------------------
>
>    /**
>       * Internal factory class to return <tt>Reference</tt>
>       * implementation.
>       */
>   private static class RefFactory {
>         private static Reference reference(
>                Object o,
>                Object key,
>                ReferenceQueue queue) {
>                return SoftKey.create(o, key, queue);
>         }
>   }
>/**
>       * Internal extension of <tt>SoftReference</tt>
>       * to be stored as the <tt>value</tt> in the
>       * underlying map.
>       */
>   static private class SoftKey extends SoftReference {
>         private int hash;
>         private Object key;
>         /* Hashcode of key, stored here since the key
>         may be tossed by the GC */
>
>         private SoftKey(Object k, Object key, ReferenceQueue queue) {
>                super(k, queue);
>                this.key = key;
>                hash = k.hashCode();
>         }
>
>         private static SoftKey create(Object k, Object key, ReferenceQueue 
>queue)
>{
>                if (k == null)
>                       return null;
>                else
>                       return new SoftKey(k, key, queue);
>         }
>
>         /* A SoftKey is equal to another WeakKey iff they both refer to objects
>         that are, in turn, equal according to their own equals methods */
>         public boolean equals(Object o) {
>                if (this == o)
>                       return true;
>                if (!(o instanceof SoftKey))
>                       return false;
>                Object t = this.get();
>                Object u = ((SoftKey) o).get();
>                if ((t == null) || (u == null))
>                       return false;
>                if (t == u)
>                       return true;
>                return t.equals(u);
>         }
>         public Object getKey() {
>                return this.key;
>         }
>
>         public int hashCode() {
>                return hash;
>         }
>
>         public String toString() {
>                return "SoftKey(" + get() + ")";
>         }
>   }
>
>   /**
>       * Internal implementation of <tt>Map.Entry</tt>
>       * for presentation through the <tt>EntryIterator</tt>
>       */
>   private class SoftKeyEntry implements Map.Entry {
>         private Object key;
>         private Reference value;
>
>         private Map.Entry underlying;
>         private SoftKeyEntry(Object key, Reference value) {
>                this.key = key;
>                this.value = value;
>         }
>
>         private SoftKeyEntry(Map.Entry e) {
>                this.underlying = e;
>                this.key = e.getKey();
>                if (e instanceof SoftKeyEntry)
>                       this.value = ((SoftKeyEntry) e).getRef();
>                else if (e.getValue() instanceof Reference)
>                       this.value = (Reference) e.getValue();
>                else
>                       this.value = RefFactory.reference(e.getValue(), e.getKey(), 
>queue);
>         }
>         public Object getKey() {
>                return this.key;
>         }
>         public Object getValue() {
>                return this.value.get();
>         }
>         public Object setValue(Object o) {
>                if (underlying != null)
>                       underlying.setValue(o);
>                Object oldValue = value.get();
>                this.value = RefFactory.reference(o, getKey(), queue);
>                return oldValue;
>         }
>
>         private Reference getRef() {
>                return this.value;
>         }
>   }
>
>   /**
>       * Internal <tt>Iterator</tt> representation to iterate
>       * Through the underlying <tt>values()</tt>
>       */
>   private class ValueIterator implements Iterator {
>         private Iterator iterator;
>         private ValueIterator() {
>                this.iterator = getMap().values().iterator();
>         }
>         public boolean hasNext() {
>                return this.iterator.hasNext();
>         }
>
>         public Object next() {
>                if (!this.iterator.hasNext())
>                       throw new NoSuchElementException();
>                Object o = null;
>                while (o == null && this.iterator.hasNext()) {
>                       Reference ref = (Reference) this.iterator.next();
>                       if ((o = ref.get()) == null)
>                          this.iterator.remove();
>                }
>                return o;
>         }
>
>         public void remove() {
>                this.iterator.remove();
>         }
>   }
>
>   /**
>       * Internal <tt>Iterator</tt> representation to
>       * iterate through the underlying <tt>entrySet()</tt>.
>       */
>   private class EntryIterator implements Iterator {
>         private Iterator iterator;
>         private EntryIterator() {
>                this.iterator = getMap().entrySet().iterator();
>         }
>
>         public boolean hasNext() {
>                return this.iterator.hasNext();
>         }
>
>         public Object next() {
>                if (!this.iterator.hasNext())
>                       throw new NoSuchElementException();
>                Map.Entry entry = null;
>                while (entry == null && this.iterator.hasNext()) {
>                       Map.Entry e = (Map.Entry) this.iterator.next();
>                       Reference ref = (Reference) e.getValue();
>                       if (ref.get() == null)
>                          this.iterator.remove();
>                       else
>                          entry = new SoftKeyEntry(e);
>                }
>                return entry;
>         }
>
>         public void remove() {
>                this.iterator.remove();
>         }
>   }
>    public SoftRefHashMap() {
>    }
>
>
>    /**
>     * Removes References that have had their referents garbage collected
>     * @deprecated
>     */
>    public void purge() {
>        processQueue();
>    }
>
>    /**
>     * Retrieves the referent of the Referenced value
>     * @param key The key with which to retrieve the value
>     */
>    public Object get(Object key) {
>       SoftReference ref = (SoftReference) getMap().get(key);
>       if (ref == null)
>               return null;
>       Object o = null;
>       if ((o = ref.get()) == null)
>               remove(key);
>       return o;
>    }
>
>    /**
>     * Adds a key-value mapping, wrapping the value in a Reference
>     */
>    public Object put(Object key, Object value) {
>       processQueue();
>       return getMap().put(key, RefFactory.reference(value, key, queue));
>    }
>
>    /**
>     * Process the reference queue to clean up.
>     * Creation date: (07/05/02 10:13:59)
>     */
>    private void processQueue() {
>       Reference r = null;
>       while ((r = queue.poll()) != null)
>              getMap().remove(((SoftKey) r).getKey());
>    }
>    /**
>      * Returns a collection of the Referenced values
>      */
>    public java.util.Collection values() {
>            return new AbstractCollection() {
>                    public int size() {
>                            return getMap().size();
>                    }
>
>                    public Iterator iterator() {
>                            return new ValueIterator();
>
>                    }
>            };
>
>    }
>
>    /**
>     * Returns the value to which this map maps the specified key.  If the
>     * map does not contain the key, or if the value has been cleared by 
>the
>     * GarbageCollector, the supplied <tt>ObjectFactory</tt> is used to
>place
>     * a new value in the map foir the key. This new value is returned.
>     *
>     * @param key key whose associated value is to be returned.
>     * @param objectFactory factory to create the Object with which to put
>     *                 the <tt>Map</tt>
>     * @return the value to which this map maps the specified key, or
>     *        <tt>objectFactory.create()</tt> if the map contains no
>     *            mapping (or the mapping has been cleared) for this key.
>     *
>     * @throws ClassCastException if the key is of an inappropriate type 
>for
>     *                    this map.
>     * @throws NullPointerException key is <tt>null</tt> and this map does
>not
>     *           not permit <tt>null</tt> keys.
>     * @throws Exception if the <tt>ObjectFactory</tt>'s
>     *            <tt>create()</tt> method throws an Exception
>     *
>     * @see #containsKey(Object)
>     */
>    public Object getIfAbsentPut(Object key,
>org.apache.commons.collections.ObjectFactory objectFactory)
>       throws Exception {
>       Object o = get(key);
>       if (o == null)
>              put(key, o = objectFactory.create());
>       return o;
>    }
>    /**
>     * Answers whether the argument is in the domain of the mappings
>     */
>    public boolean containsKey( Object key ) {
>        return getMap().containsKey( key );
>    }
>
>    /**
>     * Answers whether the argument is a Referenced value
>     */
>    public boolean containsValue( Object value ) {
>        Collection values = (Collection) getMap().values();
>        if ( values == null ) {
>            return false;
>        }
>        for ( Iterator i = values.iterator(); i.hasNext(); ) {
>            Reference ref = (Reference) i.next();
>            if ( ref == null ) {
>                continue;
>            }
>            Object target = ref.get();
>            if ( target == value ) {
>                return true;
>            }
>        }
>        return false;
>    }
>
>    /**
>      * Put all of the mappings in the argument into this wrapped map
>      */
>    public void putAll(java.util.Map map) {
>        if ( map == null || map.size() == 0 ) {
>            return;
>        }
>        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
>           Map.Entry e = (Map.Entry) it.next();
>           put(e.getKey(), e.getValue());
>        }
>    }
>
>    /**
>      * Returns a set view of the mappings in the wrapped map
>      */
>    public java.util.Set entrySet() {
>       return new AbstractSet() {
>
>               public int size() {
>                       return getMap().size();
>               }
>
>               public Iterator iterator() {
>                       return new EntryIterator();
>               }
>       };
>    }
>
>    /**
>      * Removes a mapping from this map
>      */
>    public Object remove(Object key) {
>       SoftReference ref = (SoftReference) getMap().remove(key);
>        if (ref != null) {
>           return ref.get();
>        }
>        return null;
>    }
>
>    /**
>      * Clears all  mappings
>      */
>    public void clear() {
>        getMap().clear();
>    }
>
>    /**
>      * Calculates the hash code for this map
>      */
>    public int hashCode() {
>        return getMap().hashCode();
>    }
>
>    /**
>      * Returns the domain of the mappings
>      */
>    public Set keySet() {
>        return getMap().keySet();
>    }
>
>    /**
>      * Answers whether there are any mappings
>      */
>    public boolean isEmpty() {
>        return getMap().isEmpty();
>    }
>
>    /**
>      * Answers whether this map and the argument are 'the same'
>      */
>    public boolean equals( final Object object ) {
>        return getMap().equals( object );
>    }
>
>    /**
>      * Returns the number of mappings in this map
>      */
>    public int size() {
>        return getMap().size();
>    }
>
>
>    /**
>     * Retrieves the wrapped HashMap
>     * @return The wrapped HashMap
>     */
>    protected Map getMap() {
>        return hashMap;
>    }
>}
>
>_________________________________________________________________
>Chat with friends online, try MSN Messenger: http://messenger.msn.com
>
>
>--
>To unsubscribe, e-mail:   
><mailto:[EMAIL PROTECTED]>
>For additional commands, e-mail: 
><mailto:[EMAIL PROTECTED]>
>




_________________________________________________________________
Send and receive Hotmail on your mobile device: http://mobile.msn.com


--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to