From: "Neil O'Toole" <[EMAIL PROTECTED]> > --- Stephen Colebourne <[EMAIL PROTECTED]> 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" <[EMAIL PROTECTED]> > > > > '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 [EMAIL PROTECTED] > > > * > > > * 5. Products derived from this software may not be called > > "Apache" > > > * nor may "Apache" appear in their names without prior written > > > * permission of the Apache 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 > > [EMAIL PROTECTED] #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 [EMAIL PROTECTED] Map.Entry} view of the supplied > > <code>KeyValueRecord</code>. The > > > * returned entry is unmodifiable (<code>#setValue</code> throws an > > [EMAIL PROTECTED] > > 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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] > > > * > > > * 5. Products derived from this software may not be called > > "Apache" > > > * nor may "Apache" appear in their names without prior written > > > * permission of the Apache 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: [EMAIL PROTECTED] > > > For additional commands, e-mail: > > [EMAIL PROTECTED] > > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] > --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
