diff --git a/src/java/org/apache/commons/collections/MultiKey.java b/src/java/org/apache/commons/collections/MultiKey.java new file mode 100644 index 000000000..74b9867e9 --- /dev/null +++ b/src/java/org/apache/commons/collections/MultiKey.java @@ -0,0 +1,227 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/Attic/MultiKey.java,v 1.1 2003/03/09 00:06:44 scolebourne Exp $ + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 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 + * . + * + */ +package org.apache.commons.collections; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * A MultiKey allows multiple map keys to be merged together. + *

+ * The purpose of this class is to avoid the need to write code to handle + * maps of maps. An example might be the need to lookup a filename by + * key and locale. The typical solution might be nested maps. This class + * can be used instead by creating an instance passing in the key and locale. + * + * @since Commons Collections 2.2 + * @version $Revision: 1.1 $ $Date: 2003/03/09 00:06:44 $ + * + * @author Howard Lewis Ship + * @author Stephen Colebourne + */ +public class MultiKey implements Serializable { + + private static final long serialVersionUID = 4465448607415788805L; + + /** The individual keys */ + private final Object[] keys; + /** The cached hashCode */ + private final int hashCode; + + /** + * Constructor taking two keys. + * + * @param key1 the first key + * @param key2 the second key + */ + public MultiKey(Object key1, Object key2) { + this(new Object[] {key1, key2}, false); + } + + /** + * Constructor taking three keys. + * + * @param key1 the first key + * @param key2 the second key + * @param key3 the third key + */ + public MultiKey(Object key1, Object key2, Object key3) { + this(new Object[] {key1, key2, key3}, false); + } + + /** + * Constructor taking four keys. + * + * @param key1 the first key + * @param key2 the second key + * @param key3 the third key + * @param key4 the fourth key + */ + public MultiKey(Object key1, Object key2, Object key3, Object key4) { + this(new Object[] {key1, key2, key3, key4}, false); + } + + /** + * Constructor taking five keys. + * + * @param key1 the first key + * @param key2 the second key + * @param key3 the third key + * @param key4 the fourth key + * @param key5 the fifth key + */ + public MultiKey(Object key1, Object key2, Object key3, Object key4, Object key5) { + this(new Object[] {key1, key2, key3, key4, key5}, false); + } + + /** + * Constructor taking an array of keys. + * + * @param keys the array of keys + * @throws IllegalArgumentException if the key array is null + */ + public MultiKey(Object[] keys) { + this(keys, true); + } + + /** + * Constructor taking an array of keys. + *

+ * If the array is not copied, then it must not be modified. + * + * @param keys the array of keys + * @param makeCopy true to copy the array, false to assign it + * @throws IllegalArgumentException if the key array is null + */ + protected MultiKey(Object[] keys, boolean makeCopy) { + super(); + if (keys == null) { + throw new IllegalArgumentException("The array of keys must not be null"); + } + if (makeCopy) { + this.keys = (Object[]) keys.clone(); + } else { + this.keys = keys; + } + + int total = 0; + for (int i = 0; i < keys.length; i++) { + if (keys[i] != null) { + if (i == 0) { + total = keys[i].hashCode(); + } else { + total ^= keys[i].hashCode(); + } + } + } + hashCode = total; + } + + /** + * Gets a copy of the individual keys. + * + * @return the individual keys + */ + public Object[] getKeys() { + return (Object[]) keys.clone(); + } + + /** + * Compares this object to another. + *

+ * To be equal, the other object must be a MultiKey with the + * same number of keys which are also equal. + * + * @param other the other object to compare to + * @return true if equal + */ + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other instanceof MultiKey) { + MultiKey otherMulti = (MultiKey) other; + return Arrays.equals(keys, otherMulti.keys); + } + return false; + } + + /** + * Gets the combined hashcode that is computed from all the keys. + *

+ * This value is computed once and then cached, so elements should not + * change their hash codes once created (note that this is the same + * constraint that would be used if the individual keys elements were + * themselves {@link java.util.Map Map} keys. + * + * @return the hashcode + */ + public int hashCode() { + return hashCode; + } + + /** + * Gets a debugging string version of the key. + * + * @return a debugging string + */ + public String toString() { + return "MultiKey" + Arrays.asList(keys).toString(); + } + +} diff --git a/src/test/org/apache/commons/collections/TestAll.java b/src/test/org/apache/commons/collections/TestAll.java index a1e088698..a68f34ce7 100644 --- a/src/test/org/apache/commons/collections/TestAll.java +++ b/src/test/org/apache/commons/collections/TestAll.java @@ -1,7 +1,7 @@ /* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/TestAll.java,v 1.40 2003/02/19 20:33:11 scolebourne Exp $ - * $Revision: 1.40 $ - * $Date: 2003/02/19 20:33:11 $ + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/TestAll.java,v 1.41 2003/03/09 00:07:41 scolebourne Exp $ + * $Revision: 1.41 $ + * $Date: 2003/03/09 00:07:41 $ * * ==================================================================== * @@ -68,7 +68,7 @@ import junit.framework.TestSuite; /** * Entry point for all Collections tests. * @author Rodney Waldhoff - * @version $Id: TestAll.java,v 1.40 2003/02/19 20:33:11 scolebourne Exp $ + * @version $Id: TestAll.java,v 1.41 2003/03/09 00:07:41 scolebourne Exp $ */ public class TestAll extends TestCase { public TestAll(String testName) { @@ -100,6 +100,7 @@ public class TestAll extends TestCase { suite.addTest(TestHashBag.suite()); suite.addTest(TestLRUMap.suite()); suite.addTest(TestMultiHashMap.suite()); + suite.addTest(TestMultiKey.suite()); suite.addTest(TestNodeCachingLinkedList.suite()); suite.addTest(TestSequencedHashMap.suite()); suite.addTest(TestStaticBucketMap.suite()); diff --git a/src/test/org/apache/commons/collections/TestMultiKey.java b/src/test/org/apache/commons/collections/TestMultiKey.java new file mode 100644 index 000000000..40aceeb42 --- /dev/null +++ b/src/test/org/apache/commons/collections/TestMultiKey.java @@ -0,0 +1,146 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 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 "Apache" and "Apache Software Foundation" and + * "Apache Turbine" 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", + * "Apache Turbine", nor may "Apache" appear in their name, 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 + * . + */ +package org.apache.commons.collections; + +import java.util.Arrays; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit tests + * {@link org.apache.commons.collections.MultiKey}. + * + * @author Stephen Colebourne + */ +public class TestMultiKey extends TestCase { + + Integer ONE = new Integer(1); + Integer TWO = new Integer(2); + Integer THREE = new Integer(3); + Integer FOUR = new Integer(4); + Integer FIVE = new Integer(5); + + public TestMultiKey(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(TestMultiKey.class); + } + + public static void main(String[] args) { + String[] testCaseName = { TestMultiKey.class.getName() }; + junit.textui.TestRunner.main(testCaseName); + } + + public void setUp() throws Exception { + super.setUp(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + + public void testConstructorsAndGet() throws Exception { + MultiKey mk = null; + mk = new MultiKey(ONE, TWO); + Assert.assertTrue(Arrays.equals(new Object[] {ONE, TWO}, mk.getKeys())); + + mk = new MultiKey(ONE, TWO, THREE); + Assert.assertTrue(Arrays.equals(new Object[] {ONE, TWO, THREE}, mk.getKeys())); + + mk = new MultiKey(ONE, TWO, THREE, FOUR); + Assert.assertTrue(Arrays.equals(new Object[] {ONE, TWO, THREE, FOUR}, mk.getKeys())); + + mk = new MultiKey(ONE, TWO, THREE, FOUR, FIVE); + Assert.assertTrue(Arrays.equals(new Object[] {ONE, TWO, THREE, FOUR, FIVE}, mk.getKeys())); + + mk = new MultiKey(new Object[] {THREE, FOUR, ONE, TWO}, false); + Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys())); + + // don't do this! + Object[] keys = new Object[] {THREE, FOUR, ONE, TWO}; + mk = new MultiKey(keys); + Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys())); + keys[3] = FIVE; // no effect + Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys())); + } + + public void testHashCode() { + MultiKey mk1 = new MultiKey(ONE, TWO); + MultiKey mk2 = new MultiKey(ONE, TWO); + MultiKey mk3 = new MultiKey(ONE, "TWO"); + + Assert.assertTrue(mk1.hashCode() == mk1.hashCode()); + Assert.assertTrue(mk1.hashCode() == mk2.hashCode()); + Assert.assertTrue(mk1.hashCode() != mk3.hashCode()); + } + + public void testEquals() { + MultiKey mk1 = new MultiKey(ONE, TWO); + MultiKey mk2 = new MultiKey(ONE, TWO); + MultiKey mk3 = new MultiKey(ONE, "TWO"); + + Assert.assertEquals(mk1, mk1); + Assert.assertEquals(mk1, mk2); + Assert.assertTrue(mk1.equals(mk3) == false); + Assert.assertTrue(mk1.equals("") == false); + Assert.assertTrue(mk1.equals(null) == false); + } + +}