scolebourne    2004/03/13 04:43:43

  Modified:    collections RELEASE-NOTES.html
               collections/src/java/org/apache/commons/collections/keyvalue
                        MultiKey.java
               collections/src/test/org/apache/commons/collections/keyvalue
                        TestMultiKey.java
  Log:
  MultiKey enhancements to add getKey(index) and size()

  Make protected constructor public

  Add lots of javadoc examples and warnings
  
  Revision  Changes    Path
  1.11      +13 -2     jakarta-commons/collections/RELEASE-NOTES.html
  
  Index: RELEASE-NOTES.html
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/collections/RELEASE-NOTES.html,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- RELEASE-NOTES.html        27 Feb 2004 00:22:09 -0000      1.10
  +++ RELEASE-NOTES.html        13 Mar 2004 12:43:43 -0000      1.11
  @@ -34,7 +34,18 @@
   
   <hr />
   
  +<center><h3>BUG FIXES</h3></center>
  +<p>
  +[27159] AbstractHashedMap subclasses failed to clone() correctly
  +</p>
  +
  +<center><h3>ENHANCEMENTS</h3></center>
  +<p>
  +MultiKey - Add getKey(index) and size() methods and make constructor public
  +</p>
  +
   <center><h3>CHANGES</h3></center>
   <p>
  -[26470] Javadoc - Add javadoc about requiring Comparable entries
  -[27159] Bug - AbstractHashedMap subclasses failed to clone() correctly
  +[26470] TreeBidiMap - Add javadoc about requiring Comparable entries
  +MultiKey - Add extra explanatations, examples and warnings
  +</p>
  
  
  
  1.5       +86 -15    
jakarta-commons/collections/src/java/org/apache/commons/collections/keyvalue/MultiKey.java
  
  Index: MultiKey.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/collections/src/java/org/apache/commons/collections/keyvalue/MultiKey.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- MultiKey.java     18 Feb 2004 01:00:08 -0000      1.4
  +++ MultiKey.java     13 Mar 2004 12:43:43 -0000      1.5
  @@ -25,6 +25,18 @@
    * 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.
  + * <p>
  + * Example usage:
  + * <pre>
  + * // populate map with data mapping key+locale to localizedText
  + * Map map = new HashMap();
  + * MultiKey multiKey = new MultiKey(key, locale);
  + * map.put(multiKey, localizedText);
  + *
  + * // later retireve the localized text
  + * MultiKey multiKey = new MultiKey(key, locale);
  + * String localizedText = (String) map.get(multiKey);
  + * </pre>
    * 
    * @since Commons Collections 3.0
    * @version $Revision$ $Date$
  @@ -33,6 +45,7 @@
    * @author Stephen Colebourne
    */
   public class MultiKey implements Serializable {
  +    // This class could implement List, but that would confuse it's purpose
   
       /** Serialisation version */
       private static final long serialVersionUID = 4465448607415788805L;
  @@ -44,6 +57,9 @@
       
       /**
        * Constructor taking two keys.
  +     * <p>
  +     * The keys should be immutable
  +     * If they are not then they must not be changed after adding to the MultiKey.
        * 
        * @param key1  the first key
        * @param key2  the second key
  @@ -54,6 +70,9 @@
       
       /**
        * Constructor taking three keys.
  +     * <p>
  +     * The keys should be immutable
  +     * If they are not then they must not be changed after adding to the MultiKey.
        * 
        * @param key1  the first key
        * @param key2  the second key
  @@ -65,6 +84,9 @@
       
       /**
        * Constructor taking four keys.
  +     * <p>
  +     * The keys should be immutable
  +     * If they are not then they must not be changed after adding to the MultiKey.
        * 
        * @param key1  the first key
        * @param key2  the second key
  @@ -77,6 +99,9 @@
       
       /**
        * Constructor taking five keys.
  +     * <p>
  +     * The keys should be immutable
  +     * If they are not then they must not be changed after adding to the MultiKey.
        * 
        * @param key1  the first key
        * @param key2  the second key
  @@ -89,9 +114,14 @@
       }
       
       /**
  -     * Constructor taking an array of keys.
  +     * Constructor taking an array of keys which is cloned.
  +     * <p>
  +     * The keys should be immutable
  +     * If they are not then they must not be changed after adding to the MultiKey.
  +     * <p>
  +     * This is equivalent to <code>new MultiKey(keys, true)</code>.
        *
  -     * @param keys  the array of keys
  +     * @param keys  the array of keys, not null
        * @throws IllegalArgumentException if the key array is null
        */
       public MultiKey(Object[] keys) {
  @@ -99,20 +129,35 @@
       }
       
       /**
  -     * Constructor taking an array of keys.
  +     * Constructor taking an array of keys, optionally choosing whether to clone.
        * <p>
  -     * If the array is not copied, then it must not be modified.
  +     * <b>If the array is not cloned, then it must not be modified.</b>
  +     * <p>
  +     * This method is public for performance reasons only, to avoid a clone.
  +     * The hashcode is calculated once here in this method.
  +     * Therefore, changing the array passed in would not change the hashcode but
  +     * would change the equals method, which is a bug.
  +     * <p>
  +     * This is the only fully safe usage of this constructor, as the object array
  +     * is never made available in a variable:
  +     * <pre>
  +     * new MultiKey(new Object[] {...}, false);
  +     * </pre>
  +     * <p>
  +     * The keys should be immutable
  +     * If they are not then they must not be changed after adding to the MultiKey.
        *
  -     * @param keys  the array of keys
  -     * @param makeCopy  true to copy the array, false to assign it
  +     * @param keys  the array of keys, not null
  +     * @param makeClone  true to clone the array, false to assign it
        * @throws IllegalArgumentException if the key array is null
  +     * @since Commons Collections 3.1
        */
  -    protected MultiKey(Object[] keys, boolean makeCopy) {
  +    public MultiKey(Object[] keys, boolean makeClone) {
           super();
           if (keys == null) {
               throw new IllegalArgumentException("The array of keys must not be 
null");
           }
  -        if (makeCopy) {
  +        if (makeClone) {
               this.keys = (Object[]) keys.clone();
           } else {
               this.keys = keys;
  @@ -121,18 +166,18 @@
           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();
  -                }
  +                total ^= keys[i].hashCode();
               }
           }
           hashCode = total;
       }
       
  +    //-----------------------------------------------------------------------
       /**
  -     * Gets a copy of the individual keys.
  +     * Gets a clone of the array of keys.
  +     * <p>
  +     * The keys should be immutable
  +     * If they are not then they must not be changed.
        * 
        * @return the individual keys
        */
  @@ -140,6 +185,32 @@
           return (Object[]) keys.clone();
       }
       
  +    /**
  +     * Gets the key at the specified index.
  +     * <p>
  +     * The key should be immutable.
  +     * If it is not then it must not be changed.
  +     * 
  +     * @param index  the index to retrieve
  +     * @return the key at the index
  +     * @throws IndexOutOfBoundsException if the index is invalid
  +     * @since Commons Collections 3.1
  +     */
  +    public Object getKey(int index) {
  +        return keys[index];
  +    }
  +    
  +    /**
  +     * Gets the size of the list of keys.
  +     * 
  +     * @return the size of the list of keys
  +     * @since Commons Collections 3.1
  +     */
  +    public int size() {
  +        return keys.length;
  +    }
  +    
  +    //-----------------------------------------------------------------------
       /**
        * Compares this object to another.
        * <p>
  
  
  
  1.4       +104 -5    
jakarta-commons/collections/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java
  
  Index: TestMultiKey.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/collections/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- TestMultiKey.java 18 Feb 2004 01:20:40 -0000      1.3
  +++ TestMultiKey.java 13 Mar 2004 12:43:43 -0000      1.4
  @@ -58,8 +58,8 @@
           super.tearDown();
       }
       
  -
  -    public void testConstructorsAndGet() throws Exception {
  +    //-----------------------------------------------------------------------
  +    public void testConstructors() throws Exception {
           MultiKey mk = null;
           mk = new MultiKey(ONE, TWO);
           Assert.assertTrue(Arrays.equals(new Object[] {ONE, TWO}, mk.getKeys()));
  @@ -75,13 +75,109 @@
   
           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!
  +    }
  +    
  +    public void testConstructorsByArray() throws Exception {
  +        MultiKey mk = null;
           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()));
  +
  +        keys = new Object[] {};
  +        mk = new MultiKey(keys);
  +        Assert.assertTrue(Arrays.equals(new Object[] {}, mk.getKeys()));
  +
  +        keys = new Object[] {THREE, FOUR, ONE, TWO};
  +        mk = new MultiKey(keys, true);
  +        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()));
  +
  +        keys = new Object[] {THREE, FOUR, ONE, TWO};
  +        mk = new MultiKey(keys, false);
  +        Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, 
mk.getKeys()));
  +        // change key - don't do this!
  +        // the hashcode of the MultiKey is now broken
  +        keys[3] = FIVE;
  +        Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, FIVE}, 
mk.getKeys()));
  +    }        
  +    
  +    public void testConstructorsByArrayNull() throws Exception {
  +        Object[] keys = null;
  +        try {
  +            new MultiKey(keys);
  +            fail();
  +        } catch (IllegalArgumentException ex) {}
  +        try {
  +            new MultiKey(keys, true);
  +            fail();
  +        } catch (IllegalArgumentException ex) {}
  +        try {
  +            new MultiKey(keys, false);
  +            fail();
  +        } catch (IllegalArgumentException ex) {}
  +    }
  +    
  +    public void testSize() {
  +        Assert.assertEquals(2, new MultiKey(ONE, TWO).size());
  +        Assert.assertEquals(2, new MultiKey(null, null).size());
  +        Assert.assertEquals(3, new MultiKey(ONE, TWO, THREE).size());
  +        Assert.assertEquals(3, new MultiKey(null, null, null).size());
  +        Assert.assertEquals(4, new MultiKey(ONE, TWO, THREE, FOUR).size());
  +        Assert.assertEquals(4, new MultiKey(null, null, null, null).size());
  +        Assert.assertEquals(5, new MultiKey(ONE, TWO, THREE, FOUR, FIVE).size());
  +        Assert.assertEquals(5, new MultiKey(null, null, null, null, null).size());
  +        
  +        Assert.assertEquals(0, new MultiKey(new Object[] {}).size());
  +        Assert.assertEquals(1, new MultiKey(new Object[] {ONE}).size());
  +        Assert.assertEquals(2, new MultiKey(new Object[] {ONE, TWO}).size());
  +        Assert.assertEquals(7, new MultiKey(new Object[] {ONE, TWO, ONE, TWO, ONE, 
TWO, ONE}).size());
  +    }
  +    
  +    public void testGetIndexed() {
  +        MultiKey mk = new MultiKey(ONE, TWO);
  +        Assert.assertSame(ONE, mk.getKey(0));
  +        Assert.assertSame(TWO, mk.getKey(1));
  +        try {
  +            mk.getKey(-1);
  +            fail();
  +        } catch (IndexOutOfBoundsException ex) {}
  +        try {
  +            mk.getKey(2);
  +            fail();
  +        } catch (IndexOutOfBoundsException ex) {}
  +    }
  +    
  +    public void testGetKeysSimpleConstructor() {
  +        MultiKey mk = new MultiKey(ONE, TWO);
  +        Object[] array = mk.getKeys();
  +        Assert.assertSame(ONE, array[0]);
  +        Assert.assertSame(TWO, array[1]);
  +        Assert.assertEquals(2, array.length);
  +    }
  +    
  +    public void testGetKeysArrayConstructorCloned() {
  +        Object[] keys = new Object[] {ONE, TWO};
  +        MultiKey mk = new MultiKey(keys, true);
  +        Object[] array = mk.getKeys();
  +        Assert.assertTrue(array != keys);
  +        Assert.assertTrue(Arrays.equals(array, keys));
  +        Assert.assertSame(ONE, array[0]);
  +        Assert.assertSame(TWO, array[1]);
  +        Assert.assertEquals(2, array.length);
  +    }
  +    
  +    public void testGetKeysArrayConstructorNonCloned() {
  +        Object[] keys = new Object[] {ONE, TWO};
  +        MultiKey mk = new MultiKey(keys, false);
  +        Object[] array = mk.getKeys();
  +        Assert.assertTrue(array != keys);  // still not equal
  +        Assert.assertTrue(Arrays.equals(array, keys));
  +        Assert.assertSame(ONE, array[0]);
  +        Assert.assertSame(TWO, array[1]);
  +        Assert.assertEquals(2, array.length);
       }
       
       public void testHashCode() {
  @@ -92,6 +188,9 @@
           Assert.assertTrue(mk1.hashCode() == mk1.hashCode());
           Assert.assertTrue(mk1.hashCode() == mk2.hashCode());
           Assert.assertTrue(mk1.hashCode() != mk3.hashCode());
  +        
  +        int total = (0 ^ ONE.hashCode()) ^ TWO.hashCode();
  +        Assert.assertEquals(total, mk1.hashCode());
       }
       
       public void testEquals() {
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to