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 Sun, 21 Sep 2003 15:22:37 GMT
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" <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


Mime
View raw message