You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Neil O'Toole <ne...@users.sourceforge.net> on 2003/09/21 00:06:56 UTC

[collections] submission: KeyValueRecord

Attached is source (& associated tests) for KeyValueRecord.java.
KeyValueRecord is an immutable {key, value, previous-value} triplet, a
simple but very useful data structure. It's particularly of use with
Map (and Map.Entry) for storing, tracking, and comparing entry state,
but can obviously be used for any key-value data. The constructors
include one to create a KeyValueRecord from a Map.Entry. There is also
a method to view the KeyValueRecord as a Map.Entry (#asMapEntry). See
the javadoc for more details.

** I had a swift look through [collections] and [lang], and didn't see
a simple KeyValuePair implementation... Have I missed this somewhere?
If not, I have an implementation which I will post.

Thanks,

Neil

Re: [collections] Map.Entry and KeyValue

Posted by Neil O'Toole <ne...@users.sourceforge.net>.
I believe that the test case for DefaultMapEntry will test by extension
test KeyValuePair??.. though we can simply subclass the test case also
- should we do this?

N.

--- Stephen Colebourne <sc...@btopenworld.com> wrote:
> I'm happy to add it....with a test case :-)
> Stephen
> 
> ----- Original Message -----
> From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > > My suggested solution is to add a class "KeyValue.java"
> containing
> > > the code from DefaultMapEntry, but not implementing Map.Entry.
> > > DefaultMapEntry can then subclass KeyValue and implement
> Map.Entry,
> > > and be declared final.
> >
> > Suggested solution attached (KeyValuePair.java and patch for
> > DefaultMapEntry.java). Note that:
> >
> > - The superflous null check is removed from DefaultMapEntry#equals
> > (instanceof checks for null).
> > - The DefaultMapEntry #equals and #hashCode methods are declared
> final.
> > The 'correct' approach is probably that the whole class should be
> > declared final - thoughts on this??.
> > - KeyValuePair has a toString() method.
> >
> > Unless there are objections, anyone want to add this to CVS?
> >
> > Neil
> >
> >
> > --- Neil O'Toole <ne...@users.sourceforge.net> wrote:
> > > > > ** I had a swift look through [collections] and [lang], and
> > > didn't
> > > > see
> > > > > a simple KeyValuePair implementation... Have I missed this
> > > > somewhere?
> > > >
> > > > DefaultMapEntry IIRC is the simple Map.Entry impl.
> > > > Stephen
> > > >
> > >
> > > Although one (including my good self!) might initially think that
> a
> > > key-value class and a Map.Entry class are interchangeable, this
> is
> > > not
> > > in fact the case. DefaultMapEntry is not suitable for use as a
> > > general
> > > purpose KeyValue class, as it is effectively non-extendable (and
> > > there
> > > are many cases where it is useful to extend a KeyValue class). In
> > > fact,
> > > DefaultMapEntry (or at the very least its #equals and #hashcode
> > > methods) should be declared final so that users won't make the
> > > mistake
> > > of subclassing it.
> > >
> > > DefaultMapEntry is not extendable because the #equals method is
> > > contractually specified by Map.Entry to only evalute the members
> of
> > > Map.Entry. A non-trivial subclass of DefaultMapEntry will add
> members
> > > that will be used in #equals comparision. To make use of these
> > > members
> > > in #equals will of course violate the Map.Entry#equals contract.
> > >
> > > This non-extensibility property applies to any class that
> inherits or
> > > implements an #equals method with a similar closed #equals
> contract,
> > > and any such class should be declared final to prevent accidental
> > > subclassing (or again, at the very least the #hashcode and
> #equals
> > > methods should be declared final, though the user might just
> ignore
> > > those methods, resulting in unfortunate code).
> > >
> > > My suggested solution is to add a class "KeyValue.java"
> containing
> > > the
> > > code from DefaultMapEntry, but not implementing Map.Entry.
> > > DefaultMapEntry can then subclass KeyValue and implement
> Map.Entry,
> > > and
> > > be declared final.
> > >
> > > - Neil
> > >
> > >
> > >
> > >
> ---------------------------------------------------------------------
> > > To unsubscribe, e-mail:
> commons-dev-unsubscribe@jakarta.apache.org
> > > For additional commands, e-mail:
> commons-dev-help@jakarta.apache.org
> > >
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> > /*
> >  * $Header:
>
/home/cvspublic/jakarta-commons/collections/src/java/org/apache/commons/coll
> ections/DefaultMapEntry.java,v 1.11 2003/08/31 17:26:43 scolebourne
> Exp $
> >  *
> ====================================================================
> >  *
> >  * The Apache Software License, Version 1.1
> >  *
> >  * Copyright (c) 2001-2003 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 acknowledgement:
> >  *       "This product includes software developed by the
> >  *        Apache Software Foundation (http://www.apache.org/)."
> >  *    Alternately, this acknowledgement may appear in the software
> itself,
> >  *    if and wherever such third-party acknowledgements 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 apache@apache.org.
> >  *
> >  * 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 Software Foundation.
> >  *
> >  * 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;
> >
> >
> >
> > /**
> >  * A simple key-value pair.
> >  *
> >  * @since Commons Collections 3.0
> >  * @version $Revision: $
> >  *
> >  * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
> >  * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
> >  * @author Neil O'Toole
> >  */
> > public class KeyValuePair{
> >
> >     /** The key */
> >     private Object key;
> >     /** The value */
> >     private Object value;
> >
> >     /**
> >      * Constructs a new <Code>DefaultMapEntry</Code> with a null
> key
> >      * and null value.
> >      */
> >     public KeyValuePair() {
> >     }
> >
> >     /**
> >      * Constructs a new <Code>DefaultMapEntry</Code> with the given
> >      * key and given value.
> >      *
> >      * @param key  the key for the entry, may be null
> >      * @param value  the value for the entry, may be null
> >      */
> >     public KeyValuePair(Object key, Object value) {
> >         this.key = key;
> >         this.value = value;
> >     }
> >
> >
> > /**
> > * Returns true if the compared object is also a
> <code>KeyValuePair</code>,
> > * and its key and value are equal to this object's key and value.
> > */
> >     public boolean equals(Object o) {
> >
> >         if ( o == this ) return true;
> >
> >         if ( o instanceof KeyValuePair == false ) return false;
> >
> >         KeyValuePair e2 = (KeyValuePair) o;
> >         return ((getKey() == null ?
> >                  e2.getKey() == null :
> getKey().equals(e2.getKey())) &&
> >                 (getValue() == null ?
> >                  e2.getValue() == null :
> getValue().equals(e2.getValue())));
> >     }
> >
> >
> >
> >     public int hashCode() {
> >         return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
> >                  ( getValue() == null ? 0 : getValue().hashCode() )
> );
> >     }
> >
> >
> >
> >     /**
> >      * Returns the key.
> >      *
> >      * @return the key
> >      */
> >     public Object getKey() {
> >         return key;
> >     }
> >
> >     /**
> >      * Returns the value.
> >      *
> >      * @return the value
> >      */
> >     public Object getValue() {
> >         return value;
> >     }
> >
> >
> >
> >     /**
> >      * Sets the key.
> >      *
> >      * @param key  the new key
> >      */
> >     public void setKey(Object key) {
> >         this.key = key;
> >     }
> >
> >     /**
> >      * Sets the value.
> >      *
> >      * @return the old value of the value
> >      * @param value the new value
> >      */
> >     public Object setValue(Object value) {
> >         Object answer = this.value;
> >         this.value = value;
> >         return answer;
> >     }
> >
> > public String toString()
> > {
> > return new
>
StringBuffer().append(getKey()).append('=').append(getValue()).toString();
> > }
> > }
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> > Index: DefaultMapEntry.java
> > ===================================================================
> > RCS file:
>
/home/cvspublic/jakarta-commons/collections/src/java/org/apache/commons/coll
> ections/DefaultMapEntry.java,v
> > retrieving revision 1.11
> > diff -u -r1.11 DefaultMapEntry.java
> > --- DefaultMapEntry.java 31 Aug 2003 17:26:43 -0000 1.11
> > +++ DefaultMapEntry.java 23 Sep 2003 02:00:14 -0000
> > @@ -67,13 +67,10 @@
> >   *
> >   * @author <a href="mailto:jstrachan@apache.org">James
> Strachan</a>
> >   * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
> > + * @author Neil O'Toole
> >   */
> > -public class DefaultMapEntry implements Map.Entry {
> > +public class DefaultMapEntry extends KeyValuePair implements
> Map.Entry {
> >
> > -    /** The key */
> > -    private Object key;
> > -    /** The value */
> > -    private Object value;
> >
> >      /**
> >       * Constructs a new <Code>DefaultMapEntry</Code> with a null
> key
> > @@ -90,20 +87,18 @@
> >       * @param value  the value for the entry, may be null
> >       */
> >      public DefaultMapEntry(Object key, Object value) {
> > -        this.key = key;
> > -        this.value = value;
> > +        super(key, value);
> >      }
> >
> >      /**
> >       * Implemented per API documentation of
> >       * {@link java.util.Map.Entry#equals(Object)}
> >       */
> > -    public boolean equals(Object o) {
> > -        if( o == null ) return false;
> > +    public final boolean equals(Object o) {
> >          if( o == this ) return true;
> >
> > -        if ( ! (o instanceof Map.Entry ) )
> > -            return false;
> > +        if ( !(o instanceof Map.Entry ) ) return false;
> > +
> >          Map.Entry e2 = (Map.Entry)o;
> >          return ((getKey() == null ?
> >                   e2.getKey() == null :
> getKey().equals(e2.getKey())) &&
> > @@ -116,57 +111,12 @@
> >       * Implemented per API documentation of
> >       * {@link java.util.Map.Entry#hashCode()}
> >       */
> > -    public int hashCode() {
> > +    public final int hashCode() {
> >          return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
> >                   ( getValue() == null ? 0 : getValue().hashCode()
> ) );
> >      }
> >
> >
> >
> > -    // Map.Entry interface
> > -
>
//-------------------------------------------------------------------------
> > -
> > -    /**
> > -     * Returns the key.
> > -     *
> > -     * @return the key
> > -     */
> > -    public Object getKey() {
> > -        return key;
> > -    }
> > -
> > -    /**
> > -     * Returns the value.
> > -     *
> > -     * @return the value
> > -     */
> > -    public Object getValue() {
> > -        return value;
> > -    }
> > -
> > -    // Properties
> > -
>
//-------------------------------------------------------------------------
> > -
> > -    /**
> > -     * Sets the key.  This method does not modify any map.
> > -     *
> > -     * @param key  the new key
> > -     */
> > -    public void setKey(Object key) {
> > -        this.key = key;
> > -    }
> > -
> > -    /**
> > -     * Note that this method only sets the local reference inside
> this
> object and
> > -     * does not modify the original Map.
> > -     *
> > -     * @return the old value of the value
> > -     * @param value the new value
> > -     */
> > -    public Object setValue(Object value) {
> > -        Object answer = this.value;
> > -        this.value = value;
> > -        return answer;
> > -    }
> >
> >  }
> >
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> >
> ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail:
> commons-dev-help@jakarta.apache.org
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [collections] Map.Entry and KeyValue

Posted by Neil O'Toole <ne...@users.sourceforge.net>.
I believe that the test case for DefaultMapEntry will test by extension
test KeyValuePair??.. though we can simply subclass the test case also
- should we do this?

N.

--- Stephen Colebourne <sc...@btopenworld.com> wrote:
> I'm happy to add it....with a test case :-)
> Stephen
> 
> ----- Original Message -----
> From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > > My suggested solution is to add a class "KeyValue.java"
> containing
> > > the code from DefaultMapEntry, but not implementing Map.Entry.
> > > DefaultMapEntry can then subclass KeyValue and implement
> Map.Entry,
> > > and be declared final.
> >
> > Suggested solution attached (KeyValuePair.java and patch for
> > DefaultMapEntry.java). Note that:
> >
> > - The superflous null check is removed from DefaultMapEntry#equals
> > (instanceof checks for null).
> > - The DefaultMapEntry #equals and #hashCode methods are declared
> final.
> > The 'correct' approach is probably that the whole class should be
> > declared final - thoughts on this??.
> > - KeyValuePair has a toString() method.
> >
> > Unless there are objections, anyone want to add this to CVS?
> >
> > Neil
> >
> >
> > --- Neil O'Toole <ne...@users.sourceforge.net> wrote:
> > > > > ** I had a swift look through [collections] and [lang], and
> > > didn't
> > > > see
> > > > > a simple KeyValuePair implementation... Have I missed this
> > > > somewhere?
> > > >
> > > > DefaultMapEntry IIRC is the simple Map.Entry impl.
> > > > Stephen
> > > >
> > >
> > > Although one (including my good self!) might initially think that
> a
> > > key-value class and a Map.Entry class are interchangeable, this
> is
> > > not
> > > in fact the case. DefaultMapEntry is not suitable for use as a
> > > general
> > > purpose KeyValue class, as it is effectively non-extendable (and
> > > there
> > > are many cases where it is useful to extend a KeyValue class). In
> > > fact,
> > > DefaultMapEntry (or at the very least its #equals and #hashcode
> > > methods) should be declared final so that users won't make the
> > > mistake
> > > of subclassing it.
> > >
> > > DefaultMapEntry is not extendable because the #equals method is
> > > contractually specified by Map.Entry to only evalute the members
> of
> > > Map.Entry. A non-trivial subclass of DefaultMapEntry will add
> members
> > > that will be used in #equals comparision. To make use of these
> > > members
> > > in #equals will of course violate the Map.Entry#equals contract.
> > >
> > > This non-extensibility property applies to any class that
> inherits or
> > > implements an #equals method with a similar closed #equals
> contract,
> > > and any such class should be declared final to prevent accidental
> > > subclassing (or again, at the very least the #hashcode and
> #equals
> > > methods should be declared final, though the user might just
> ignore
> > > those methods, resulting in unfortunate code).
> > >
> > > My suggested solution is to add a class "KeyValue.java"
> containing
> > > the
> > > code from DefaultMapEntry, but not implementing Map.Entry.
> > > DefaultMapEntry can then subclass KeyValue and implement
> Map.Entry,
> > > and
> > > be declared final.
> > >
> > > - Neil
> > >
> > >
> > >
> > >
> ---------------------------------------------------------------------
> > > To unsubscribe, e-mail:
> commons-dev-unsubscribe@jakarta.apache.org
> > > For additional commands, e-mail:
> commons-dev-help@jakarta.apache.org
> > >
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> > /*
> >  * $Header:
>
/home/cvspublic/jakarta-commons/collections/src/java/org/apache/commons/coll
> ections/DefaultMapEntry.java,v 1.11 2003/08/31 17:26:43 scolebourne
> Exp $
> >  *
> ====================================================================
> >  *
> >  * The Apache Software License, Version 1.1
> >  *
> >  * Copyright (c) 2001-2003 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 acknowledgement:
> >  *       "This product includes software developed by the
> >  *        Apache Software Foundation (http://www.apache.org/)."
> >  *    Alternately, this acknowledgement may appear in the software
> itself,
> >  *    if and wherever such third-party acknowledgements 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 apache@apache.org.
> >  *
> >  * 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 Software Foundation.
> >  *
> >  * 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;
> >
> >
> >
> > /**
> >  * A simple key-value pair.
> >  *
> >  * @since Commons Collections 3.0
> >  * @version $Revision: $
> >  *
> >  * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
> >  * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
> >  * @author Neil O'Toole
> >  */
> > public class KeyValuePair{
> >
> >     /** The key */
> >     private Object key;
> >     /** The value */
> >     private Object value;
> >
> >     /**
> >      * Constructs a new <Code>DefaultMapEntry</Code> with a null
> key
> >      * and null value.
> >      */
> >     public KeyValuePair() {
> >     }
> >
> >     /**
> >      * Constructs a new <Code>DefaultMapEntry</Code> with the given
> >      * key and given value.
> >      *
> >      * @param key  the key for the entry, may be null
> >      * @param value  the value for the entry, may be null
> >      */
> >     public KeyValuePair(Object key, Object value) {
> >         this.key = key;
> >         this.value = value;
> >     }
> >
> >
> > /**
> > * Returns true if the compared object is also a
> <code>KeyValuePair</code>,
> > * and its key and value are equal to this object's key and value.
> > */
> >     public boolean equals(Object o) {
> >
> >         if ( o == this ) return true;
> >
> >         if ( o instanceof KeyValuePair == false ) return false;
> >
> >         KeyValuePair e2 = (KeyValuePair) o;
> >         return ((getKey() == null ?
> >                  e2.getKey() == null :
> getKey().equals(e2.getKey())) &&
> >                 (getValue() == null ?
> >                  e2.getValue() == null :
> getValue().equals(e2.getValue())));
> >     }
> >
> >
> >
> >     public int hashCode() {
> >         return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
> >                  ( getValue() == null ? 0 : getValue().hashCode() )
> );
> >     }
> >
> >
> >
> >     /**
> >      * Returns the key.
> >      *
> >      * @return the key
> >      */
> >     public Object getKey() {
> >         return key;
> >     }
> >
> >     /**
> >      * Returns the value.
> >      *
> >      * @return the value
> >      */
> >     public Object getValue() {
> >         return value;
> >     }
> >
> >
> >
> >     /**
> >      * Sets the key.
> >      *
> >      * @param key  the new key
> >      */
> >     public void setKey(Object key) {
> >         this.key = key;
> >     }
> >
> >     /**
> >      * Sets the value.
> >      *
> >      * @return the old value of the value
> >      * @param value the new value
> >      */
> >     public Object setValue(Object value) {
> >         Object answer = this.value;
> >         this.value = value;
> >         return answer;
> >     }
> >
> > public String toString()
> > {
> > return new
>
StringBuffer().append(getKey()).append('=').append(getValue()).toString();
> > }
> > }
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> > Index: DefaultMapEntry.java
> > ===================================================================
> > RCS file:
>
/home/cvspublic/jakarta-commons/collections/src/java/org/apache/commons/coll
> ections/DefaultMapEntry.java,v
> > retrieving revision 1.11
> > diff -u -r1.11 DefaultMapEntry.java
> > --- DefaultMapEntry.java 31 Aug 2003 17:26:43 -0000 1.11
> > +++ DefaultMapEntry.java 23 Sep 2003 02:00:14 -0000
> > @@ -67,13 +67,10 @@
> >   *
> >   * @author <a href="mailto:jstrachan@apache.org">James
> Strachan</a>
> >   * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
> > + * @author Neil O'Toole
> >   */
> > -public class DefaultMapEntry implements Map.Entry {
> > +public class DefaultMapEntry extends KeyValuePair implements
> Map.Entry {
> >
> > -    /** The key */
> > -    private Object key;
> > -    /** The value */
> > -    private Object value;
> >
> >      /**
> >       * Constructs a new <Code>DefaultMapEntry</Code> with a null
> key
> > @@ -90,20 +87,18 @@
> >       * @param value  the value for the entry, may be null
> >       */
> >      public DefaultMapEntry(Object key, Object value) {
> > -        this.key = key;
> > -        this.value = value;
> > +        super(key, value);
> >      }
> >
> >      /**
> >       * Implemented per API documentation of
> >       * {@link java.util.Map.Entry#equals(Object)}
> >       */
> > -    public boolean equals(Object o) {
> > -        if( o == null ) return false;
> > +    public final boolean equals(Object o) {
> >          if( o == this ) return true;
> >
> > -        if ( ! (o instanceof Map.Entry ) )
> > -            return false;
> > +        if ( !(o instanceof Map.Entry ) ) return false;
> > +
> >          Map.Entry e2 = (Map.Entry)o;
> >          return ((getKey() == null ?
> >                   e2.getKey() == null :
> getKey().equals(e2.getKey())) &&
> > @@ -116,57 +111,12 @@
> >       * Implemented per API documentation of
> >       * {@link java.util.Map.Entry#hashCode()}
> >       */
> > -    public int hashCode() {
> > +    public final int hashCode() {
> >          return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
> >                   ( getValue() == null ? 0 : getValue().hashCode()
> ) );
> >      }
> >
> >
> >
> > -    // Map.Entry interface
> > -
>
//-------------------------------------------------------------------------
> > -
> > -    /**
> > -     * Returns the key.
> > -     *
> > -     * @return the key
> > -     */
> > -    public Object getKey() {
> > -        return key;
> > -    }
> > -
> > -    /**
> > -     * Returns the value.
> > -     *
> > -     * @return the value
> > -     */
> > -    public Object getValue() {
> > -        return value;
> > -    }
> > -
> > -    // Properties
> > -
>
//-------------------------------------------------------------------------
> > -
> > -    /**
> > -     * Sets the key.  This method does not modify any map.
> > -     *
> > -     * @param key  the new key
> > -     */
> > -    public void setKey(Object key) {
> > -        this.key = key;
> > -    }
> > -
> > -    /**
> > -     * Note that this method only sets the local reference inside
> this
> object and
> > -     * does not modify the original Map.
> > -     *
> > -     * @return the old value of the value
> > -     * @param value the new value
> > -     */
> > -    public Object setValue(Object value) {
> > -        Object answer = this.value;
> > -        this.value = value;
> > -        return answer;
> > -    }
> >
> >  }
> >
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> >
> ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail:
> commons-dev-help@jakarta.apache.org
> 


Re: [collections] Map.Entry and KeyValue

Posted by Stephen Colebourne <sc...@btopenworld.com>.
I'm happy to add it....with a test case :-)
Stephen

----- Original Message -----
From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > My suggested solution is to add a class "KeyValue.java" containing
> > the code from DefaultMapEntry, but not implementing Map.Entry.
> > DefaultMapEntry can then subclass KeyValue and implement Map.Entry,
> > and be declared final.
>
> Suggested solution attached (KeyValuePair.java and patch for
> DefaultMapEntry.java). Note that:
>
> - The superflous null check is removed from DefaultMapEntry#equals
> (instanceof checks for null).
> - The DefaultMapEntry #equals and #hashCode methods are declared final.
> The 'correct' approach is probably that the whole class should be
> declared final - thoughts on this??.
> - KeyValuePair has a toString() method.
>
> Unless there are objections, anyone want to add this to CVS?
>
> Neil
>
>
> --- Neil O'Toole <ne...@users.sourceforge.net> wrote:
> > > > ** I had a swift look through [collections] and [lang], and
> > didn't
> > > see
> > > > a simple KeyValuePair implementation... Have I missed this
> > > somewhere?
> > >
> > > DefaultMapEntry IIRC is the simple Map.Entry impl.
> > > Stephen
> > >
> >
> > Although one (including my good self!) might initially think that a
> > key-value class and a Map.Entry class are interchangeable, this is
> > not
> > in fact the case. DefaultMapEntry is not suitable for use as a
> > general
> > purpose KeyValue class, as it is effectively non-extendable (and
> > there
> > are many cases where it is useful to extend a KeyValue class). In
> > fact,
> > DefaultMapEntry (or at the very least its #equals and #hashcode
> > methods) should be declared final so that users won't make the
> > mistake
> > of subclassing it.
> >
> > DefaultMapEntry is not extendable because the #equals method is
> > contractually specified by Map.Entry to only evalute the members of
> > Map.Entry. A non-trivial subclass of DefaultMapEntry will add members
> > that will be used in #equals comparision. To make use of these
> > members
> > in #equals will of course violate the Map.Entry#equals contract.
> >
> > This non-extensibility property applies to any class that inherits or
> > implements an #equals method with a similar closed #equals contract,
> > and any such class should be declared final to prevent accidental
> > subclassing (or again, at the very least the #hashcode and #equals
> > methods should be declared final, though the user might just ignore
> > those methods, resulting in unfortunate code).
> >
> > My suggested solution is to add a class "KeyValue.java" containing
> > the
> > code from DefaultMapEntry, but not implementing Map.Entry.
> > DefaultMapEntry can then subclass KeyValue and implement Map.Entry,
> > and
> > be declared final.
> >
> > - Neil
> >
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail: commons-dev-help@jakarta.apache.org
> >
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
/home/cvspublic/jakarta-commons/collections/src/java/org/apache/commons/coll
ections/DefaultMapEntry.java,v 1.11 2003/08/31 17:26:43 scolebourne Exp $
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgement:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgement may appear in the software itself,
>  *    if and wherever such third-party acknowledgements 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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;
>
>
>
> /**
>  * A simple key-value pair.
>  *
>  * @since Commons Collections 3.0
>  * @version $Revision: $
>  *
>  * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
>  * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
>  * @author Neil O'Toole
>  */
> public class KeyValuePair{
>
>     /** The key */
>     private Object key;
>     /** The value */
>     private Object value;
>
>     /**
>      * Constructs a new <Code>DefaultMapEntry</Code> with a null key
>      * and null value.
>      */
>     public KeyValuePair() {
>     }
>
>     /**
>      * Constructs a new <Code>DefaultMapEntry</Code> with the given
>      * key and given value.
>      *
>      * @param key  the key for the entry, may be null
>      * @param value  the value for the entry, may be null
>      */
>     public KeyValuePair(Object key, Object value) {
>         this.key = key;
>         this.value = value;
>     }
>
>
> /**
> * Returns true if the compared object is also a <code>KeyValuePair</code>,
> * and its key and value are equal to this object's key and value.
> */
>     public boolean equals(Object o) {
>
>         if ( o == this ) return true;
>
>         if ( o instanceof KeyValuePair == false ) return false;
>
>         KeyValuePair e2 = (KeyValuePair) o;
>         return ((getKey() == null ?
>                  e2.getKey() == null : getKey().equals(e2.getKey())) &&
>                 (getValue() == null ?
>                  e2.getValue() == null :
getValue().equals(e2.getValue())));
>     }
>
>
>
>     public int hashCode() {
>         return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
>                  ( getValue() == null ? 0 : getValue().hashCode() ) );
>     }
>
>
>
>     /**
>      * Returns the key.
>      *
>      * @return the key
>      */
>     public Object getKey() {
>         return key;
>     }
>
>     /**
>      * Returns the value.
>      *
>      * @return the value
>      */
>     public Object getValue() {
>         return value;
>     }
>
>
>
>     /**
>      * Sets the key.
>      *
>      * @param key  the new key
>      */
>     public void setKey(Object key) {
>         this.key = key;
>     }
>
>     /**
>      * Sets the value.
>      *
>      * @return the old value of the value
>      * @param value the new value
>      */
>     public Object setValue(Object value) {
>         Object answer = this.value;
>         this.value = value;
>         return answer;
>     }
>
> public String toString()
> {
> return new
StringBuffer().append(getKey()).append('=').append(getValue()).toString();
> }
> }
>


----------------------------------------------------------------------------
----


> Index: DefaultMapEntry.java
> ===================================================================
> RCS file:
/home/cvspublic/jakarta-commons/collections/src/java/org/apache/commons/coll
ections/DefaultMapEntry.java,v
> retrieving revision 1.11
> diff -u -r1.11 DefaultMapEntry.java
> --- DefaultMapEntry.java 31 Aug 2003 17:26:43 -0000 1.11
> +++ DefaultMapEntry.java 23 Sep 2003 02:00:14 -0000
> @@ -67,13 +67,10 @@
>   *
>   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
>   * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
> + * @author Neil O'Toole
>   */
> -public class DefaultMapEntry implements Map.Entry {
> +public class DefaultMapEntry extends KeyValuePair implements Map.Entry {
>
> -    /** The key */
> -    private Object key;
> -    /** The value */
> -    private Object value;
>
>      /**
>       * Constructs a new <Code>DefaultMapEntry</Code> with a null key
> @@ -90,20 +87,18 @@
>       * @param value  the value for the entry, may be null
>       */
>      public DefaultMapEntry(Object key, Object value) {
> -        this.key = key;
> -        this.value = value;
> +        super(key, value);
>      }
>
>      /**
>       * Implemented per API documentation of
>       * {@link java.util.Map.Entry#equals(Object)}
>       */
> -    public boolean equals(Object o) {
> -        if( o == null ) return false;
> +    public final boolean equals(Object o) {
>          if( o == this ) return true;
>
> -        if ( ! (o instanceof Map.Entry ) )
> -            return false;
> +        if ( !(o instanceof Map.Entry ) ) return false;
> +
>          Map.Entry e2 = (Map.Entry)o;
>          return ((getKey() == null ?
>                   e2.getKey() == null : getKey().equals(e2.getKey())) &&
> @@ -116,57 +111,12 @@
>       * Implemented per API documentation of
>       * {@link java.util.Map.Entry#hashCode()}
>       */
> -    public int hashCode() {
> +    public final int hashCode() {
>          return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
>                   ( getValue() == null ? 0 : getValue().hashCode() ) );
>      }
>
>
>
> -    // Map.Entry interface
> -
//-------------------------------------------------------------------------
> -
> -    /**
> -     * Returns the key.
> -     *
> -     * @return the key
> -     */
> -    public Object getKey() {
> -        return key;
> -    }
> -
> -    /**
> -     * Returns the value.
> -     *
> -     * @return the value
> -     */
> -    public Object getValue() {
> -        return value;
> -    }
> -
> -    // Properties
> -
//-------------------------------------------------------------------------
> -
> -    /**
> -     * Sets the key.  This method does not modify any map.
> -     *
> -     * @param key  the new key
> -     */
> -    public void setKey(Object key) {
> -        this.key = key;
> -    }
> -
> -    /**
> -     * Note that this method only sets the local reference inside this
object and
> -     * does not modify the original Map.
> -     *
> -     * @return the old value of the value
> -     * @param value the new value
> -     */
> -    public Object setValue(Object value) {
> -        Object answer = this.value;
> -        this.value = value;
> -        return answer;
> -    }
>
>  }
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [collections] Map.Entry and KeyValue

Posted by Stephen Colebourne <sc...@btopenworld.com>.
I'm happy to add it....with a test case :-)
Stephen

----- Original Message -----
From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > My suggested solution is to add a class "KeyValue.java" containing
> > the code from DefaultMapEntry, but not implementing Map.Entry.
> > DefaultMapEntry can then subclass KeyValue and implement Map.Entry,
> > and be declared final.
>
> Suggested solution attached (KeyValuePair.java and patch for
> DefaultMapEntry.java). Note that:
>
> - The superflous null check is removed from DefaultMapEntry#equals
> (instanceof checks for null).
> - The DefaultMapEntry #equals and #hashCode methods are declared final.
> The 'correct' approach is probably that the whole class should be
> declared final - thoughts on this??.
> - KeyValuePair has a toString() method.
>
> Unless there are objections, anyone want to add this to CVS?
>
> Neil
>
>
> --- Neil O'Toole <ne...@users.sourceforge.net> wrote:
> > > > ** I had a swift look through [collections] and [lang], and
> > didn't
> > > see
> > > > a simple KeyValuePair implementation... Have I missed this
> > > somewhere?
> > >
> > > DefaultMapEntry IIRC is the simple Map.Entry impl.
> > > Stephen
> > >
> >
> > Although one (including my good self!) might initially think that a
> > key-value class and a Map.Entry class are interchangeable, this is
> > not
> > in fact the case. DefaultMapEntry is not suitable for use as a
> > general
> > purpose KeyValue class, as it is effectively non-extendable (and
> > there
> > are many cases where it is useful to extend a KeyValue class). In
> > fact,
> > DefaultMapEntry (or at the very least its #equals and #hashcode
> > methods) should be declared final so that users won't make the
> > mistake
> > of subclassing it.
> >
> > DefaultMapEntry is not extendable because the #equals method is
> > contractually specified by Map.Entry to only evalute the members of
> > Map.Entry. A non-trivial subclass of DefaultMapEntry will add members
> > that will be used in #equals comparision. To make use of these
> > members
> > in #equals will of course violate the Map.Entry#equals contract.
> >
> > This non-extensibility property applies to any class that inherits or
> > implements an #equals method with a similar closed #equals contract,
> > and any such class should be declared final to prevent accidental
> > subclassing (or again, at the very least the #hashcode and #equals
> > methods should be declared final, though the user might just ignore
> > those methods, resulting in unfortunate code).
> >
> > My suggested solution is to add a class "KeyValue.java" containing
> > the
> > code from DefaultMapEntry, but not implementing Map.Entry.
> > DefaultMapEntry can then subclass KeyValue and implement Map.Entry,
> > and
> > be declared final.
> >
> > - Neil
> >
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail: commons-dev-help@jakarta.apache.org
> >
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
/home/cvspublic/jakarta-commons/collections/src/java/org/apache/commons/coll
ections/DefaultMapEntry.java,v 1.11 2003/08/31 17:26:43 scolebourne Exp $
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgement:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgement may appear in the software itself,
>  *    if and wherever such third-party acknowledgements 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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;
>
>
>
> /**
>  * A simple key-value pair.
>  *
>  * @since Commons Collections 3.0
>  * @version $Revision: $
>  *
>  * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
>  * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
>  * @author Neil O'Toole
>  */
> public class KeyValuePair{
>
>     /** The key */
>     private Object key;
>     /** The value */
>     private Object value;
>
>     /**
>      * Constructs a new <Code>DefaultMapEntry</Code> with a null key
>      * and null value.
>      */
>     public KeyValuePair() {
>     }
>
>     /**
>      * Constructs a new <Code>DefaultMapEntry</Code> with the given
>      * key and given value.
>      *
>      * @param key  the key for the entry, may be null
>      * @param value  the value for the entry, may be null
>      */
>     public KeyValuePair(Object key, Object value) {
>         this.key = key;
>         this.value = value;
>     }
>
>
> /**
> * Returns true if the compared object is also a <code>KeyValuePair</code>,
> * and its key and value are equal to this object's key and value.
> */
>     public boolean equals(Object o) {
>
>         if ( o == this ) return true;
>
>         if ( o instanceof KeyValuePair == false ) return false;
>
>         KeyValuePair e2 = (KeyValuePair) o;
>         return ((getKey() == null ?
>                  e2.getKey() == null : getKey().equals(e2.getKey())) &&
>                 (getValue() == null ?
>                  e2.getValue() == null :
getValue().equals(e2.getValue())));
>     }
>
>
>
>     public int hashCode() {
>         return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
>                  ( getValue() == null ? 0 : getValue().hashCode() ) );
>     }
>
>
>
>     /**
>      * Returns the key.
>      *
>      * @return the key
>      */
>     public Object getKey() {
>         return key;
>     }
>
>     /**
>      * Returns the value.
>      *
>      * @return the value
>      */
>     public Object getValue() {
>         return value;
>     }
>
>
>
>     /**
>      * Sets the key.
>      *
>      * @param key  the new key
>      */
>     public void setKey(Object key) {
>         this.key = key;
>     }
>
>     /**
>      * Sets the value.
>      *
>      * @return the old value of the value
>      * @param value the new value
>      */
>     public Object setValue(Object value) {
>         Object answer = this.value;
>         this.value = value;
>         return answer;
>     }
>
> public String toString()
> {
> return new
StringBuffer().append(getKey()).append('=').append(getValue()).toString();
> }
> }
>


----------------------------------------------------------------------------
----


> Index: DefaultMapEntry.java
> ===================================================================
> RCS file:
/home/cvspublic/jakarta-commons/collections/src/java/org/apache/commons/coll
ections/DefaultMapEntry.java,v
> retrieving revision 1.11
> diff -u -r1.11 DefaultMapEntry.java
> --- DefaultMapEntry.java 31 Aug 2003 17:26:43 -0000 1.11
> +++ DefaultMapEntry.java 23 Sep 2003 02:00:14 -0000
> @@ -67,13 +67,10 @@
>   *
>   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
>   * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
> + * @author Neil O'Toole
>   */
> -public class DefaultMapEntry implements Map.Entry {
> +public class DefaultMapEntry extends KeyValuePair implements Map.Entry {
>
> -    /** The key */
> -    private Object key;
> -    /** The value */
> -    private Object value;
>
>      /**
>       * Constructs a new <Code>DefaultMapEntry</Code> with a null key
> @@ -90,20 +87,18 @@
>       * @param value  the value for the entry, may be null
>       */
>      public DefaultMapEntry(Object key, Object value) {
> -        this.key = key;
> -        this.value = value;
> +        super(key, value);
>      }
>
>      /**
>       * Implemented per API documentation of
>       * {@link java.util.Map.Entry#equals(Object)}
>       */
> -    public boolean equals(Object o) {
> -        if( o == null ) return false;
> +    public final boolean equals(Object o) {
>          if( o == this ) return true;
>
> -        if ( ! (o instanceof Map.Entry ) )
> -            return false;
> +        if ( !(o instanceof Map.Entry ) ) return false;
> +
>          Map.Entry e2 = (Map.Entry)o;
>          return ((getKey() == null ?
>                   e2.getKey() == null : getKey().equals(e2.getKey())) &&
> @@ -116,57 +111,12 @@
>       * Implemented per API documentation of
>       * {@link java.util.Map.Entry#hashCode()}
>       */
> -    public int hashCode() {
> +    public final int hashCode() {
>          return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
>                   ( getValue() == null ? 0 : getValue().hashCode() ) );
>      }
>
>
>
> -    // Map.Entry interface
> -
//-------------------------------------------------------------------------
> -
> -    /**
> -     * Returns the key.
> -     *
> -     * @return the key
> -     */
> -    public Object getKey() {
> -        return key;
> -    }
> -
> -    /**
> -     * Returns the value.
> -     *
> -     * @return the value
> -     */
> -    public Object getValue() {
> -        return value;
> -    }
> -
> -    // Properties
> -
//-------------------------------------------------------------------------
> -
> -    /**
> -     * Sets the key.  This method does not modify any map.
> -     *
> -     * @param key  the new key
> -     */
> -    public void setKey(Object key) {
> -        this.key = key;
> -    }
> -
> -    /**
> -     * Note that this method only sets the local reference inside this
object and
> -     * does not modify the original Map.
> -     *
> -     * @return the old value of the value
> -     * @param value the new value
> -     */
> -    public Object setValue(Object value) {
> -        Object answer = this.value;
> -        this.value = value;
> -        return answer;
> -    }
>
>  }
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [collections] Map.Entry and KeyValue

Posted by Neil O'Toole <ne...@users.sourceforge.net>.
> My suggested solution is to add a class "KeyValue.java" containing
> the code from DefaultMapEntry, but not implementing Map.Entry.
> DefaultMapEntry can then subclass KeyValue and implement Map.Entry,
> and be declared final. 

Suggested solution attached (KeyValuePair.java and patch for
DefaultMapEntry.java). Note that:

- The superflous null check is removed from DefaultMapEntry#equals
(instanceof checks for null).
- The DefaultMapEntry #equals and #hashCode methods are declared final.
The 'correct' approach is probably that the whole class should be
declared final - thoughts on this??.
- KeyValuePair has a toString() method.

Unless there are objections, anyone want to add this to CVS?

Neil


--- Neil O'Toole <ne...@users.sourceforge.net> wrote:
> > > ** I had a swift look through [collections] and [lang], and
> didn't
> > see
> > > a simple KeyValuePair implementation... Have I missed this
> > somewhere?
> > 
> > DefaultMapEntry IIRC is the simple Map.Entry impl.
> > Stephen
> >
> 
> Although one (including my good self!) might initially think that a
> key-value class and a Map.Entry class are interchangeable, this is
> not
> in fact the case. DefaultMapEntry is not suitable for use as a
> general
> purpose KeyValue class, as it is effectively non-extendable (and
> there
> are many cases where it is useful to extend a KeyValue class). In
> fact,
> DefaultMapEntry (or at the very least its #equals and #hashcode
> methods) should be declared final so that users won't make the
> mistake
> of subclassing it.
> 
> DefaultMapEntry is not extendable because the #equals method is
> contractually specified by Map.Entry to only evalute the members of
> Map.Entry. A non-trivial subclass of DefaultMapEntry will add members
> that will be used in #equals comparision. To make use of these
> members
> in #equals will of course violate the Map.Entry#equals contract.
> 
> This non-extensibility property applies to any class that inherits or
> implements an #equals method with a similar closed #equals contract,
> and any such class should be declared final to prevent accidental
> subclassing (or again, at the very least the #hashcode and #equals
> methods should be declared final, though the user might just ignore
> those methods, resulting in unfortunate code).
> 
> My suggested solution is to add a class "KeyValue.java" containing
> the
> code from DefaultMapEntry, but not implementing Map.Entry.
> DefaultMapEntry can then subclass KeyValue and implement Map.Entry,
> and
> be declared final. 
> 
> - Neil
> 
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org
> 

[collections] Map.Entry and KeyValue

Posted by Neil O'Toole <ne...@users.sourceforge.net>.
> > ** I had a swift look through [collections] and [lang], and didn't
> see
> > a simple KeyValuePair implementation... Have I missed this
> somewhere?
> 
> DefaultMapEntry IIRC is the simple Map.Entry impl.
> Stephen
>

Although one (including my good self!) might initially think that a
key-value class and a Map.Entry class are interchangeable, this is not
in fact the case. DefaultMapEntry is not suitable for use as a general
purpose KeyValue class, as it is effectively non-extendable (and there
are many cases where it is useful to extend a KeyValue class). In fact,
DefaultMapEntry (or at the very least its #equals and #hashcode
methods) should be declared final so that users won't make the mistake
of subclassing it.

DefaultMapEntry is not extendable because the #equals method is
contractually specified by Map.Entry to only evalute the members of
Map.Entry. A non-trivial subclass of DefaultMapEntry will add members
that will be used in #equals comparision. To make use of these members
in #equals will of course violate the Map.Entry#equals contract.

This non-extensibility property applies to any class that inherits or
implements an #equals method with a similar closed #equals contract,
and any such class should be declared final to prevent accidental
subclassing (or again, at the very least the #hashcode and #equals
methods should be declared final, though the user might just ignore
those methods, resulting in unfortunate code).

My suggested solution is to add a class "KeyValue.java" containing the
code from DefaultMapEntry, but not implementing Map.Entry.
DefaultMapEntry can then subclass KeyValue and implement Map.Entry, and
be declared final. 

- Neil



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


[collections] Map.Entry and KeyValue

Posted by Neil O'Toole <ne...@users.sourceforge.net>.
> > ** I had a swift look through [collections] and [lang], and didn't
> see
> > a simple KeyValuePair implementation... Have I missed this
> somewhere?
> 
> DefaultMapEntry IIRC is the simple Map.Entry impl.
> Stephen
>

Although one (including my good self!) might initially think that a
key-value class and a Map.Entry class are interchangeable, this is not
in fact the case. DefaultMapEntry is not suitable for use as a general
purpose KeyValue class, as it is effectively non-extendable (and there
are many cases where it is useful to extend a KeyValue class). In fact,
DefaultMapEntry (or at the very least its #equals and #hashcode
methods) should be declared final so that users won't make the mistake
of subclassing it.

DefaultMapEntry is not extendable because the #equals method is
contractually specified by Map.Entry to only evalute the members of
Map.Entry. A non-trivial subclass of DefaultMapEntry will add members
that will be used in #equals comparision. To make use of these members
in #equals will of course violate the Map.Entry#equals contract.

This non-extensibility property applies to any class that inherits or
implements an #equals method with a similar closed #equals contract,
and any such class should be declared final to prevent accidental
subclassing (or again, at the very least the #hashcode and #equals
methods should be declared final, though the user might just ignore
those methods, resulting in unfortunate code).

My suggested solution is to add a class "KeyValue.java" containing the
code from DefaultMapEntry, but not implementing Map.Entry.
DefaultMapEntry can then subclass KeyValue and implement Map.Entry, and
be declared final. 

- Neil



Re: [collections] submission: KeyValueRecord

Posted by Stephen Colebourne <sc...@btopenworld.com>.
From: "Neil O'Toole" <ne...@users.sourceforge.net>
> --- Stephen Colebourne <sc...@btopenworld.com> wrote:
> > PS. I guess this came from the rich events package, which I would
> > like to see as an implementation of the observed code.
>
> Guilty as charged... unfortunately i've been away from my open-source
> desk for the last month (in fact moving home, and - gasp! - no
> connectivity for weeks on end) and hadn't been able to devote time to
> the notifying/observable work, but i certainly want to integrate the
> code, so I'll be looking into this next chance I get.

Real life always has priority! :-)  Hope you're settling in well.
But hopefully we can grab your rich events soon too. Thanks.
Stephen




> > ----- Original Message -----
> > From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > > > 'Record' confused me and made me think of databases. Is
> > > > KeyValueHistory a better name?
> > >
> > > Stephen, you're probably right. I had been bouncing back and forth
> > > between 'Record' and 'History'... the data structure does *record*
> > a
> > > kv-pair's *history*, but in fact only the most recent part (one
> > state
> > > transition 'record') of the history.
> > >
> > > However, one would expect that a "KeyValueHistory" class should be
> > able
> > > to store the entire history of a key (as well as just a single
> > value).
> > > I hadn't initially needed this capability for my own purposes, but
> > I
> > > now see the usefulness. So,
> > >
> > >  #getPreviousValue : Object
> > >
> > > should probably be replaced with:
> > >
> > >  #getAllValues : List
> > >
> > > What do we think? In terms of design, either the constructors would
> > get
> > > very messy to use, or we need to add factory methods to distinguish
> > > between:
> > >
> > > KeyValueHistory( Object key, Object value)
> > > KeyValueHistory( Object key, List values)
> > >
> > > Attached is an implementation that supports multiple previous
> > values,
> > > and provides static factory methods. Thoughts?
> > >
> > > - Neil
> > >
> > >
> > >
> >
> >
> >
> --------------------------------------------------------------------------
--
> > ----
> >
> >
> > > /*
> > >  * $Header:
> >
>
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/c
> > ommons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32
> > otoolen Exp
> > $
> > >  *
> > ====================================================================
> > >  *
> > >  * The Apache Software License, Version 1.1
> > >  *
> > >  * Copyright (c) 2001-2003 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 acknowledgment:
> > >  *       "This product includes software developed by the
> > >  *        Apache Software Foundation (http://www.apache.org/)."
> > >  *    Alternately, this acknowledgment may appear in the software
> > itself,
> > >  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
> > >  *
> > >  * 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 Software Foundation.
> > >  *
> > >  * 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.util.ArrayList;
> > > import java.util.Collections;
> > > import java.util.List;
> > > import java.util.Map;
> > >
> > > /**
> > >  * An immutable {key, value, previous-value} triplet. This class is
> > frequently used
> > >  * in conjunction with <code>Map.Entry</code>. A constructor is
> > provided
> > to create
> > >  * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and
> > the
> > {@link #asMapEntry}
> > >  * method can be used to view an object of this class as a
> > <code>Map.Entry</code>.
> > >  * Note that it is not possible for <code>KeyValueRecord</code> to
> > implement the <code>Map.Entry</code>
> > >  * interface as the <code>#equals</code> implementations are not
> > compatible.
> > >  *
> > >  *
> > >  *
> > >  * @author Neil O'Toole
> > >  */
> > >
> > > public class KeyValueHistory
> > > {
> > >
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with supplied key and
> > value,
> > > * and no previous values.
> > > */
> > > public static KeyValueHistory createFromKeyValue(final Object key,
> > final
> > Object value)
> > > {
> > > return new KeyValueHistory(key, Collections.singletonList(value));
> > > }
> > >
> > >
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with the specified key
> > and
> > values.
> > > * The supplied list must:
> > > * <ul>
> > > * <li>contain at least one value</li>
> > > * <li>contain the values in reverse-chronological order (i.e. most
> > > * recent value first)</li>
> > > * <li>be immutable</li>
> > > * </ul>
> > > *
> > > * @param values an immutable list of the values associated with
> > this key.
> > > * @throws IllegalArgumentException if <code>values</code> is
> > <code>null</code> or
> > > * is empty. <br />Note: If the value associated with the key is the
> > value
> > <code>null</code>,
> > > * then supply <code>Collections#singletonList( null )</code>.
> > > */
> > > public static KeyValueHistory createFromKeyValueList(final Object
> > key,
> > final List values)
> > > {
> > > if (values == null || values.size() < 1)
> > > {
> > > throw new IllegalArgumentException("The supplied 'values' list must
> > contain at least one value.");
> > > }
> > >
> > > return new KeyValueHistory(key, values);
> > > }
> > >
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with supplied key,
> > value
> > > * and previous value.
> > > */
> > > public static KeyValueHistory createFromKeyValuePrevious(final
> > Object key,
> > final Object value, final Object previous)
> > > {
> > > final List values = new ArrayList(2);
> > > values.add(value);
> > > values.add(previous);
> > >
> > > return new KeyValueHistory(key,
> > Collections.unmodifiableList(values));
> > > }
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with key and value from
> > the
> > supplied
> > > * <code>Map.Entry</code> and no previous value.
> > > */
> > > public static KeyValueHistory createFromMapEntry(final Map.Entry
> > entry)
> > > {
> > > return new KeyValueHistory(entry.getKey(),
> > Collections.singletonList(entry.getValue()));
> > > }
> > >
> > > private int hash = -1; // lazily calculated
> > >
> > > private final Object key;
> > > private final List values;
> > >
> > >
> > >
> > >
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with the specified key
> > and
> > values.
> > > * The supplied list:
> > > * <ul>
> > > * <li>must contain at least one value</li>
> > > * <li>must contain the values in reverse-chronological order (i.e.
> > most
> > > * recent value first)</li>
> > > * <li>must be immutable</li>
> > > *
> > > * @param values an immutable List of the values associated with
> > this key.
> > > */
> > > protected KeyValueHistory(final Object key, final List values)
> > > {
> > > this.key = key;
> > > this.values = values;
> > > }
> > >
> > > /**
> > > * Returns a {@link Map.Entry} view of the supplied
> > <code>KeyValueRecord</code>. The
> > > * returned entry is unmodifiable (<code>#setValue</code> throws an
> > {@link
> > UnsupportedOperationException}),
> > > * as the backing <code>KeyValueRecord</code> is itself immutable.
> > > * The returned entry correctly implements the
> > <code>#hashCode</code> and
> > > * <code>#equals</code> operations as per the <code>Map.Entry</code>
> > contract.
> > > */
> > >
> > > public Map.Entry asMapEntry()
> > > {
> > > return new Map.Entry()
> > > {
> > > private int hash = -1;
> > >
> > > public boolean equals(Object o)
> > > {
> > > if (o instanceof Map.Entry == false)
> > > {
> > > return false;
> > > }
> > >
> > > if (o == this)
> > > {
> > > return true;
> > > }
> > >
> > > Map.Entry e = (Map.Entry) o;
> > >
> > > return (
> > > KeyValueHistory.this.getKey() == null
> > > ? e.getKey() == null
> > > : KeyValueHistory.this.getKey().equals(e.getKey()))
> > > && (KeyValueHistory.this.getValue() == null
> > > ? e.getValue() == null
> > > : KeyValueHistory.this.getValue().equals(e.getValue()));
> > > }
> > >
> > > public Object getKey()
> > > {
> > > return KeyValueHistory.this.getKey();
> > > }
> > >
> > > public Object getValue()
> > > {
> > > return KeyValueHistory.this.getValue();
> > > }
> > >
> > > public int hashCode()
> > > {
> > > if (this.hash == -1)
> > > {
> > > this.hash =
> > > (KeyValueHistory.this.getKey() == null ? 0 :
> > KeyValueHistory.this.getKey().hashCode())
> > > ^ (KeyValueHistory.this.getValue() == null ? 0 :
> > KeyValueHistory.this.getValue().hashCode());
> > > }
> > >
> > > return this.hash;
> > > }
> > >
> > > public Object setValue(Object value)
> > > {
> > > throw new UnsupportedOperationException("This Map.Entry is
> > unmodifiable.");
> > > }
> > >
> > > public String toString()
> > > {
> > > return new StringBuffer()
> > > .append(KeyValueHistory.this.getKey())
> > > .append('=')
> > > .append(KeyValueHistory.this.getValue())
> > > .toString();
> > > }
> > > };
> > >
> > > }
> > >
> > > /**
> > > * Compares the specified object with this
> > <code>KeyValueRecord</code> for
> > equality. Returns
> > > * true if the given object is also a <code>KeyValueRecord</code>
> > and
> > > * the records' key, value, and previous value are equal.
> > > *
> > > * @param o object to be compared for equality with this
> > <code>KeyValueRecord</code>.
> > > * @return <code>true</code> if the specified object is equal to
> > this
> > > *         record.
> > > */
> > > public boolean equals(final Object o)
> > > {
> > > if (!(o instanceof KeyValueHistory))
> > > {
> > > return false;
> > > }
> > >
> > > if (this == o)
> > > {
> > > return true;
> > > }
> > >
> > > final KeyValueHistory kvr = (KeyValueHistory) o;
> > >
> > > return (this.key == null ? kvr.key == null :
> > this.key.equals(kvr.key))
> > > && this.values.equals(kvr.values);
> > > }
> > >
> > > /**
> > > * Returns the first value previously associated with this key, if
> > any.
> > Note that
> > > * <code>null</code> will be returned
> > > * if the previous value is <code>null</code> <i>or</i> if there are
> > no
> > previous values
> > > * Therefore {@link #hasPreviousValues()} should be used to test if
> > there
> > is a previous value.
> > > *
> > > * @return the previous value (which may be <code>null</code>)
> > > * associated with this record's key, or <code>null</code> if there
> > is no
> > > * previous value.
> > > */
> > > /*
> > > public Object getPreviousValue()
> > > {
> > > return (this.values.size() < 2) ? null : this.values.get(1);
> > > }*/
> > >
> > > /*
> > > public List getAllPreviousValues()
> > > {
> > > return this.values.subList(1, values.size() -1);
> > > }
> > > */
> > >
> > > /**
> > > * Return all values associated with this key, in reverse
> > chronological
> > > * order. The most recent (current) value is at index zero, the
> > previous
> > value is
> > > * at index one, the value previous to that at index two, etc.. If
> > there
> > are no
> > > * previous values associated with this key, then list will contain
> > only
> > one element (the current value).
> > > * The returned list is immutable.
> > > *
> > > * @return an immutable list of the values associated with this key,
> > the
> > list having a minimum size of one.
> > > */
> > > public List getAllValues()
> > > {
> > > return this.values;
> > > }
> > >
> > > /**
> > > * Returns the key associated with this record.
> > > */
> > > public Object getKey()
> > > {
> > > return this.key;
> > > }
> > >
> > > /**
> > > * Returns the current (or most recent) value associated with this
> > record's
> > key.
> > > *
> > > * @return the value (which may be <code>null</code>)
> > > * associated with this record's key
> > > */
> > > public Object getValue()
> > > {
> > > return this.values.get(0);
> > > }
> > >
> > > public int hashCode()
> > > {
> > > if (this.hash == -1)
> > > {
> > > this.hash =
> > > (this.key == null ? 0 : this.key.hashCode())
> > > ^ this.values.hashCode();
> > > }
> > >
> > > return this.hash;
> > > }
> > >
> > > /**
> > > * Returns true if this record's key
> > > * was associated with a value previous to being
> > > * associated with its current value.
> > > *
> > > * @return <code>true</code> if this key previously had a value
> > associated
> > > * with it, <code>false</code> otherwise.
> > > * @see #getPreviousValue()
> > > */
> > > public boolean hasPreviousValues()
> > > {
> > > return this.values.size() > 1;
> > > }
> > >
> > > /**
> > > * Returns a string representation of this
> > <code>KeyValueHistory</code>.
> > > *
> > > */
> > > public String toString()
> > > {
> > > final StringBuffer sb = new
> > StringBuffer().append(this.key).append('=').append(this.values);
> > >
> > > return sb.toString();
> > > }
> > >
> > > }
> > >
> >
> >
> >
> --------------------------------------------------------------------------
--
> > ----
> >
> >
> > > /*
> > >  * $Header:
> >
>
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
> > ommons/collections/TestKeyValueHistory.java,v 1.1 2003/09/20 22:00:46
> > otoolen Exp $
> > >  *
> > ====================================================================
> > >  *
> > >  * The Apache Software License, Version 1.1
> > >  *
> > >  * Copyright (c) 2001-2003 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 acknowledgment:
> > >  *       "This product includes software developed by the
> > >  *        Apache Software Foundation (http://www.apache.org/)."
> > >  *    Alternately, this acknowledgment may appear in the software
> > itself,
> > >  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
> > >  *
> > >  * 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 Software Foundation.
> > >  *
> > >  * 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.util.HashMap;
> > > import java.util.Map;
> > >
> > > import junit.framework.TestCase;
> > >
> > >
> > > /**
> > >  *
> > >  * @author Neil O'Toole
> > >  */
> > > public class TestKeyValueHistory extends TestCase
> > > {
> > > private final String key = "key";
> > > private final String value = "value";
> > > private final String previous = "previous";
> > >
> > > public TestKeyValueHistory(String testName)
> > > {
> > > super(testName);
> > >
> > > }
> > >
> > > public static void main(String[] args)
> > > {
> > > junit.textui.TestRunner.run(TestKeyValueHistory.class);
> > > }
> > >
> > > public void testKeyValueHistory()
> > > {
> > >
> > > // KVR with no previous value
> > > KeyValueHistory kvr = KeyValueHistory.createFromKeyValue(key,
> > value);
> > >
> > > assertTrue(
> > > kvr.getKey() == key
> > > && kvr.getValue() == value
> > > && kvr.hasPreviousValues() == false);
> > >
> > > assertTrue(kvr.equals(kvr));
> > > assertTrue(kvr.toString().equals("key=[value]"));
> > >
> > > // KVR with a previous value
> > > KeyValueHistory kvr2 =
> > KeyValueHistory.createFromKeyValuePrevious(key,
> > value, previous);
> > >
> > > assertTrue(
> > > kvr2.getKey() == key
> > > && kvr2.getValue() == value
> > > && kvr2.hasPreviousValues() == true
> > > && kvr2.getAllValues().get(1) == previous);
> > >
> > > assertTrue(kvr2.equals(kvr2));
> > > assertTrue(kvr2.toString().equals("key=[value, previous]"));
> > >
> > > assertFalse(kvr.equals(kvr2));
> > > assertFalse(kvr.hashCode() == kvr2.hashCode());
> > >
> > > // test that a previous value of 'null' is treated differently to
> > no
> > previous value
> > > KeyValueHistory kvr3 =
> > KeyValueHistory.createFromKeyValuePrevious(key,
> > value, null);
> > > assertTrue(kvr3.equals(kvr3));
> > > assertTrue(kvr3.toString().equals("key=[value, null]"));
> > > assertFalse(kvr3.equals(kvr));
> > > assertFalse(kvr3.equals(kvr2));
> > >
> > > assertFalse(kvr3.hashCode() == kvr2.hashCode());
> > > assertFalse(kvr3.hashCode() == kvr.hashCode());
> > >
> > > // test the Map.Entry handling
> > > Map map = new HashMap();
> > > map.put(key, value);
> > >
> > > Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
> > >
> > > KeyValueHistory kvr4 = KeyValueHistory.createFromMapEntry(entry);
> > >
> > > assertTrue(
> > > kvr4.getKey() == key
> > > && kvr4.getValue() == value
> > > && kvr4.hasPreviousValues() == false);
> > >
> > > assertTrue(kvr4.equals(kvr4));
> > > assertTrue(kvr4.equals(kvr));
> > > assertTrue(kvr4.toString().equals("key=[value]"));
> > >
> > > assertFalse("Map.Entry and KeyValueHistory are never equal",
> > kvr4.equals(entry));
> > >
> > > Map.Entry entry2 = kvr4.asMapEntry();
> > >
> > > assertTrue(entry.equals(entry2));
> > > assertTrue(entry.hashCode() == entry2.hashCode());
> > >
> > > }
> > >
> > > }
> > >
> > >
> >
> >
> >
> --------------------------------------------------------------------------
--
> > ----
> >
> >
> > >
> > ---------------------------------------------------------------------
> > > To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> > > For additional commands, e-mail:
> > commons-dev-help@jakarta.apache.org
> >
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org
>


Re: [collections] submission: KeyValueRecord

Posted by Stephen Colebourne <sc...@btopenworld.com>.
From: "Neil O'Toole" <ne...@users.sourceforge.net>
> --- Stephen Colebourne <sc...@btopenworld.com> wrote:
> > PS. I guess this came from the rich events package, which I would
> > like to see as an implementation of the observed code.
>
> Guilty as charged... unfortunately i've been away from my open-source
> desk for the last month (in fact moving home, and - gasp! - no
> connectivity for weeks on end) and hadn't been able to devote time to
> the notifying/observable work, but i certainly want to integrate the
> code, so I'll be looking into this next chance I get.

Real life always has priority! :-)  Hope you're settling in well.
But hopefully we can grab your rich events soon too. Thanks.
Stephen




> > ----- Original Message -----
> > From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > > > 'Record' confused me and made me think of databases. Is
> > > > KeyValueHistory a better name?
> > >
> > > Stephen, you're probably right. I had been bouncing back and forth
> > > between 'Record' and 'History'... the data structure does *record*
> > a
> > > kv-pair's *history*, but in fact only the most recent part (one
> > state
> > > transition 'record') of the history.
> > >
> > > However, one would expect that a "KeyValueHistory" class should be
> > able
> > > to store the entire history of a key (as well as just a single
> > value).
> > > I hadn't initially needed this capability for my own purposes, but
> > I
> > > now see the usefulness. So,
> > >
> > >  #getPreviousValue : Object
> > >
> > > should probably be replaced with:
> > >
> > >  #getAllValues : List
> > >
> > > What do we think? In terms of design, either the constructors would
> > get
> > > very messy to use, or we need to add factory methods to distinguish
> > > between:
> > >
> > > KeyValueHistory( Object key, Object value)
> > > KeyValueHistory( Object key, List values)
> > >
> > > Attached is an implementation that supports multiple previous
> > values,
> > > and provides static factory methods. Thoughts?
> > >
> > > - Neil
> > >
> > >
> > >
> >
> >
> >
> --------------------------------------------------------------------------
--
> > ----
> >
> >
> > > /*
> > >  * $Header:
> >
>
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/c
> > ommons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32
> > otoolen Exp
> > $
> > >  *
> > ====================================================================
> > >  *
> > >  * The Apache Software License, Version 1.1
> > >  *
> > >  * Copyright (c) 2001-2003 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 acknowledgment:
> > >  *       "This product includes software developed by the
> > >  *        Apache Software Foundation (http://www.apache.org/)."
> > >  *    Alternately, this acknowledgment may appear in the software
> > itself,
> > >  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
> > >  *
> > >  * 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 Software Foundation.
> > >  *
> > >  * 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.util.ArrayList;
> > > import java.util.Collections;
> > > import java.util.List;
> > > import java.util.Map;
> > >
> > > /**
> > >  * An immutable {key, value, previous-value} triplet. This class is
> > frequently used
> > >  * in conjunction with <code>Map.Entry</code>. A constructor is
> > provided
> > to create
> > >  * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and
> > the
> > {@link #asMapEntry}
> > >  * method can be used to view an object of this class as a
> > <code>Map.Entry</code>.
> > >  * Note that it is not possible for <code>KeyValueRecord</code> to
> > implement the <code>Map.Entry</code>
> > >  * interface as the <code>#equals</code> implementations are not
> > compatible.
> > >  *
> > >  *
> > >  *
> > >  * @author Neil O'Toole
> > >  */
> > >
> > > public class KeyValueHistory
> > > {
> > >
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with supplied key and
> > value,
> > > * and no previous values.
> > > */
> > > public static KeyValueHistory createFromKeyValue(final Object key,
> > final
> > Object value)
> > > {
> > > return new KeyValueHistory(key, Collections.singletonList(value));
> > > }
> > >
> > >
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with the specified key
> > and
> > values.
> > > * The supplied list must:
> > > * <ul>
> > > * <li>contain at least one value</li>
> > > * <li>contain the values in reverse-chronological order (i.e. most
> > > * recent value first)</li>
> > > * <li>be immutable</li>
> > > * </ul>
> > > *
> > > * @param values an immutable list of the values associated with
> > this key.
> > > * @throws IllegalArgumentException if <code>values</code> is
> > <code>null</code> or
> > > * is empty. <br />Note: If the value associated with the key is the
> > value
> > <code>null</code>,
> > > * then supply <code>Collections#singletonList( null )</code>.
> > > */
> > > public static KeyValueHistory createFromKeyValueList(final Object
> > key,
> > final List values)
> > > {
> > > if (values == null || values.size() < 1)
> > > {
> > > throw new IllegalArgumentException("The supplied 'values' list must
> > contain at least one value.");
> > > }
> > >
> > > return new KeyValueHistory(key, values);
> > > }
> > >
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with supplied key,
> > value
> > > * and previous value.
> > > */
> > > public static KeyValueHistory createFromKeyValuePrevious(final
> > Object key,
> > final Object value, final Object previous)
> > > {
> > > final List values = new ArrayList(2);
> > > values.add(value);
> > > values.add(previous);
> > >
> > > return new KeyValueHistory(key,
> > Collections.unmodifiableList(values));
> > > }
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with key and value from
> > the
> > supplied
> > > * <code>Map.Entry</code> and no previous value.
> > > */
> > > public static KeyValueHistory createFromMapEntry(final Map.Entry
> > entry)
> > > {
> > > return new KeyValueHistory(entry.getKey(),
> > Collections.singletonList(entry.getValue()));
> > > }
> > >
> > > private int hash = -1; // lazily calculated
> > >
> > > private final Object key;
> > > private final List values;
> > >
> > >
> > >
> > >
> > > /**
> > > * Create a new <code>KeyValueHistory</code> with the specified key
> > and
> > values.
> > > * The supplied list:
> > > * <ul>
> > > * <li>must contain at least one value</li>
> > > * <li>must contain the values in reverse-chronological order (i.e.
> > most
> > > * recent value first)</li>
> > > * <li>must be immutable</li>
> > > *
> > > * @param values an immutable List of the values associated with
> > this key.
> > > */
> > > protected KeyValueHistory(final Object key, final List values)
> > > {
> > > this.key = key;
> > > this.values = values;
> > > }
> > >
> > > /**
> > > * Returns a {@link Map.Entry} view of the supplied
> > <code>KeyValueRecord</code>. The
> > > * returned entry is unmodifiable (<code>#setValue</code> throws an
> > {@link
> > UnsupportedOperationException}),
> > > * as the backing <code>KeyValueRecord</code> is itself immutable.
> > > * The returned entry correctly implements the
> > <code>#hashCode</code> and
> > > * <code>#equals</code> operations as per the <code>Map.Entry</code>
> > contract.
> > > */
> > >
> > > public Map.Entry asMapEntry()
> > > {
> > > return new Map.Entry()
> > > {
> > > private int hash = -1;
> > >
> > > public boolean equals(Object o)
> > > {
> > > if (o instanceof Map.Entry == false)
> > > {
> > > return false;
> > > }
> > >
> > > if (o == this)
> > > {
> > > return true;
> > > }
> > >
> > > Map.Entry e = (Map.Entry) o;
> > >
> > > return (
> > > KeyValueHistory.this.getKey() == null
> > > ? e.getKey() == null
> > > : KeyValueHistory.this.getKey().equals(e.getKey()))
> > > && (KeyValueHistory.this.getValue() == null
> > > ? e.getValue() == null
> > > : KeyValueHistory.this.getValue().equals(e.getValue()));
> > > }
> > >
> > > public Object getKey()
> > > {
> > > return KeyValueHistory.this.getKey();
> > > }
> > >
> > > public Object getValue()
> > > {
> > > return KeyValueHistory.this.getValue();
> > > }
> > >
> > > public int hashCode()
> > > {
> > > if (this.hash == -1)
> > > {
> > > this.hash =
> > > (KeyValueHistory.this.getKey() == null ? 0 :
> > KeyValueHistory.this.getKey().hashCode())
> > > ^ (KeyValueHistory.this.getValue() == null ? 0 :
> > KeyValueHistory.this.getValue().hashCode());
> > > }
> > >
> > > return this.hash;
> > > }
> > >
> > > public Object setValue(Object value)
> > > {
> > > throw new UnsupportedOperationException("This Map.Entry is
> > unmodifiable.");
> > > }
> > >
> > > public String toString()
> > > {
> > > return new StringBuffer()
> > > .append(KeyValueHistory.this.getKey())
> > > .append('=')
> > > .append(KeyValueHistory.this.getValue())
> > > .toString();
> > > }
> > > };
> > >
> > > }
> > >
> > > /**
> > > * Compares the specified object with this
> > <code>KeyValueRecord</code> for
> > equality. Returns
> > > * true if the given object is also a <code>KeyValueRecord</code>
> > and
> > > * the records' key, value, and previous value are equal.
> > > *
> > > * @param o object to be compared for equality with this
> > <code>KeyValueRecord</code>.
> > > * @return <code>true</code> if the specified object is equal to
> > this
> > > *         record.
> > > */
> > > public boolean equals(final Object o)
> > > {
> > > if (!(o instanceof KeyValueHistory))
> > > {
> > > return false;
> > > }
> > >
> > > if (this == o)
> > > {
> > > return true;
> > > }
> > >
> > > final KeyValueHistory kvr = (KeyValueHistory) o;
> > >
> > > return (this.key == null ? kvr.key == null :
> > this.key.equals(kvr.key))
> > > && this.values.equals(kvr.values);
> > > }
> > >
> > > /**
> > > * Returns the first value previously associated with this key, if
> > any.
> > Note that
> > > * <code>null</code> will be returned
> > > * if the previous value is <code>null</code> <i>or</i> if there are
> > no
> > previous values
> > > * Therefore {@link #hasPreviousValues()} should be used to test if
> > there
> > is a previous value.
> > > *
> > > * @return the previous value (which may be <code>null</code>)
> > > * associated with this record's key, or <code>null</code> if there
> > is no
> > > * previous value.
> > > */
> > > /*
> > > public Object getPreviousValue()
> > > {
> > > return (this.values.size() < 2) ? null : this.values.get(1);
> > > }*/
> > >
> > > /*
> > > public List getAllPreviousValues()
> > > {
> > > return this.values.subList(1, values.size() -1);
> > > }
> > > */
> > >
> > > /**
> > > * Return all values associated with this key, in reverse
> > chronological
> > > * order. The most recent (current) value is at index zero, the
> > previous
> > value is
> > > * at index one, the value previous to that at index two, etc.. If
> > there
> > are no
> > > * previous values associated with this key, then list will contain
> > only
> > one element (the current value).
> > > * The returned list is immutable.
> > > *
> > > * @return an immutable list of the values associated with this key,
> > the
> > list having a minimum size of one.
> > > */
> > > public List getAllValues()
> > > {
> > > return this.values;
> > > }
> > >
> > > /**
> > > * Returns the key associated with this record.
> > > */
> > > public Object getKey()
> > > {
> > > return this.key;
> > > }
> > >
> > > /**
> > > * Returns the current (or most recent) value associated with this
> > record's
> > key.
> > > *
> > > * @return the value (which may be <code>null</code>)
> > > * associated with this record's key
> > > */
> > > public Object getValue()
> > > {
> > > return this.values.get(0);
> > > }
> > >
> > > public int hashCode()
> > > {
> > > if (this.hash == -1)
> > > {
> > > this.hash =
> > > (this.key == null ? 0 : this.key.hashCode())
> > > ^ this.values.hashCode();
> > > }
> > >
> > > return this.hash;
> > > }
> > >
> > > /**
> > > * Returns true if this record's key
> > > * was associated with a value previous to being
> > > * associated with its current value.
> > > *
> > > * @return <code>true</code> if this key previously had a value
> > associated
> > > * with it, <code>false</code> otherwise.
> > > * @see #getPreviousValue()
> > > */
> > > public boolean hasPreviousValues()
> > > {
> > > return this.values.size() > 1;
> > > }
> > >
> > > /**
> > > * Returns a string representation of this
> > <code>KeyValueHistory</code>.
> > > *
> > > */
> > > public String toString()
> > > {
> > > final StringBuffer sb = new
> > StringBuffer().append(this.key).append('=').append(this.values);
> > >
> > > return sb.toString();
> > > }
> > >
> > > }
> > >
> >
> >
> >
> --------------------------------------------------------------------------
--
> > ----
> >
> >
> > > /*
> > >  * $Header:
> >
>
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
> > ommons/collections/TestKeyValueHistory.java,v 1.1 2003/09/20 22:00:46
> > otoolen Exp $
> > >  *
> > ====================================================================
> > >  *
> > >  * The Apache Software License, Version 1.1
> > >  *
> > >  * Copyright (c) 2001-2003 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 acknowledgment:
> > >  *       "This product includes software developed by the
> > >  *        Apache Software Foundation (http://www.apache.org/)."
> > >  *    Alternately, this acknowledgment may appear in the software
> > itself,
> > >  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
> > >  *
> > >  * 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 Software Foundation.
> > >  *
> > >  * 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.util.HashMap;
> > > import java.util.Map;
> > >
> > > import junit.framework.TestCase;
> > >
> > >
> > > /**
> > >  *
> > >  * @author Neil O'Toole
> > >  */
> > > public class TestKeyValueHistory extends TestCase
> > > {
> > > private final String key = "key";
> > > private final String value = "value";
> > > private final String previous = "previous";
> > >
> > > public TestKeyValueHistory(String testName)
> > > {
> > > super(testName);
> > >
> > > }
> > >
> > > public static void main(String[] args)
> > > {
> > > junit.textui.TestRunner.run(TestKeyValueHistory.class);
> > > }
> > >
> > > public void testKeyValueHistory()
> > > {
> > >
> > > // KVR with no previous value
> > > KeyValueHistory kvr = KeyValueHistory.createFromKeyValue(key,
> > value);
> > >
> > > assertTrue(
> > > kvr.getKey() == key
> > > && kvr.getValue() == value
> > > && kvr.hasPreviousValues() == false);
> > >
> > > assertTrue(kvr.equals(kvr));
> > > assertTrue(kvr.toString().equals("key=[value]"));
> > >
> > > // KVR with a previous value
> > > KeyValueHistory kvr2 =
> > KeyValueHistory.createFromKeyValuePrevious(key,
> > value, previous);
> > >
> > > assertTrue(
> > > kvr2.getKey() == key
> > > && kvr2.getValue() == value
> > > && kvr2.hasPreviousValues() == true
> > > && kvr2.getAllValues().get(1) == previous);
> > >
> > > assertTrue(kvr2.equals(kvr2));
> > > assertTrue(kvr2.toString().equals("key=[value, previous]"));
> > >
> > > assertFalse(kvr.equals(kvr2));
> > > assertFalse(kvr.hashCode() == kvr2.hashCode());
> > >
> > > // test that a previous value of 'null' is treated differently to
> > no
> > previous value
> > > KeyValueHistory kvr3 =
> > KeyValueHistory.createFromKeyValuePrevious(key,
> > value, null);
> > > assertTrue(kvr3.equals(kvr3));
> > > assertTrue(kvr3.toString().equals("key=[value, null]"));
> > > assertFalse(kvr3.equals(kvr));
> > > assertFalse(kvr3.equals(kvr2));
> > >
> > > assertFalse(kvr3.hashCode() == kvr2.hashCode());
> > > assertFalse(kvr3.hashCode() == kvr.hashCode());
> > >
> > > // test the Map.Entry handling
> > > Map map = new HashMap();
> > > map.put(key, value);
> > >
> > > Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
> > >
> > > KeyValueHistory kvr4 = KeyValueHistory.createFromMapEntry(entry);
> > >
> > > assertTrue(
> > > kvr4.getKey() == key
> > > && kvr4.getValue() == value
> > > && kvr4.hasPreviousValues() == false);
> > >
> > > assertTrue(kvr4.equals(kvr4));
> > > assertTrue(kvr4.equals(kvr));
> > > assertTrue(kvr4.toString().equals("key=[value]"));
> > >
> > > assertFalse("Map.Entry and KeyValueHistory are never equal",
> > kvr4.equals(entry));
> > >
> > > Map.Entry entry2 = kvr4.asMapEntry();
> > >
> > > assertTrue(entry.equals(entry2));
> > > assertTrue(entry.hashCode() == entry2.hashCode());
> > >
> > > }
> > >
> > > }
> > >
> > >
> >
> >
> >
> --------------------------------------------------------------------------
--
> > ----
> >
> >
> > >
> > ---------------------------------------------------------------------
> > > To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> > > For additional commands, e-mail:
> > commons-dev-help@jakarta.apache.org
> >
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org
>


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [collections] submission: KeyValueRecord

Posted by Neil O'Toole <ne...@users.sourceforge.net>.
--- Stephen Colebourne <sc...@btopenworld.com> wrote:
> I'm currently struggling to understand why I would need to use a
> HistoricMapEntry (better name?).
> It has a part in the undo events code you wrote I believe, but
> outside that
> where does it fit and what use is it? Use cases please :-)
> 
> Stephen

The better half is throwing dirty looks at me (I'm supposed to be
painting the house)  - I'll get back to you on the use cases asap.


> PS. I guess this came from the rich events package, which I would
> like to see as an implementation of the observed code.

Guilty as charged... unfortunately i've been away from my open-source
desk for the last month (in fact moving home, and - gasp! - no
connectivity for weeks on end) and hadn't been able to devote time to
the notifying/observable work, but i certainly want to integrate the
code, so I'll be looking into this next chance I get.

Neil




> ----- Original Message -----
> From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > > 'Record' confused me and made me think of databases. Is
> > > KeyValueHistory a better name?
> >
> > Stephen, you're probably right. I had been bouncing back and forth
> > between 'Record' and 'History'... the data structure does *record* 
> a
> > kv-pair's *history*, but in fact only the most recent part (one
> state
> > transition 'record') of the history.
> >
> > However, one would expect that a "KeyValueHistory" class should be
> able
> > to store the entire history of a key (as well as just a single
> value).
> > I hadn't initially needed this capability for my own purposes, but
> I
> > now see the usefulness. So,
> >
> >  #getPreviousValue : Object
> >
> > should probably be replaced with:
> >
> >  #getAllValues : List
> >
> > What do we think? In terms of design, either the constructors would
> get
> > very messy to use, or we need to add factory methods to distinguish
> > between:
> >
> > KeyValueHistory( Object key, Object value)
> > KeyValueHistory( Object key, List values)
> >
> > Attached is an implementation that supports multiple previous
> values,
> > and provides static factory methods. Thoughts?
> >
> > - Neil
> >
> >
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> > /*
> >  * $Header:
>
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/c
> ommons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32
> otoolen Exp
> $
> >  *
> ====================================================================
> >  *
> >  * The Apache Software License, Version 1.1
> >  *
> >  * Copyright (c) 2001-2003 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 acknowledgment:
> >  *       "This product includes software developed by the
> >  *        Apache Software Foundation (http://www.apache.org/)."
> >  *    Alternately, this acknowledgment may appear in the software
> itself,
> >  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
> >  *
> >  * 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 Software Foundation.
> >  *
> >  * 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.util.ArrayList;
> > import java.util.Collections;
> > import java.util.List;
> > import java.util.Map;
> >
> > /**
> >  * An immutable {key, value, previous-value} triplet. This class is
> frequently used
> >  * in conjunction with <code>Map.Entry</code>. A constructor is
> provided
> to create
> >  * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and
> the
> {@link #asMapEntry}
> >  * method can be used to view an object of this class as a
> <code>Map.Entry</code>.
> >  * Note that it is not possible for <code>KeyValueRecord</code> to
> implement the <code>Map.Entry</code>
> >  * interface as the <code>#equals</code> implementations are not
> compatible.
> >  *
> >  *
> >  *
> >  * @author Neil O'Toole
> >  */
> >
> > public class KeyValueHistory
> > {
> >
> > /**
> > * Create a new <code>KeyValueHistory</code> with supplied key and
> value,
> > * and no previous values.
> > */
> > public static KeyValueHistory createFromKeyValue(final Object key,
> final
> Object value)
> > {
> > return new KeyValueHistory(key, Collections.singletonList(value));
> > }
> >
> >
> > /**
> > * Create a new <code>KeyValueHistory</code> with the specified key
> and
> values.
> > * The supplied list must:
> > * <ul>
> > * <li>contain at least one value</li>
> > * <li>contain the values in reverse-chronological order (i.e. most
> > * recent value first)</li>
> > * <li>be immutable</li>
> > * </ul>
> > *
> > * @param values an immutable list of the values associated with
> this key.
> > * @throws IllegalArgumentException if <code>values</code> is
> <code>null</code> or
> > * is empty. <br />Note: If the value associated with the key is the
> value
> <code>null</code>,
> > * then supply <code>Collections#singletonList( null )</code>.
> > */
> > public static KeyValueHistory createFromKeyValueList(final Object
> key,
> final List values)
> > {
> > if (values == null || values.size() < 1)
> > {
> > throw new IllegalArgumentException("The supplied 'values' list must
> contain at least one value.");
> > }
> >
> > return new KeyValueHistory(key, values);
> > }
> >
> > /**
> > * Create a new <code>KeyValueHistory</code> with supplied key,
> value
> > * and previous value.
> > */
> > public static KeyValueHistory createFromKeyValuePrevious(final
> Object key,
> final Object value, final Object previous)
> > {
> > final List values = new ArrayList(2);
> > values.add(value);
> > values.add(previous);
> >
> > return new KeyValueHistory(key,
> Collections.unmodifiableList(values));
> > }
> > /**
> > * Create a new <code>KeyValueHistory</code> with key and value from
> the
> supplied
> > * <code>Map.Entry</code> and no previous value.
> > */
> > public static KeyValueHistory createFromMapEntry(final Map.Entry
> entry)
> > {
> > return new KeyValueHistory(entry.getKey(),
> Collections.singletonList(entry.getValue()));
> > }
> >
> > private int hash = -1; // lazily calculated
> >
> > private final Object key;
> > private final List values;
> >
> >
> >
> >
> > /**
> > * Create a new <code>KeyValueHistory</code> with the specified key
> and
> values.
> > * The supplied list:
> > * <ul>
> > * <li>must contain at least one value</li>
> > * <li>must contain the values in reverse-chronological order (i.e.
> most
> > * recent value first)</li>
> > * <li>must be immutable</li>
> > *
> > * @param values an immutable List of the values associated with
> this key.
> > */
> > protected KeyValueHistory(final Object key, final List values)
> > {
> > this.key = key;
> > this.values = values;
> > }
> >
> > /**
> > * Returns a {@link Map.Entry} view of the supplied
> <code>KeyValueRecord</code>. The
> > * returned entry is unmodifiable (<code>#setValue</code> throws an
> {@link
> UnsupportedOperationException}),
> > * as the backing <code>KeyValueRecord</code> is itself immutable.
> > * The returned entry correctly implements the
> <code>#hashCode</code> and
> > * <code>#equals</code> operations as per the <code>Map.Entry</code>
> contract.
> > */
> >
> > public Map.Entry asMapEntry()
> > {
> > return new Map.Entry()
> > {
> > private int hash = -1;
> >
> > public boolean equals(Object o)
> > {
> > if (o instanceof Map.Entry == false)
> > {
> > return false;
> > }
> >
> > if (o == this)
> > {
> > return true;
> > }
> >
> > Map.Entry e = (Map.Entry) o;
> >
> > return (
> > KeyValueHistory.this.getKey() == null
> > ? e.getKey() == null
> > : KeyValueHistory.this.getKey().equals(e.getKey()))
> > && (KeyValueHistory.this.getValue() == null
> > ? e.getValue() == null
> > : KeyValueHistory.this.getValue().equals(e.getValue()));
> > }
> >
> > public Object getKey()
> > {
> > return KeyValueHistory.this.getKey();
> > }
> >
> > public Object getValue()
> > {
> > return KeyValueHistory.this.getValue();
> > }
> >
> > public int hashCode()
> > {
> > if (this.hash == -1)
> > {
> > this.hash =
> > (KeyValueHistory.this.getKey() == null ? 0 :
> KeyValueHistory.this.getKey().hashCode())
> > ^ (KeyValueHistory.this.getValue() == null ? 0 :
> KeyValueHistory.this.getValue().hashCode());
> > }
> >
> > return this.hash;
> > }
> >
> > public Object setValue(Object value)
> > {
> > throw new UnsupportedOperationException("This Map.Entry is
> unmodifiable.");
> > }
> >
> > public String toString()
> > {
> > return new StringBuffer()
> > .append(KeyValueHistory.this.getKey())
> > .append('=')
> > .append(KeyValueHistory.this.getValue())
> > .toString();
> > }
> > };
> >
> > }
> >
> > /**
> > * Compares the specified object with this
> <code>KeyValueRecord</code> for
> equality. Returns
> > * true if the given object is also a <code>KeyValueRecord</code>
> and
> > * the records' key, value, and previous value are equal.
> > *
> > * @param o object to be compared for equality with this
> <code>KeyValueRecord</code>.
> > * @return <code>true</code> if the specified object is equal to
> this
> > *         record.
> > */
> > public boolean equals(final Object o)
> > {
> > if (!(o instanceof KeyValueHistory))
> > {
> > return false;
> > }
> >
> > if (this == o)
> > {
> > return true;
> > }
> >
> > final KeyValueHistory kvr = (KeyValueHistory) o;
> >
> > return (this.key == null ? kvr.key == null :
> this.key.equals(kvr.key))
> > && this.values.equals(kvr.values);
> > }
> >
> > /**
> > * Returns the first value previously associated with this key, if
> any.
> Note that
> > * <code>null</code> will be returned
> > * if the previous value is <code>null</code> <i>or</i> if there are
> no
> previous values
> > * Therefore {@link #hasPreviousValues()} should be used to test if
> there
> is a previous value.
> > *
> > * @return the previous value (which may be <code>null</code>)
> > * associated with this record's key, or <code>null</code> if there
> is no
> > * previous value.
> > */
> > /*
> > public Object getPreviousValue()
> > {
> > return (this.values.size() < 2) ? null : this.values.get(1);
> > }*/
> >
> > /*
> > public List getAllPreviousValues()
> > {
> > return this.values.subList(1, values.size() -1);
> > }
> > */
> >
> > /**
> > * Return all values associated with this key, in reverse
> chronological
> > * order. The most recent (current) value is at index zero, the
> previous
> value is
> > * at index one, the value previous to that at index two, etc.. If
> there
> are no
> > * previous values associated with this key, then list will contain
> only
> one element (the current value).
> > * The returned list is immutable.
> > *
> > * @return an immutable list of the values associated with this key,
> the
> list having a minimum size of one.
> > */
> > public List getAllValues()
> > {
> > return this.values;
> > }
> >
> > /**
> > * Returns the key associated with this record.
> > */
> > public Object getKey()
> > {
> > return this.key;
> > }
> >
> > /**
> > * Returns the current (or most recent) value associated with this
> record's
> key.
> > *
> > * @return the value (which may be <code>null</code>)
> > * associated with this record's key
> > */
> > public Object getValue()
> > {
> > return this.values.get(0);
> > }
> >
> > public int hashCode()
> > {
> > if (this.hash == -1)
> > {
> > this.hash =
> > (this.key == null ? 0 : this.key.hashCode())
> > ^ this.values.hashCode();
> > }
> >
> > return this.hash;
> > }
> >
> > /**
> > * Returns true if this record's key
> > * was associated with a value previous to being
> > * associated with its current value.
> > *
> > * @return <code>true</code> if this key previously had a value
> associated
> > * with it, <code>false</code> otherwise.
> > * @see #getPreviousValue()
> > */
> > public boolean hasPreviousValues()
> > {
> > return this.values.size() > 1;
> > }
> >
> > /**
> > * Returns a string representation of this
> <code>KeyValueHistory</code>.
> > *
> > */
> > public String toString()
> > {
> > final StringBuffer sb = new
> StringBuffer().append(this.key).append('=').append(this.values);
> >
> > return sb.toString();
> > }
> >
> > }
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> > /*
> >  * $Header:
>
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
> ommons/collections/TestKeyValueHistory.java,v 1.1 2003/09/20 22:00:46
> otoolen Exp $
> >  *
> ====================================================================
> >  *
> >  * The Apache Software License, Version 1.1
> >  *
> >  * Copyright (c) 2001-2003 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 acknowledgment:
> >  *       "This product includes software developed by the
> >  *        Apache Software Foundation (http://www.apache.org/)."
> >  *    Alternately, this acknowledgment may appear in the software
> itself,
> >  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
> >  *
> >  * 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 Software Foundation.
> >  *
> >  * 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.util.HashMap;
> > import java.util.Map;
> >
> > import junit.framework.TestCase;
> >
> >
> > /**
> >  *
> >  * @author Neil O'Toole
> >  */
> > public class TestKeyValueHistory extends TestCase
> > {
> > private final String key = "key";
> > private final String value = "value";
> > private final String previous = "previous";
> >
> > public TestKeyValueHistory(String testName)
> > {
> > super(testName);
> >
> > }
> >
> > public static void main(String[] args)
> > {
> > junit.textui.TestRunner.run(TestKeyValueHistory.class);
> > }
> >
> > public void testKeyValueHistory()
> > {
> >
> > // KVR with no previous value
> > KeyValueHistory kvr = KeyValueHistory.createFromKeyValue(key,
> value);
> >
> > assertTrue(
> > kvr.getKey() == key
> > && kvr.getValue() == value
> > && kvr.hasPreviousValues() == false);
> >
> > assertTrue(kvr.equals(kvr));
> > assertTrue(kvr.toString().equals("key=[value]"));
> >
> > // KVR with a previous value
> > KeyValueHistory kvr2 =
> KeyValueHistory.createFromKeyValuePrevious(key,
> value, previous);
> >
> > assertTrue(
> > kvr2.getKey() == key
> > && kvr2.getValue() == value
> > && kvr2.hasPreviousValues() == true
> > && kvr2.getAllValues().get(1) == previous);
> >
> > assertTrue(kvr2.equals(kvr2));
> > assertTrue(kvr2.toString().equals("key=[value, previous]"));
> >
> > assertFalse(kvr.equals(kvr2));
> > assertFalse(kvr.hashCode() == kvr2.hashCode());
> >
> > // test that a previous value of 'null' is treated differently to
> no
> previous value
> > KeyValueHistory kvr3 =
> KeyValueHistory.createFromKeyValuePrevious(key,
> value, null);
> > assertTrue(kvr3.equals(kvr3));
> > assertTrue(kvr3.toString().equals("key=[value, null]"));
> > assertFalse(kvr3.equals(kvr));
> > assertFalse(kvr3.equals(kvr2));
> >
> > assertFalse(kvr3.hashCode() == kvr2.hashCode());
> > assertFalse(kvr3.hashCode() == kvr.hashCode());
> >
> > // test the Map.Entry handling
> > Map map = new HashMap();
> > map.put(key, value);
> >
> > Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
> >
> > KeyValueHistory kvr4 = KeyValueHistory.createFromMapEntry(entry);
> >
> > assertTrue(
> > kvr4.getKey() == key
> > && kvr4.getValue() == value
> > && kvr4.hasPreviousValues() == false);
> >
> > assertTrue(kvr4.equals(kvr4));
> > assertTrue(kvr4.equals(kvr));
> > assertTrue(kvr4.toString().equals("key=[value]"));
> >
> > assertFalse("Map.Entry and KeyValueHistory are never equal",
> kvr4.equals(entry));
> >
> > Map.Entry entry2 = kvr4.asMapEntry();
> >
> > assertTrue(entry.equals(entry2));
> > assertTrue(entry.hashCode() == entry2.hashCode());
> >
> > }
> >
> > }
> >
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> >
> ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail:
> commons-dev-help@jakarta.apache.org
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [collections] submission: KeyValueRecord

Posted by Neil O'Toole <ne...@users.sourceforge.net>.
--- Stephen Colebourne <sc...@btopenworld.com> wrote:
> I'm currently struggling to understand why I would need to use a
> HistoricMapEntry (better name?).
> It has a part in the undo events code you wrote I believe, but
> outside that
> where does it fit and what use is it? Use cases please :-)
> 
> Stephen

The better half is throwing dirty looks at me (I'm supposed to be
painting the house)  - I'll get back to you on the use cases asap.


> PS. I guess this came from the rich events package, which I would
> like to see as an implementation of the observed code.

Guilty as charged... unfortunately i've been away from my open-source
desk for the last month (in fact moving home, and - gasp! - no
connectivity for weeks on end) and hadn't been able to devote time to
the notifying/observable work, but i certainly want to integrate the
code, so I'll be looking into this next chance I get.

Neil




> ----- Original Message -----
> From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > > 'Record' confused me and made me think of databases. Is
> > > KeyValueHistory a better name?
> >
> > Stephen, you're probably right. I had been bouncing back and forth
> > between 'Record' and 'History'... the data structure does *record* 
> a
> > kv-pair's *history*, but in fact only the most recent part (one
> state
> > transition 'record') of the history.
> >
> > However, one would expect that a "KeyValueHistory" class should be
> able
> > to store the entire history of a key (as well as just a single
> value).
> > I hadn't initially needed this capability for my own purposes, but
> I
> > now see the usefulness. So,
> >
> >  #getPreviousValue : Object
> >
> > should probably be replaced with:
> >
> >  #getAllValues : List
> >
> > What do we think? In terms of design, either the constructors would
> get
> > very messy to use, or we need to add factory methods to distinguish
> > between:
> >
> > KeyValueHistory( Object key, Object value)
> > KeyValueHistory( Object key, List values)
> >
> > Attached is an implementation that supports multiple previous
> values,
> > and provides static factory methods. Thoughts?
> >
> > - Neil
> >
> >
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> > /*
> >  * $Header:
>
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/c
> ommons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32
> otoolen Exp
> $
> >  *
> ====================================================================
> >  *
> >  * The Apache Software License, Version 1.1
> >  *
> >  * Copyright (c) 2001-2003 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 acknowledgment:
> >  *       "This product includes software developed by the
> >  *        Apache Software Foundation (http://www.apache.org/)."
> >  *    Alternately, this acknowledgment may appear in the software
> itself,
> >  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
> >  *
> >  * 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 Software Foundation.
> >  *
> >  * 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.util.ArrayList;
> > import java.util.Collections;
> > import java.util.List;
> > import java.util.Map;
> >
> > /**
> >  * An immutable {key, value, previous-value} triplet. This class is
> frequently used
> >  * in conjunction with <code>Map.Entry</code>. A constructor is
> provided
> to create
> >  * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and
> the
> {@link #asMapEntry}
> >  * method can be used to view an object of this class as a
> <code>Map.Entry</code>.
> >  * Note that it is not possible for <code>KeyValueRecord</code> to
> implement the <code>Map.Entry</code>
> >  * interface as the <code>#equals</code> implementations are not
> compatible.
> >  *
> >  *
> >  *
> >  * @author Neil O'Toole
> >  */
> >
> > public class KeyValueHistory
> > {
> >
> > /**
> > * Create a new <code>KeyValueHistory</code> with supplied key and
> value,
> > * and no previous values.
> > */
> > public static KeyValueHistory createFromKeyValue(final Object key,
> final
> Object value)
> > {
> > return new KeyValueHistory(key, Collections.singletonList(value));
> > }
> >
> >
> > /**
> > * Create a new <code>KeyValueHistory</code> with the specified key
> and
> values.
> > * The supplied list must:
> > * <ul>
> > * <li>contain at least one value</li>
> > * <li>contain the values in reverse-chronological order (i.e. most
> > * recent value first)</li>
> > * <li>be immutable</li>
> > * </ul>
> > *
> > * @param values an immutable list of the values associated with
> this key.
> > * @throws IllegalArgumentException if <code>values</code> is
> <code>null</code> or
> > * is empty. <br />Note: If the value associated with the key is the
> value
> <code>null</code>,
> > * then supply <code>Collections#singletonList( null )</code>.
> > */
> > public static KeyValueHistory createFromKeyValueList(final Object
> key,
> final List values)
> > {
> > if (values == null || values.size() < 1)
> > {
> > throw new IllegalArgumentException("The supplied 'values' list must
> contain at least one value.");
> > }
> >
> > return new KeyValueHistory(key, values);
> > }
> >
> > /**
> > * Create a new <code>KeyValueHistory</code> with supplied key,
> value
> > * and previous value.
> > */
> > public static KeyValueHistory createFromKeyValuePrevious(final
> Object key,
> final Object value, final Object previous)
> > {
> > final List values = new ArrayList(2);
> > values.add(value);
> > values.add(previous);
> >
> > return new KeyValueHistory(key,
> Collections.unmodifiableList(values));
> > }
> > /**
> > * Create a new <code>KeyValueHistory</code> with key and value from
> the
> supplied
> > * <code>Map.Entry</code> and no previous value.
> > */
> > public static KeyValueHistory createFromMapEntry(final Map.Entry
> entry)
> > {
> > return new KeyValueHistory(entry.getKey(),
> Collections.singletonList(entry.getValue()));
> > }
> >
> > private int hash = -1; // lazily calculated
> >
> > private final Object key;
> > private final List values;
> >
> >
> >
> >
> > /**
> > * Create a new <code>KeyValueHistory</code> with the specified key
> and
> values.
> > * The supplied list:
> > * <ul>
> > * <li>must contain at least one value</li>
> > * <li>must contain the values in reverse-chronological order (i.e.
> most
> > * recent value first)</li>
> > * <li>must be immutable</li>
> > *
> > * @param values an immutable List of the values associated with
> this key.
> > */
> > protected KeyValueHistory(final Object key, final List values)
> > {
> > this.key = key;
> > this.values = values;
> > }
> >
> > /**
> > * Returns a {@link Map.Entry} view of the supplied
> <code>KeyValueRecord</code>. The
> > * returned entry is unmodifiable (<code>#setValue</code> throws an
> {@link
> UnsupportedOperationException}),
> > * as the backing <code>KeyValueRecord</code> is itself immutable.
> > * The returned entry correctly implements the
> <code>#hashCode</code> and
> > * <code>#equals</code> operations as per the <code>Map.Entry</code>
> contract.
> > */
> >
> > public Map.Entry asMapEntry()
> > {
> > return new Map.Entry()
> > {
> > private int hash = -1;
> >
> > public boolean equals(Object o)
> > {
> > if (o instanceof Map.Entry == false)
> > {
> > return false;
> > }
> >
> > if (o == this)
> > {
> > return true;
> > }
> >
> > Map.Entry e = (Map.Entry) o;
> >
> > return (
> > KeyValueHistory.this.getKey() == null
> > ? e.getKey() == null
> > : KeyValueHistory.this.getKey().equals(e.getKey()))
> > && (KeyValueHistory.this.getValue() == null
> > ? e.getValue() == null
> > : KeyValueHistory.this.getValue().equals(e.getValue()));
> > }
> >
> > public Object getKey()
> > {
> > return KeyValueHistory.this.getKey();
> > }
> >
> > public Object getValue()
> > {
> > return KeyValueHistory.this.getValue();
> > }
> >
> > public int hashCode()
> > {
> > if (this.hash == -1)
> > {
> > this.hash =
> > (KeyValueHistory.this.getKey() == null ? 0 :
> KeyValueHistory.this.getKey().hashCode())
> > ^ (KeyValueHistory.this.getValue() == null ? 0 :
> KeyValueHistory.this.getValue().hashCode());
> > }
> >
> > return this.hash;
> > }
> >
> > public Object setValue(Object value)
> > {
> > throw new UnsupportedOperationException("This Map.Entry is
> unmodifiable.");
> > }
> >
> > public String toString()
> > {
> > return new StringBuffer()
> > .append(KeyValueHistory.this.getKey())
> > .append('=')
> > .append(KeyValueHistory.this.getValue())
> > .toString();
> > }
> > };
> >
> > }
> >
> > /**
> > * Compares the specified object with this
> <code>KeyValueRecord</code> for
> equality. Returns
> > * true if the given object is also a <code>KeyValueRecord</code>
> and
> > * the records' key, value, and previous value are equal.
> > *
> > * @param o object to be compared for equality with this
> <code>KeyValueRecord</code>.
> > * @return <code>true</code> if the specified object is equal to
> this
> > *         record.
> > */
> > public boolean equals(final Object o)
> > {
> > if (!(o instanceof KeyValueHistory))
> > {
> > return false;
> > }
> >
> > if (this == o)
> > {
> > return true;
> > }
> >
> > final KeyValueHistory kvr = (KeyValueHistory) o;
> >
> > return (this.key == null ? kvr.key == null :
> this.key.equals(kvr.key))
> > && this.values.equals(kvr.values);
> > }
> >
> > /**
> > * Returns the first value previously associated with this key, if
> any.
> Note that
> > * <code>null</code> will be returned
> > * if the previous value is <code>null</code> <i>or</i> if there are
> no
> previous values
> > * Therefore {@link #hasPreviousValues()} should be used to test if
> there
> is a previous value.
> > *
> > * @return the previous value (which may be <code>null</code>)
> > * associated with this record's key, or <code>null</code> if there
> is no
> > * previous value.
> > */
> > /*
> > public Object getPreviousValue()
> > {
> > return (this.values.size() < 2) ? null : this.values.get(1);
> > }*/
> >
> > /*
> > public List getAllPreviousValues()
> > {
> > return this.values.subList(1, values.size() -1);
> > }
> > */
> >
> > /**
> > * Return all values associated with this key, in reverse
> chronological
> > * order. The most recent (current) value is at index zero, the
> previous
> value is
> > * at index one, the value previous to that at index two, etc.. If
> there
> are no
> > * previous values associated with this key, then list will contain
> only
> one element (the current value).
> > * The returned list is immutable.
> > *
> > * @return an immutable list of the values associated with this key,
> the
> list having a minimum size of one.
> > */
> > public List getAllValues()
> > {
> > return this.values;
> > }
> >
> > /**
> > * Returns the key associated with this record.
> > */
> > public Object getKey()
> > {
> > return this.key;
> > }
> >
> > /**
> > * Returns the current (or most recent) value associated with this
> record's
> key.
> > *
> > * @return the value (which may be <code>null</code>)
> > * associated with this record's key
> > */
> > public Object getValue()
> > {
> > return this.values.get(0);
> > }
> >
> > public int hashCode()
> > {
> > if (this.hash == -1)
> > {
> > this.hash =
> > (this.key == null ? 0 : this.key.hashCode())
> > ^ this.values.hashCode();
> > }
> >
> > return this.hash;
> > }
> >
> > /**
> > * Returns true if this record's key
> > * was associated with a value previous to being
> > * associated with its current value.
> > *
> > * @return <code>true</code> if this key previously had a value
> associated
> > * with it, <code>false</code> otherwise.
> > * @see #getPreviousValue()
> > */
> > public boolean hasPreviousValues()
> > {
> > return this.values.size() > 1;
> > }
> >
> > /**
> > * Returns a string representation of this
> <code>KeyValueHistory</code>.
> > *
> > */
> > public String toString()
> > {
> > final StringBuffer sb = new
> StringBuffer().append(this.key).append('=').append(this.values);
> >
> > return sb.toString();
> > }
> >
> > }
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> > /*
> >  * $Header:
>
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
> ommons/collections/TestKeyValueHistory.java,v 1.1 2003/09/20 22:00:46
> otoolen Exp $
> >  *
> ====================================================================
> >  *
> >  * The Apache Software License, Version 1.1
> >  *
> >  * Copyright (c) 2001-2003 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 acknowledgment:
> >  *       "This product includes software developed by the
> >  *        Apache Software Foundation (http://www.apache.org/)."
> >  *    Alternately, this acknowledgment may appear in the software
> itself,
> >  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
> >  *
> >  * 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 Software Foundation.
> >  *
> >  * 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.util.HashMap;
> > import java.util.Map;
> >
> > import junit.framework.TestCase;
> >
> >
> > /**
> >  *
> >  * @author Neil O'Toole
> >  */
> > public class TestKeyValueHistory extends TestCase
> > {
> > private final String key = "key";
> > private final String value = "value";
> > private final String previous = "previous";
> >
> > public TestKeyValueHistory(String testName)
> > {
> > super(testName);
> >
> > }
> >
> > public static void main(String[] args)
> > {
> > junit.textui.TestRunner.run(TestKeyValueHistory.class);
> > }
> >
> > public void testKeyValueHistory()
> > {
> >
> > // KVR with no previous value
> > KeyValueHistory kvr = KeyValueHistory.createFromKeyValue(key,
> value);
> >
> > assertTrue(
> > kvr.getKey() == key
> > && kvr.getValue() == value
> > && kvr.hasPreviousValues() == false);
> >
> > assertTrue(kvr.equals(kvr));
> > assertTrue(kvr.toString().equals("key=[value]"));
> >
> > // KVR with a previous value
> > KeyValueHistory kvr2 =
> KeyValueHistory.createFromKeyValuePrevious(key,
> value, previous);
> >
> > assertTrue(
> > kvr2.getKey() == key
> > && kvr2.getValue() == value
> > && kvr2.hasPreviousValues() == true
> > && kvr2.getAllValues().get(1) == previous);
> >
> > assertTrue(kvr2.equals(kvr2));
> > assertTrue(kvr2.toString().equals("key=[value, previous]"));
> >
> > assertFalse(kvr.equals(kvr2));
> > assertFalse(kvr.hashCode() == kvr2.hashCode());
> >
> > // test that a previous value of 'null' is treated differently to
> no
> previous value
> > KeyValueHistory kvr3 =
> KeyValueHistory.createFromKeyValuePrevious(key,
> value, null);
> > assertTrue(kvr3.equals(kvr3));
> > assertTrue(kvr3.toString().equals("key=[value, null]"));
> > assertFalse(kvr3.equals(kvr));
> > assertFalse(kvr3.equals(kvr2));
> >
> > assertFalse(kvr3.hashCode() == kvr2.hashCode());
> > assertFalse(kvr3.hashCode() == kvr.hashCode());
> >
> > // test the Map.Entry handling
> > Map map = new HashMap();
> > map.put(key, value);
> >
> > Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
> >
> > KeyValueHistory kvr4 = KeyValueHistory.createFromMapEntry(entry);
> >
> > assertTrue(
> > kvr4.getKey() == key
> > && kvr4.getValue() == value
> > && kvr4.hasPreviousValues() == false);
> >
> > assertTrue(kvr4.equals(kvr4));
> > assertTrue(kvr4.equals(kvr));
> > assertTrue(kvr4.toString().equals("key=[value]"));
> >
> > assertFalse("Map.Entry and KeyValueHistory are never equal",
> kvr4.equals(entry));
> >
> > Map.Entry entry2 = kvr4.asMapEntry();
> >
> > assertTrue(entry.equals(entry2));
> > assertTrue(entry.hashCode() == entry2.hashCode());
> >
> > }
> >
> > }
> >
> >
> 
> 
>
----------------------------------------------------------------------------
> ----
> 
> 
> >
> ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail:
> commons-dev-help@jakarta.apache.org
> 


Re: [collections] submission: KeyValueRecord

Posted by Stephen Colebourne <sc...@btopenworld.com>.
I'm currently struggling to understand why I would need to use a
HistoricMapEntry (better name?).
It has a part in the undo events code you wrote I believe, but outside that
where does it fit and what use is it? Use cases please :-)

Stephen

----- Original Message -----
From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > 'Record' confused me and made me think of databases. Is
> > KeyValueHistory a better name?
>
> Stephen, you're probably right. I had been bouncing back and forth
> between 'Record' and 'History'... the data structure does *record*  a
> kv-pair's *history*, but in fact only the most recent part (one state
> transition 'record') of the history.
>
> However, one would expect that a "KeyValueHistory" class should be able
> to store the entire history of a key (as well as just a single value).
> I hadn't initially needed this capability for my own purposes, but I
> now see the usefulness. So,
>
>  #getPreviousValue : Object
>
> should probably be replaced with:
>
>  #getAllValues : List
>
> What do we think? In terms of design, either the constructors would get
> very messy to use, or we need to add factory methods to distinguish
> between:
>
> KeyValueHistory( Object key, Object value)
> KeyValueHistory( Object key, List values)
>
> Attached is an implementation that supports multiple previous values,
> and provides static factory methods. Thoughts?
>
> - Neil
>
>
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/c
ommons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32 otoolen Exp
$
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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.util.ArrayList;
> import java.util.Collections;
> import java.util.List;
> import java.util.Map;
>
> /**
>  * An immutable {key, value, previous-value} triplet. This class is
frequently used
>  * in conjunction with <code>Map.Entry</code>. A constructor is provided
to create
>  * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and the
{@link #asMapEntry}
>  * method can be used to view an object of this class as a
<code>Map.Entry</code>.
>  * Note that it is not possible for <code>KeyValueRecord</code> to
implement the <code>Map.Entry</code>
>  * interface as the <code>#equals</code> implementations are not
compatible.
>  *
>  *
>  *
>  * @author Neil O'Toole
>  */
>
> public class KeyValueHistory
> {
>
> /**
> * Create a new <code>KeyValueHistory</code> with supplied key and value,
> * and no previous values.
> */
> public static KeyValueHistory createFromKeyValue(final Object key, final
Object value)
> {
> return new KeyValueHistory(key, Collections.singletonList(value));
> }
>
>
> /**
> * Create a new <code>KeyValueHistory</code> with the specified key and
values.
> * The supplied list must:
> * <ul>
> * <li>contain at least one value</li>
> * <li>contain the values in reverse-chronological order (i.e. most
> * recent value first)</li>
> * <li>be immutable</li>
> * </ul>
> *
> * @param values an immutable list of the values associated with this key.
> * @throws IllegalArgumentException if <code>values</code> is
<code>null</code> or
> * is empty. <br />Note: If the value associated with the key is the value
<code>null</code>,
> * then supply <code>Collections#singletonList( null )</code>.
> */
> public static KeyValueHistory createFromKeyValueList(final Object key,
final List values)
> {
> if (values == null || values.size() < 1)
> {
> throw new IllegalArgumentException("The supplied 'values' list must
contain at least one value.");
> }
>
> return new KeyValueHistory(key, values);
> }
>
> /**
> * Create a new <code>KeyValueHistory</code> with supplied key, value
> * and previous value.
> */
> public static KeyValueHistory createFromKeyValuePrevious(final Object key,
final Object value, final Object previous)
> {
> final List values = new ArrayList(2);
> values.add(value);
> values.add(previous);
>
> return new KeyValueHistory(key, Collections.unmodifiableList(values));
> }
> /**
> * Create a new <code>KeyValueHistory</code> with key and value from the
supplied
> * <code>Map.Entry</code> and no previous value.
> */
> public static KeyValueHistory createFromMapEntry(final Map.Entry entry)
> {
> return new KeyValueHistory(entry.getKey(),
Collections.singletonList(entry.getValue()));
> }
>
> private int hash = -1; // lazily calculated
>
> private final Object key;
> private final List values;
>
>
>
>
> /**
> * Create a new <code>KeyValueHistory</code> with the specified key and
values.
> * The supplied list:
> * <ul>
> * <li>must contain at least one value</li>
> * <li>must contain the values in reverse-chronological order (i.e. most
> * recent value first)</li>
> * <li>must be immutable</li>
> *
> * @param values an immutable List of the values associated with this key.
> */
> protected KeyValueHistory(final Object key, final List values)
> {
> this.key = key;
> this.values = values;
> }
>
> /**
> * Returns a {@link Map.Entry} view of the supplied
<code>KeyValueRecord</code>. The
> * returned entry is unmodifiable (<code>#setValue</code> throws an {@link
UnsupportedOperationException}),
> * as the backing <code>KeyValueRecord</code> is itself immutable.
> * The returned entry correctly implements the <code>#hashCode</code> and
> * <code>#equals</code> operations as per the <code>Map.Entry</code>
contract.
> */
>
> public Map.Entry asMapEntry()
> {
> return new Map.Entry()
> {
> private int hash = -1;
>
> public boolean equals(Object o)
> {
> if (o instanceof Map.Entry == false)
> {
> return false;
> }
>
> if (o == this)
> {
> return true;
> }
>
> Map.Entry e = (Map.Entry) o;
>
> return (
> KeyValueHistory.this.getKey() == null
> ? e.getKey() == null
> : KeyValueHistory.this.getKey().equals(e.getKey()))
> && (KeyValueHistory.this.getValue() == null
> ? e.getValue() == null
> : KeyValueHistory.this.getValue().equals(e.getValue()));
> }
>
> public Object getKey()
> {
> return KeyValueHistory.this.getKey();
> }
>
> public Object getValue()
> {
> return KeyValueHistory.this.getValue();
> }
>
> public int hashCode()
> {
> if (this.hash == -1)
> {
> this.hash =
> (KeyValueHistory.this.getKey() == null ? 0 :
KeyValueHistory.this.getKey().hashCode())
> ^ (KeyValueHistory.this.getValue() == null ? 0 :
KeyValueHistory.this.getValue().hashCode());
> }
>
> return this.hash;
> }
>
> public Object setValue(Object value)
> {
> throw new UnsupportedOperationException("This Map.Entry is
unmodifiable.");
> }
>
> public String toString()
> {
> return new StringBuffer()
> .append(KeyValueHistory.this.getKey())
> .append('=')
> .append(KeyValueHistory.this.getValue())
> .toString();
> }
> };
>
> }
>
> /**
> * Compares the specified object with this <code>KeyValueRecord</code> for
equality. Returns
> * true if the given object is also a <code>KeyValueRecord</code> and
> * the records' key, value, and previous value are equal.
> *
> * @param o object to be compared for equality with this
<code>KeyValueRecord</code>.
> * @return <code>true</code> if the specified object is equal to this
> *         record.
> */
> public boolean equals(final Object o)
> {
> if (!(o instanceof KeyValueHistory))
> {
> return false;
> }
>
> if (this == o)
> {
> return true;
> }
>
> final KeyValueHistory kvr = (KeyValueHistory) o;
>
> return (this.key == null ? kvr.key == null : this.key.equals(kvr.key))
> && this.values.equals(kvr.values);
> }
>
> /**
> * Returns the first value previously associated with this key, if any.
Note that
> * <code>null</code> will be returned
> * if the previous value is <code>null</code> <i>or</i> if there are no
previous values
> * Therefore {@link #hasPreviousValues()} should be used to test if there
is a previous value.
> *
> * @return the previous value (which may be <code>null</code>)
> * associated with this record's key, or <code>null</code> if there is no
> * previous value.
> */
> /*
> public Object getPreviousValue()
> {
> return (this.values.size() < 2) ? null : this.values.get(1);
> }*/
>
> /*
> public List getAllPreviousValues()
> {
> return this.values.subList(1, values.size() -1);
> }
> */
>
> /**
> * Return all values associated with this key, in reverse chronological
> * order. The most recent (current) value is at index zero, the previous
value is
> * at index one, the value previous to that at index two, etc.. If there
are no
> * previous values associated with this key, then list will contain only
one element (the current value).
> * The returned list is immutable.
> *
> * @return an immutable list of the values associated with this key, the
list having a minimum size of one.
> */
> public List getAllValues()
> {
> return this.values;
> }
>
> /**
> * Returns the key associated with this record.
> */
> public Object getKey()
> {
> return this.key;
> }
>
> /**
> * Returns the current (or most recent) value associated with this record's
key.
> *
> * @return the value (which may be <code>null</code>)
> * associated with this record's key
> */
> public Object getValue()
> {
> return this.values.get(0);
> }
>
> public int hashCode()
> {
> if (this.hash == -1)
> {
> this.hash =
> (this.key == null ? 0 : this.key.hashCode())
> ^ this.values.hashCode();
> }
>
> return this.hash;
> }
>
> /**
> * Returns true if this record's key
> * was associated with a value previous to being
> * associated with its current value.
> *
> * @return <code>true</code> if this key previously had a value associated
> * with it, <code>false</code> otherwise.
> * @see #getPreviousValue()
> */
> public boolean hasPreviousValues()
> {
> return this.values.size() > 1;
> }
>
> /**
> * Returns a string representation of this <code>KeyValueHistory</code>.
> *
> */
> public String toString()
> {
> final StringBuffer sb = new
StringBuffer().append(this.key).append('=').append(this.values);
>
> return sb.toString();
> }
>
> }
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
ommons/collections/TestKeyValueHistory.java,v 1.1 2003/09/20 22:00:46
otoolen Exp $
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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.util.HashMap;
> import java.util.Map;
>
> import junit.framework.TestCase;
>
>
> /**
>  *
>  * @author Neil O'Toole
>  */
> public class TestKeyValueHistory extends TestCase
> {
> private final String key = "key";
> private final String value = "value";
> private final String previous = "previous";
>
> public TestKeyValueHistory(String testName)
> {
> super(testName);
>
> }
>
> public static void main(String[] args)
> {
> junit.textui.TestRunner.run(TestKeyValueHistory.class);
> }
>
> public void testKeyValueHistory()
> {
>
> // KVR with no previous value
> KeyValueHistory kvr = KeyValueHistory.createFromKeyValue(key, value);
>
> assertTrue(
> kvr.getKey() == key
> && kvr.getValue() == value
> && kvr.hasPreviousValues() == false);
>
> assertTrue(kvr.equals(kvr));
> assertTrue(kvr.toString().equals("key=[value]"));
>
> // KVR with a previous value
> KeyValueHistory kvr2 = KeyValueHistory.createFromKeyValuePrevious(key,
value, previous);
>
> assertTrue(
> kvr2.getKey() == key
> && kvr2.getValue() == value
> && kvr2.hasPreviousValues() == true
> && kvr2.getAllValues().get(1) == previous);
>
> assertTrue(kvr2.equals(kvr2));
> assertTrue(kvr2.toString().equals("key=[value, previous]"));
>
> assertFalse(kvr.equals(kvr2));
> assertFalse(kvr.hashCode() == kvr2.hashCode());
>
> // test that a previous value of 'null' is treated differently to no
previous value
> KeyValueHistory kvr3 = KeyValueHistory.createFromKeyValuePrevious(key,
value, null);
> assertTrue(kvr3.equals(kvr3));
> assertTrue(kvr3.toString().equals("key=[value, null]"));
> assertFalse(kvr3.equals(kvr));
> assertFalse(kvr3.equals(kvr2));
>
> assertFalse(kvr3.hashCode() == kvr2.hashCode());
> assertFalse(kvr3.hashCode() == kvr.hashCode());
>
> // test the Map.Entry handling
> Map map = new HashMap();
> map.put(key, value);
>
> Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
>
> KeyValueHistory kvr4 = KeyValueHistory.createFromMapEntry(entry);
>
> assertTrue(
> kvr4.getKey() == key
> && kvr4.getValue() == value
> && kvr4.hasPreviousValues() == false);
>
> assertTrue(kvr4.equals(kvr4));
> assertTrue(kvr4.equals(kvr));
> assertTrue(kvr4.toString().equals("key=[value]"));
>
> assertFalse("Map.Entry and KeyValueHistory are never equal",
kvr4.equals(entry));
>
> Map.Entry entry2 = kvr4.asMapEntry();
>
> assertTrue(entry.equals(entry2));
> assertTrue(entry.hashCode() == entry2.hashCode());
>
> }
>
> }
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [collections] submission: KeyValueRecord

Posted by Stephen Colebourne <sc...@btopenworld.com>.
I'm currently struggling to understand why I would need to use a
HistoricMapEntry (better name?).
It has a part in the undo events code you wrote I believe, but outside that
where does it fit and what use is it? Use cases please :-)

Stephen

----- Original Message -----
From: "Neil O'Toole" <ne...@users.sourceforge.net>
> > 'Record' confused me and made me think of databases. Is
> > KeyValueHistory a better name?
>
> Stephen, you're probably right. I had been bouncing back and forth
> between 'Record' and 'History'... the data structure does *record*  a
> kv-pair's *history*, but in fact only the most recent part (one state
> transition 'record') of the history.
>
> However, one would expect that a "KeyValueHistory" class should be able
> to store the entire history of a key (as well as just a single value).
> I hadn't initially needed this capability for my own purposes, but I
> now see the usefulness. So,
>
>  #getPreviousValue : Object
>
> should probably be replaced with:
>
>  #getAllValues : List
>
> What do we think? In terms of design, either the constructors would get
> very messy to use, or we need to add factory methods to distinguish
> between:
>
> KeyValueHistory( Object key, Object value)
> KeyValueHistory( Object key, List values)
>
> Attached is an implementation that supports multiple previous values,
> and provides static factory methods. Thoughts?
>
> - Neil
>
>
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/c
ommons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32 otoolen Exp
$
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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.util.ArrayList;
> import java.util.Collections;
> import java.util.List;
> import java.util.Map;
>
> /**
>  * An immutable {key, value, previous-value} triplet. This class is
frequently used
>  * in conjunction with <code>Map.Entry</code>. A constructor is provided
to create
>  * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and the
{@link #asMapEntry}
>  * method can be used to view an object of this class as a
<code>Map.Entry</code>.
>  * Note that it is not possible for <code>KeyValueRecord</code> to
implement the <code>Map.Entry</code>
>  * interface as the <code>#equals</code> implementations are not
compatible.
>  *
>  *
>  *
>  * @author Neil O'Toole
>  */
>
> public class KeyValueHistory
> {
>
> /**
> * Create a new <code>KeyValueHistory</code> with supplied key and value,
> * and no previous values.
> */
> public static KeyValueHistory createFromKeyValue(final Object key, final
Object value)
> {
> return new KeyValueHistory(key, Collections.singletonList(value));
> }
>
>
> /**
> * Create a new <code>KeyValueHistory</code> with the specified key and
values.
> * The supplied list must:
> * <ul>
> * <li>contain at least one value</li>
> * <li>contain the values in reverse-chronological order (i.e. most
> * recent value first)</li>
> * <li>be immutable</li>
> * </ul>
> *
> * @param values an immutable list of the values associated with this key.
> * @throws IllegalArgumentException if <code>values</code> is
<code>null</code> or
> * is empty. <br />Note: If the value associated with the key is the value
<code>null</code>,
> * then supply <code>Collections#singletonList( null )</code>.
> */
> public static KeyValueHistory createFromKeyValueList(final Object key,
final List values)
> {
> if (values == null || values.size() < 1)
> {
> throw new IllegalArgumentException("The supplied 'values' list must
contain at least one value.");
> }
>
> return new KeyValueHistory(key, values);
> }
>
> /**
> * Create a new <code>KeyValueHistory</code> with supplied key, value
> * and previous value.
> */
> public static KeyValueHistory createFromKeyValuePrevious(final Object key,
final Object value, final Object previous)
> {
> final List values = new ArrayList(2);
> values.add(value);
> values.add(previous);
>
> return new KeyValueHistory(key, Collections.unmodifiableList(values));
> }
> /**
> * Create a new <code>KeyValueHistory</code> with key and value from the
supplied
> * <code>Map.Entry</code> and no previous value.
> */
> public static KeyValueHistory createFromMapEntry(final Map.Entry entry)
> {
> return new KeyValueHistory(entry.getKey(),
Collections.singletonList(entry.getValue()));
> }
>
> private int hash = -1; // lazily calculated
>
> private final Object key;
> private final List values;
>
>
>
>
> /**
> * Create a new <code>KeyValueHistory</code> with the specified key and
values.
> * The supplied list:
> * <ul>
> * <li>must contain at least one value</li>
> * <li>must contain the values in reverse-chronological order (i.e. most
> * recent value first)</li>
> * <li>must be immutable</li>
> *
> * @param values an immutable List of the values associated with this key.
> */
> protected KeyValueHistory(final Object key, final List values)
> {
> this.key = key;
> this.values = values;
> }
>
> /**
> * Returns a {@link Map.Entry} view of the supplied
<code>KeyValueRecord</code>. The
> * returned entry is unmodifiable (<code>#setValue</code> throws an {@link
UnsupportedOperationException}),
> * as the backing <code>KeyValueRecord</code> is itself immutable.
> * The returned entry correctly implements the <code>#hashCode</code> and
> * <code>#equals</code> operations as per the <code>Map.Entry</code>
contract.
> */
>
> public Map.Entry asMapEntry()
> {
> return new Map.Entry()
> {
> private int hash = -1;
>
> public boolean equals(Object o)
> {
> if (o instanceof Map.Entry == false)
> {
> return false;
> }
>
> if (o == this)
> {
> return true;
> }
>
> Map.Entry e = (Map.Entry) o;
>
> return (
> KeyValueHistory.this.getKey() == null
> ? e.getKey() == null
> : KeyValueHistory.this.getKey().equals(e.getKey()))
> && (KeyValueHistory.this.getValue() == null
> ? e.getValue() == null
> : KeyValueHistory.this.getValue().equals(e.getValue()));
> }
>
> public Object getKey()
> {
> return KeyValueHistory.this.getKey();
> }
>
> public Object getValue()
> {
> return KeyValueHistory.this.getValue();
> }
>
> public int hashCode()
> {
> if (this.hash == -1)
> {
> this.hash =
> (KeyValueHistory.this.getKey() == null ? 0 :
KeyValueHistory.this.getKey().hashCode())
> ^ (KeyValueHistory.this.getValue() == null ? 0 :
KeyValueHistory.this.getValue().hashCode());
> }
>
> return this.hash;
> }
>
> public Object setValue(Object value)
> {
> throw new UnsupportedOperationException("This Map.Entry is
unmodifiable.");
> }
>
> public String toString()
> {
> return new StringBuffer()
> .append(KeyValueHistory.this.getKey())
> .append('=')
> .append(KeyValueHistory.this.getValue())
> .toString();
> }
> };
>
> }
>
> /**
> * Compares the specified object with this <code>KeyValueRecord</code> for
equality. Returns
> * true if the given object is also a <code>KeyValueRecord</code> and
> * the records' key, value, and previous value are equal.
> *
> * @param o object to be compared for equality with this
<code>KeyValueRecord</code>.
> * @return <code>true</code> if the specified object is equal to this
> *         record.
> */
> public boolean equals(final Object o)
> {
> if (!(o instanceof KeyValueHistory))
> {
> return false;
> }
>
> if (this == o)
> {
> return true;
> }
>
> final KeyValueHistory kvr = (KeyValueHistory) o;
>
> return (this.key == null ? kvr.key == null : this.key.equals(kvr.key))
> && this.values.equals(kvr.values);
> }
>
> /**
> * Returns the first value previously associated with this key, if any.
Note that
> * <code>null</code> will be returned
> * if the previous value is <code>null</code> <i>or</i> if there are no
previous values
> * Therefore {@link #hasPreviousValues()} should be used to test if there
is a previous value.
> *
> * @return the previous value (which may be <code>null</code>)
> * associated with this record's key, or <code>null</code> if there is no
> * previous value.
> */
> /*
> public Object getPreviousValue()
> {
> return (this.values.size() < 2) ? null : this.values.get(1);
> }*/
>
> /*
> public List getAllPreviousValues()
> {
> return this.values.subList(1, values.size() -1);
> }
> */
>
> /**
> * Return all values associated with this key, in reverse chronological
> * order. The most recent (current) value is at index zero, the previous
value is
> * at index one, the value previous to that at index two, etc.. If there
are no
> * previous values associated with this key, then list will contain only
one element (the current value).
> * The returned list is immutable.
> *
> * @return an immutable list of the values associated with this key, the
list having a minimum size of one.
> */
> public List getAllValues()
> {
> return this.values;
> }
>
> /**
> * Returns the key associated with this record.
> */
> public Object getKey()
> {
> return this.key;
> }
>
> /**
> * Returns the current (or most recent) value associated with this record's
key.
> *
> * @return the value (which may be <code>null</code>)
> * associated with this record's key
> */
> public Object getValue()
> {
> return this.values.get(0);
> }
>
> public int hashCode()
> {
> if (this.hash == -1)
> {
> this.hash =
> (this.key == null ? 0 : this.key.hashCode())
> ^ this.values.hashCode();
> }
>
> return this.hash;
> }
>
> /**
> * Returns true if this record's key
> * was associated with a value previous to being
> * associated with its current value.
> *
> * @return <code>true</code> if this key previously had a value associated
> * with it, <code>false</code> otherwise.
> * @see #getPreviousValue()
> */
> public boolean hasPreviousValues()
> {
> return this.values.size() > 1;
> }
>
> /**
> * Returns a string representation of this <code>KeyValueHistory</code>.
> *
> */
> public String toString()
> {
> final StringBuffer sb = new
StringBuffer().append(this.key).append('=').append(this.values);
>
> return sb.toString();
> }
>
> }
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
ommons/collections/TestKeyValueHistory.java,v 1.1 2003/09/20 22:00:46
otoolen Exp $
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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.util.HashMap;
> import java.util.Map;
>
> import junit.framework.TestCase;
>
>
> /**
>  *
>  * @author Neil O'Toole
>  */
> public class TestKeyValueHistory extends TestCase
> {
> private final String key = "key";
> private final String value = "value";
> private final String previous = "previous";
>
> public TestKeyValueHistory(String testName)
> {
> super(testName);
>
> }
>
> public static void main(String[] args)
> {
> junit.textui.TestRunner.run(TestKeyValueHistory.class);
> }
>
> public void testKeyValueHistory()
> {
>
> // KVR with no previous value
> KeyValueHistory kvr = KeyValueHistory.createFromKeyValue(key, value);
>
> assertTrue(
> kvr.getKey() == key
> && kvr.getValue() == value
> && kvr.hasPreviousValues() == false);
>
> assertTrue(kvr.equals(kvr));
> assertTrue(kvr.toString().equals("key=[value]"));
>
> // KVR with a previous value
> KeyValueHistory kvr2 = KeyValueHistory.createFromKeyValuePrevious(key,
value, previous);
>
> assertTrue(
> kvr2.getKey() == key
> && kvr2.getValue() == value
> && kvr2.hasPreviousValues() == true
> && kvr2.getAllValues().get(1) == previous);
>
> assertTrue(kvr2.equals(kvr2));
> assertTrue(kvr2.toString().equals("key=[value, previous]"));
>
> assertFalse(kvr.equals(kvr2));
> assertFalse(kvr.hashCode() == kvr2.hashCode());
>
> // test that a previous value of 'null' is treated differently to no
previous value
> KeyValueHistory kvr3 = KeyValueHistory.createFromKeyValuePrevious(key,
value, null);
> assertTrue(kvr3.equals(kvr3));
> assertTrue(kvr3.toString().equals("key=[value, null]"));
> assertFalse(kvr3.equals(kvr));
> assertFalse(kvr3.equals(kvr2));
>
> assertFalse(kvr3.hashCode() == kvr2.hashCode());
> assertFalse(kvr3.hashCode() == kvr.hashCode());
>
> // test the Map.Entry handling
> Map map = new HashMap();
> map.put(key, value);
>
> Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
>
> KeyValueHistory kvr4 = KeyValueHistory.createFromMapEntry(entry);
>
> assertTrue(
> kvr4.getKey() == key
> && kvr4.getValue() == value
> && kvr4.hasPreviousValues() == false);
>
> assertTrue(kvr4.equals(kvr4));
> assertTrue(kvr4.equals(kvr));
> assertTrue(kvr4.toString().equals("key=[value]"));
>
> assertFalse("Map.Entry and KeyValueHistory are never equal",
kvr4.equals(entry));
>
> Map.Entry entry2 = kvr4.asMapEntry();
>
> assertTrue(entry.equals(entry2));
> assertTrue(entry.hashCode() == entry2.hashCode());
>
> }
>
> }
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [collections] submission: KeyValueRecord

Posted by Neil O'Toole <ne...@users.sourceforge.net>.
> 'Record' confused me and made me think of databases. Is
> KeyValueHistory a better name?

Stephen, you're probably right. I had been bouncing back and forth
between 'Record' and 'History'... the data structure does *record*  a
kv-pair's *history*, but in fact only the most recent part (one state
transition 'record') of the history.

However, one would expect that a "KeyValueHistory" class should be able
to store the entire history of a key (as well as just a single value).
I hadn't initially needed this capability for my own purposes, but I
now see the usefulness. So,
 
 #getPreviousValue : Object

should probably be replaced with:

 #getAllValues : List

What do we think? In terms of design, either the constructors would get
very messy to use, or we need to add factory methods to distinguish
between:

KeyValueHistory( Object key, Object value)
KeyValueHistory( Object key, List values)

Attached is an implementation that supports multiple previous values,
and provides static factory methods. Thoughts?

- Neil



Re: [collections] submission: KeyValueRecord

Posted by Stephen Colebourne <sc...@btopenworld.com>.
'Record' confused me and made me think of databases. Is KeyValueHistory a
better name?

DefaultMapEntry IIRC is the simple Map.Entry impl.
Stephen

PS. I guess this came from the rich events package, which I would like to
see as an implementation of the observed code.

----- Original Message -----
From: "Neil O'Toole" <ne...@users.sourceforge.net>
To: <co...@jakarta.apache.org>
Sent: Saturday, September 20, 2003 11:06 PM
Subject: [collections] submission: KeyValueRecord


> Attached is source (& associated tests) for KeyValueRecord.java.
> KeyValueRecord is an immutable {key, value, previous-value} triplet, a
> simple but very useful data structure. It's particularly of use with
> Map (and Map.Entry) for storing, tracking, and comparing entry state,
> but can obviously be used for any key-value data. The constructors
> include one to create a KeyValueRecord from a Map.Entry. There is also
> a method to view the KeyValueRecord as a Map.Entry (#asMapEntry). See
> the javadoc for more details.
>
> ** I had a swift look through [collections] and [lang], and didn't see
> a simple KeyValuePair implementation... Have I missed this somewhere?
> If not, I have an implementation which I will post.
>
> Thanks,
>
> Neil
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/c
ommons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32 otoolen Exp
$
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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.util.Map;
>
> /**
>  * An immutable {key, value, previous-value} triplet. This class is
frequently used
>  * in conjunction with <code>Map.Entry</code>. A constructor is provided
to create
>  * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and the
{@link #asMapEntry}
>  * method can be used to view an object of this class as a
<code>Map.Entry</code>.
>  * Note that it is not possible for <code>KeyValueRecord</code> to
implement the <code>Map.Entry</code>
>  * interface as the <code>#equals</code> implementations are not
compatible.
>  *
>  *
>  *
>  * @author Neil O'Toole
>  */
>
> public class KeyValueRecord
> {
> private static final Object NO_PREVIOUS_VALUE = new Object();
>
> private int hash = -1; // lazily calculated
>
> private final Object key;
> private final Object previous;
> private final Object value;
>
> /**
> * Create a new <code>KeyValueRecord</code> with key and value from the
supplied
> * <code>Map.Entry</code> and no previous value.
> */
> public KeyValueRecord(final Map.Entry entry)
> {
> this(entry.getKey(), entry.getValue(), NO_PREVIOUS_VALUE);
> }
>
> /**
> * Create a new <code>KeyValueRecord</code> with the specified key and
value and
> * no previous value.
> */
> public KeyValueRecord(final Object key, final Object value)
> {
> this(key, value, NO_PREVIOUS_VALUE);
> }
>
> /**
> * Create a new <code>KeyValueRecord</code> with the specified key, value,
and previous value.
> */
> public KeyValueRecord(final Object key, final Object value, final Object
previousValue)
> {
> this.key = key;
> this.value = value;
> this.previous = previousValue;
> }
>
> /**
> * Returns a {@link Map.Entry} view of the supplied
<code>KeyValueRecord</code>. The
> * returned entry is unmodifiable (<code>#setValue</code> throws an {@link
UnsupportedOperationException}),
> * as the backing <code>KeyValueRecord</code> is itself immutable.
> * The returned entry correctly implements the <code>#hashCode</code> and
> * <code>#equals</code> operations as per the <code>Map.Entry</code>
contract.
> */
>
> public Map.Entry asMapEntry()
> {
> return new Map.Entry()
> {
> private int hash = -1;
>
> public boolean equals(Object o)
> {
> if (o instanceof Map.Entry == false)
> {
> return false;
> }
>
> if (o == this)
> {
> return true;
> }
>
> Map.Entry e = (Map.Entry) o;
>
> return (
> KeyValueRecord.this.getKey() == null
> ? e.getKey() == null
> : KeyValueRecord.this.getKey().equals(e.getKey()))
> && (KeyValueRecord.this.getValue() == null
> ? e.getValue() == null
> : KeyValueRecord.this.getValue().equals(e.getValue()));
> }
>
> public Object getKey()
> {
> return KeyValueRecord.this.getKey();
> }
>
> public Object getValue()
> {
> return KeyValueRecord.this.getValue();
> }
>
> public int hashCode()
> {
> if (this.hash == -1)
> {
> this.hash =
> (KeyValueRecord.this.getKey() == null ? 0 :
KeyValueRecord.this.getKey().hashCode())
> ^ (KeyValueRecord.this.getValue() == null ? 0 :
KeyValueRecord.this.getValue().hashCode());
> }
>
> return this.hash;
> }
>
> public Object setValue(Object value)
> {
> throw new UnsupportedOperationException("This Map.Entry is
unmodifiable.");
> }
>
> public String toString()
> {
> return new StringBuffer()
> .append(KeyValueRecord.this.getKey())
> .append('=')
> .append(KeyValueRecord.this.getValue())
> .toString();
> }
> };
>
> }
>
> /**
> * Compares the specified object with this <code>KeyValueRecord</code> for
equality. Returns
> * true if the given object is also a <code>KeyValueRecord</code> and
> * the records' key, value, and previous value are equal.
> *
> * @param o object to be compared for equality with this
<code>KeyValueRecord</code>.
> * @return <code>true</code> if the specified object is equal to this
> *         record.
> */
> public boolean equals(final Object o)
> {
> if (!(o instanceof KeyValueRecord))
> {
> return false;
> }
>
> if (this == o)
> {
> return true;
> }
>
> final KeyValueRecord kvr = (KeyValueRecord) o;
>
> return (this.key == null ? kvr.key == null : this.key.equals(kvr.key))
> && (this.value == null ? kvr.value == null : this.value.equals(kvr.value))
> && (this.previous == NO_PREVIOUS_VALUE
> ? (kvr.previous == NO_PREVIOUS_VALUE)
> : (this.previous == null ? kvr.previous == null :
this.previous.equals(kvr.previous)));
> }
>
> /**
> * Returns the key associated with this record.
> */
> public Object getKey()
> {
> return this.key;
> }
>
> /**
> * Returns the value previously associated with this record's key, if any.
Note that
> * <code>null</code> will be returned
> * if the previous value is <code>null</code> <i>or</i> if there is no
previous value.
> * Therefore {@link #hasPreviousValue()} should be used to test if there is
a previous value.
> *
> * @return the previous value (which may be <code>null</code>)
> * associated with this record's key, or <code>null</code> if there is no
> * previous value.
> */
> public Object getPreviousValue()
> {
> return (this.previous == NO_PREVIOUS_VALUE) ? null : this.previous;
> }
>
> /**
> * Returns the value associated with this record's key.
> *
> * @return the object (which may be <code>null</code>)
> * associated with this record's key
> */
> public Object getValue()
> {
> return this.value;
> }
>
> public int hashCode()
> {
> if (this.hash == -1)
> {
> this.hash =
> (this.key == null ? 0 : this.key.hashCode())
> ^ (this.value == null ? 0 : this.value.hashCode())
> ^ (this.previous == null ? 0 : this.previous.hashCode());
> }
>
> return this.hash;
> }
>
> /**
> * Returns true if this record's key
> * was associated with a value previous to being
> * associated with its current value.
> *
> * @return <code>true</code> if this key previously had a value associated
> * with it, <code>false</code> otherwise.
> * @see #getPreviousValue()
> */
> public boolean hasPreviousValue()
> {
> return this.previous != NO_PREVIOUS_VALUE;
> }
>
> /**
> * Returns a string representation of this <code>KeyValueRecord</code>. The
text
> * rendering is <code>key=value</code> or <code>key=value (previous)</code>
> * depending on whether this <code>KeyValueRecord</code>
> * has a previous value.
> *
> */
> public String toString()
> {
> final StringBuffer sb = new
StringBuffer().append(this.key).append('=').append(this.value);
>
> if (this.hasPreviousValue())
> {
> sb.append(' ').append('(').append(this.getPreviousValue()).append(')');
> }
>
> return sb.toString();
> }
>
> }
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
ommons/collections/TestKeyValueRecord.java,v 1.1 2003/09/20 22:00:46 otoolen
Exp $
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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.util.HashMap;
> import java.util.Map;
> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
ommons/collections/TestKeyValueRecord.java,v 1.1 2003/09/20 22:00:46 otoolen
Exp $
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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/>.
>  *
>  */
> import junit.framework.TestCase;
>
> import org.apache.commons.collections.KeyValueRecord;
>
> /**
>  *
>  * @author Neil O'Toole
>  */
> public class TestKeyValueRecord extends TestCase
> {
> private final String key = "key";
> private final String value = "value";
> private final String previous = "previous";
>
> public TestKeyValueRecord(String testName)
> {
> super(testName);
>
> }
>
> public static void main(String[] args)
> {
> junit.textui.TestRunner.run(TestKeyValueRecord.class);
> }
>
> public void testKeyValueRecord()
> {
>
> // KVR with no previous value
> KeyValueRecord kvr = new KeyValueRecord(key, value);
>
> assertTrue(
> kvr.getKey() == key
> && kvr.getValue() == value
> && kvr.getPreviousValue() == null
> && kvr.hasPreviousValue() == false);
>
> assertTrue(kvr.equals(kvr));
> assertTrue(kvr.toString().equals("key=value"));
>
> // KVR with a previous value
> KeyValueRecord kvr2 = new KeyValueRecord(key, value, previous);
>
> assertTrue(
> kvr2.getKey() == key
> && kvr2.getValue() == value
> && kvr2.getPreviousValue() == previous
> && kvr2.hasPreviousValue() == true);
>
> assertTrue(kvr2.equals(kvr2));
> assertTrue(kvr2.toString().equals("key=value (previous)"));
>
> assertFalse(kvr.equals(kvr2));
> assertFalse(kvr.hashCode() == kvr2.hashCode());
>
> // test that a previous value of 'null' is treated differently to no
previous value
> KeyValueRecord kvr3 = new KeyValueRecord(key, value, null);
> assertTrue(kvr3.equals(kvr3));
> assertTrue(kvr3.toString().equals("key=value (null)"));
> assertFalse(kvr3.equals(kvr));
> assertFalse(kvr3.equals(kvr2));
>
> assertFalse(kvr3.hashCode() == kvr2.hashCode());
> assertFalse(kvr3.hashCode() == kvr.hashCode());
>
> // test the Map.Entry handling
> Map map = new HashMap();
> map.put(key, value);
>
> Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
>
> KeyValueRecord kvr4 = new KeyValueRecord(entry);
>
> assertTrue(
> kvr4.getKey() == key
> && kvr4.getValue() == value
> && kvr4.getPreviousValue() == null
> && kvr4.hasPreviousValue() == false);
>
> assertTrue(kvr4.equals(kvr4));
> assertTrue(kvr4.equals(kvr));
> assertTrue(kvr4.toString().equals("key=value"));
>
> assertFalse("Map.Entry and KeyValueRecord are never equal",
kvr4.equals(entry));
>
> Map.Entry entry2 = kvr4.asMapEntry();
>
> assertTrue(entry.equals(entry2));
> assertTrue(entry.hashCode() == entry2.hashCode());
>
> }
>
> }
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [collections] submission: KeyValueRecord

Posted by Stephen Colebourne <sc...@btopenworld.com>.
'Record' confused me and made me think of databases. Is KeyValueHistory a
better name?

DefaultMapEntry IIRC is the simple Map.Entry impl.
Stephen

PS. I guess this came from the rich events package, which I would like to
see as an implementation of the observed code.

----- Original Message -----
From: "Neil O'Toole" <ne...@users.sourceforge.net>
To: <co...@jakarta.apache.org>
Sent: Saturday, September 20, 2003 11:06 PM
Subject: [collections] submission: KeyValueRecord


> Attached is source (& associated tests) for KeyValueRecord.java.
> KeyValueRecord is an immutable {key, value, previous-value} triplet, a
> simple but very useful data structure. It's particularly of use with
> Map (and Map.Entry) for storing, tracking, and comparing entry state,
> but can obviously be used for any key-value data. The constructors
> include one to create a KeyValueRecord from a Map.Entry. There is also
> a method to view the KeyValueRecord as a Map.Entry (#asMapEntry). See
> the javadoc for more details.
>
> ** I had a swift look through [collections] and [lang], and didn't see
> a simple KeyValuePair implementation... Have I missed this somewhere?
> If not, I have an implementation which I will post.
>
> Thanks,
>
> Neil
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/c
ommons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32 otoolen Exp
$
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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.util.Map;
>
> /**
>  * An immutable {key, value, previous-value} triplet. This class is
frequently used
>  * in conjunction with <code>Map.Entry</code>. A constructor is provided
to create
>  * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and the
{@link #asMapEntry}
>  * method can be used to view an object of this class as a
<code>Map.Entry</code>.
>  * Note that it is not possible for <code>KeyValueRecord</code> to
implement the <code>Map.Entry</code>
>  * interface as the <code>#equals</code> implementations are not
compatible.
>  *
>  *
>  *
>  * @author Neil O'Toole
>  */
>
> public class KeyValueRecord
> {
> private static final Object NO_PREVIOUS_VALUE = new Object();
>
> private int hash = -1; // lazily calculated
>
> private final Object key;
> private final Object previous;
> private final Object value;
>
> /**
> * Create a new <code>KeyValueRecord</code> with key and value from the
supplied
> * <code>Map.Entry</code> and no previous value.
> */
> public KeyValueRecord(final Map.Entry entry)
> {
> this(entry.getKey(), entry.getValue(), NO_PREVIOUS_VALUE);
> }
>
> /**
> * Create a new <code>KeyValueRecord</code> with the specified key and
value and
> * no previous value.
> */
> public KeyValueRecord(final Object key, final Object value)
> {
> this(key, value, NO_PREVIOUS_VALUE);
> }
>
> /**
> * Create a new <code>KeyValueRecord</code> with the specified key, value,
and previous value.
> */
> public KeyValueRecord(final Object key, final Object value, final Object
previousValue)
> {
> this.key = key;
> this.value = value;
> this.previous = previousValue;
> }
>
> /**
> * Returns a {@link Map.Entry} view of the supplied
<code>KeyValueRecord</code>. The
> * returned entry is unmodifiable (<code>#setValue</code> throws an {@link
UnsupportedOperationException}),
> * as the backing <code>KeyValueRecord</code> is itself immutable.
> * The returned entry correctly implements the <code>#hashCode</code> and
> * <code>#equals</code> operations as per the <code>Map.Entry</code>
contract.
> */
>
> public Map.Entry asMapEntry()
> {
> return new Map.Entry()
> {
> private int hash = -1;
>
> public boolean equals(Object o)
> {
> if (o instanceof Map.Entry == false)
> {
> return false;
> }
>
> if (o == this)
> {
> return true;
> }
>
> Map.Entry e = (Map.Entry) o;
>
> return (
> KeyValueRecord.this.getKey() == null
> ? e.getKey() == null
> : KeyValueRecord.this.getKey().equals(e.getKey()))
> && (KeyValueRecord.this.getValue() == null
> ? e.getValue() == null
> : KeyValueRecord.this.getValue().equals(e.getValue()));
> }
>
> public Object getKey()
> {
> return KeyValueRecord.this.getKey();
> }
>
> public Object getValue()
> {
> return KeyValueRecord.this.getValue();
> }
>
> public int hashCode()
> {
> if (this.hash == -1)
> {
> this.hash =
> (KeyValueRecord.this.getKey() == null ? 0 :
KeyValueRecord.this.getKey().hashCode())
> ^ (KeyValueRecord.this.getValue() == null ? 0 :
KeyValueRecord.this.getValue().hashCode());
> }
>
> return this.hash;
> }
>
> public Object setValue(Object value)
> {
> throw new UnsupportedOperationException("This Map.Entry is
unmodifiable.");
> }
>
> public String toString()
> {
> return new StringBuffer()
> .append(KeyValueRecord.this.getKey())
> .append('=')
> .append(KeyValueRecord.this.getValue())
> .toString();
> }
> };
>
> }
>
> /**
> * Compares the specified object with this <code>KeyValueRecord</code> for
equality. Returns
> * true if the given object is also a <code>KeyValueRecord</code> and
> * the records' key, value, and previous value are equal.
> *
> * @param o object to be compared for equality with this
<code>KeyValueRecord</code>.
> * @return <code>true</code> if the specified object is equal to this
> *         record.
> */
> public boolean equals(final Object o)
> {
> if (!(o instanceof KeyValueRecord))
> {
> return false;
> }
>
> if (this == o)
> {
> return true;
> }
>
> final KeyValueRecord kvr = (KeyValueRecord) o;
>
> return (this.key == null ? kvr.key == null : this.key.equals(kvr.key))
> && (this.value == null ? kvr.value == null : this.value.equals(kvr.value))
> && (this.previous == NO_PREVIOUS_VALUE
> ? (kvr.previous == NO_PREVIOUS_VALUE)
> : (this.previous == null ? kvr.previous == null :
this.previous.equals(kvr.previous)));
> }
>
> /**
> * Returns the key associated with this record.
> */
> public Object getKey()
> {
> return this.key;
> }
>
> /**
> * Returns the value previously associated with this record's key, if any.
Note that
> * <code>null</code> will be returned
> * if the previous value is <code>null</code> <i>or</i> if there is no
previous value.
> * Therefore {@link #hasPreviousValue()} should be used to test if there is
a previous value.
> *
> * @return the previous value (which may be <code>null</code>)
> * associated with this record's key, or <code>null</code> if there is no
> * previous value.
> */
> public Object getPreviousValue()
> {
> return (this.previous == NO_PREVIOUS_VALUE) ? null : this.previous;
> }
>
> /**
> * Returns the value associated with this record's key.
> *
> * @return the object (which may be <code>null</code>)
> * associated with this record's key
> */
> public Object getValue()
> {
> return this.value;
> }
>
> public int hashCode()
> {
> if (this.hash == -1)
> {
> this.hash =
> (this.key == null ? 0 : this.key.hashCode())
> ^ (this.value == null ? 0 : this.value.hashCode())
> ^ (this.previous == null ? 0 : this.previous.hashCode());
> }
>
> return this.hash;
> }
>
> /**
> * Returns true if this record's key
> * was associated with a value previous to being
> * associated with its current value.
> *
> * @return <code>true</code> if this key previously had a value associated
> * with it, <code>false</code> otherwise.
> * @see #getPreviousValue()
> */
> public boolean hasPreviousValue()
> {
> return this.previous != NO_PREVIOUS_VALUE;
> }
>
> /**
> * Returns a string representation of this <code>KeyValueRecord</code>. The
text
> * rendering is <code>key=value</code> or <code>key=value (previous)</code>
> * depending on whether this <code>KeyValueRecord</code>
> * has a previous value.
> *
> */
> public String toString()
> {
> final StringBuffer sb = new
StringBuffer().append(this.key).append('=').append(this.value);
>
> if (this.hasPreviousValue())
> {
> sb.append(' ').append('(').append(this.getPreviousValue()).append(')');
> }
>
> return sb.toString();
> }
>
> }
>


----------------------------------------------------------------------------
----


> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
ommons/collections/TestKeyValueRecord.java,v 1.1 2003/09/20 22:00:46 otoolen
Exp $
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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.util.HashMap;
> import java.util.Map;
> /*
>  * $Header:
x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/c
ommons/collections/TestKeyValueRecord.java,v 1.1 2003/09/20 22:00:46 otoolen
Exp $
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2001-2003 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 acknowledgment:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgment may appear in the software itself,
>  *    if and wherever such third-party acknowledgments 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 apache@apache.org.
>  *
>  * 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 Software Foundation.
>  *
>  * 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/>.
>  *
>  */
> import junit.framework.TestCase;
>
> import org.apache.commons.collections.KeyValueRecord;
>
> /**
>  *
>  * @author Neil O'Toole
>  */
> public class TestKeyValueRecord extends TestCase
> {
> private final String key = "key";
> private final String value = "value";
> private final String previous = "previous";
>
> public TestKeyValueRecord(String testName)
> {
> super(testName);
>
> }
>
> public static void main(String[] args)
> {
> junit.textui.TestRunner.run(TestKeyValueRecord.class);
> }
>
> public void testKeyValueRecord()
> {
>
> // KVR with no previous value
> KeyValueRecord kvr = new KeyValueRecord(key, value);
>
> assertTrue(
> kvr.getKey() == key
> && kvr.getValue() == value
> && kvr.getPreviousValue() == null
> && kvr.hasPreviousValue() == false);
>
> assertTrue(kvr.equals(kvr));
> assertTrue(kvr.toString().equals("key=value"));
>
> // KVR with a previous value
> KeyValueRecord kvr2 = new KeyValueRecord(key, value, previous);
>
> assertTrue(
> kvr2.getKey() == key
> && kvr2.getValue() == value
> && kvr2.getPreviousValue() == previous
> && kvr2.hasPreviousValue() == true);
>
> assertTrue(kvr2.equals(kvr2));
> assertTrue(kvr2.toString().equals("key=value (previous)"));
>
> assertFalse(kvr.equals(kvr2));
> assertFalse(kvr.hashCode() == kvr2.hashCode());
>
> // test that a previous value of 'null' is treated differently to no
previous value
> KeyValueRecord kvr3 = new KeyValueRecord(key, value, null);
> assertTrue(kvr3.equals(kvr3));
> assertTrue(kvr3.toString().equals("key=value (null)"));
> assertFalse(kvr3.equals(kvr));
> assertFalse(kvr3.equals(kvr2));
>
> assertFalse(kvr3.hashCode() == kvr2.hashCode());
> assertFalse(kvr3.hashCode() == kvr.hashCode());
>
> // test the Map.Entry handling
> Map map = new HashMap();
> map.put(key, value);
>
> Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
>
> KeyValueRecord kvr4 = new KeyValueRecord(entry);
>
> assertTrue(
> kvr4.getKey() == key
> && kvr4.getValue() == value
> && kvr4.getPreviousValue() == null
> && kvr4.hasPreviousValue() == false);
>
> assertTrue(kvr4.equals(kvr4));
> assertTrue(kvr4.equals(kvr));
> assertTrue(kvr4.toString().equals("key=value"));
>
> assertFalse("Map.Entry and KeyValueRecord are never equal",
kvr4.equals(entry));
>
> Map.Entry entry2 = kvr4.asMapEntry();
>
> assertTrue(entry.equals(entry2));
> assertTrue(entry.hashCode() == entry2.hashCode());
>
> }
>
> }
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org