commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Stephen Colebourne" <scolebou...@btopenworld.com>
Subject Re: [collections] submission: KeyValueRecord
Date Mon, 22 Sep 2003 20:56:01 GMT
From: "Neil O'Toole" <neilotoole@users.sourceforge.net>
> --- Stephen Colebourne <scolebourne@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" <neilotoole@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
>


Mime
View raw message