Clover coverage report - Code Coverage for tapestry release 4.0-beta-10
Coverage timestamp: Sat Oct 8 2005 19:08:05 EDT
file stats: LOC: 233   Methods: 7
NCLOC: 102   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
MultiKey.java 50% 46.2% 42.9% 47.4%
coverage coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.tapestry.util;
 16   
 17    import java.io.Externalizable;
 18    import java.io.IOException;
 19    import java.io.ObjectInput;
 20    import java.io.ObjectOutput;
 21   
 22    import org.apache.tapestry.Tapestry;
 23   
 24    /**
 25    * A complex key that may be used as an alternative to nested
 26    * {@link java.util.Map}s.
 27    *
 28    * @author Howard Lewis Ship
 29    *
 30    **/
 31   
 32    public class MultiKey implements Externalizable
 33    {
 34    /**
 35    * @since 2.0.4
 36    *
 37    **/
 38   
 39    private static final long serialVersionUID = 4465448607415788806L;
 40   
 41    private static final int HASH_CODE_UNSET = -1;
 42   
 43    private transient int hashCode = HASH_CODE_UNSET;
 44   
 45    private Object[] keys;
 46   
 47    /**
 48    * Public no-arguments constructor needed to be compatible with
 49    * {@link Externalizable}; this leaves the new MultiKey in a
 50    * non-usable state and shouldn't be used by user code.
 51    *
 52    **/
 53   
 54  0 public MultiKey()
 55    {
 56    }
 57   
 58    /**
 59    * Builds a <code>MultiKey</code> from an array of keys. If the array is not
 60    * copied, then it must not be modified.
 61    *
 62    * @param keys The components of the key.
 63    * @param makeCopy If true, a copy of the keys is created. If false,
 64    * the keys are simple retained by the <code>MultiKey</code>.
 65    *
 66    * @throws IllegalArgumentException if keys is null, of if the
 67    * first element of keys is null.
 68    *
 69    **/
 70   
 71  578 public MultiKey(Object[] keys, boolean makeCopy)
 72    {
 73  578 super();
 74   
 75  578 if (keys == null || keys.length == 0)
 76  0 throw new IllegalArgumentException(Tapestry.getMessage("MultiKey.null-keys"));
 77   
 78  578 if (keys[0] == null)
 79  0 throw new IllegalArgumentException(Tapestry.getMessage("MultiKey.first-element-may-not-be-null"));
 80   
 81  578 if (makeCopy)
 82    {
 83  0 this.keys = new Object[keys.length];
 84  0 System.arraycopy(keys, 0, this.keys, 0, keys.length);
 85    }
 86    else
 87  578 this.keys = keys;
 88    }
 89   
 90    /**
 91    * Returns true if:
 92    * <ul>
 93    * <li>The other object is a <code>MultiKey</code>
 94    * <li>They have the same number of key elements
 95    * <li>Every element is an exact match or is equal
 96    * </ul>
 97    *
 98    **/
 99   
 100  211 public boolean equals(Object other)
 101    {
 102  211 int i;
 103   
 104  211 if (other == null)
 105  0 return false;
 106   
 107  211 if (keys == null)
 108  0 throw new IllegalStateException(Tapestry.getMessage("MultiKey.no-keys"));
 109   
 110    // Would a hashCode check be worthwhile here?
 111   
 112  211 try
 113    {
 114  211 MultiKey otherMulti = (MultiKey) other;
 115   
 116  211 if (keys.length != otherMulti.keys.length)
 117  0 return false;
 118   
 119  211 for (i = 0; i < keys.length; i++)
 120    {
 121    // On an exact match, continue. This means that null matches
 122    // null.
 123   
 124  378 if (keys[i] == otherMulti.keys[i])
 125  276 continue;
 126   
 127    // If either is null, but not both, then
 128    // not a match.
 129   
 130  102 if (keys[i] == null || otherMulti.keys[i] == null)
 131  0 return false;
 132   
 133  102 if (!keys[i].equals(otherMulti.keys[i]))
 134  44 return false;
 135   
 136    }
 137   
 138    // Every key equal. A match.
 139   
 140  167 return true;
 141    }
 142    catch (ClassCastException e)
 143    {
 144    }
 145   
 146  0 return false;
 147    }
 148   
 149    /**
 150    * Returns the hash code of the receiver, which is computed from all the
 151    * non-null key elements. This value is computed once and
 152    * then cached, so elements should not change their hash codes
 153    * once created (note that this
 154    * is the same constraint that would be used if the individual
 155    * key elements were
 156    * themselves {@link java.util.Map} keys.
 157    *
 158    *
 159    **/
 160   
 161  860 public int hashCode()
 162    {
 163  860 if (hashCode == HASH_CODE_UNSET)
 164    {
 165  578 hashCode = keys[0].hashCode();
 166   
 167  578 for (int i = 1; i < keys.length; i++)
 168    {
 169  578 if (keys[i] != null)
 170  578 hashCode ^= keys[i].hashCode();
 171    }
 172    }
 173   
 174  860 return hashCode;
 175    }
 176   
 177    /**
 178    * Identifies all the keys stored by this <code>MultiKey</code>.
 179    *
 180    **/
 181   
 182  0 public String toString()
 183    {
 184  0 StringBuffer buffer;
 185  0 int i;
 186   
 187  0 buffer = new StringBuffer("MultiKey[");
 188   
 189  0 for (i = 0; i < keys.length; i++)
 190    {
 191  0 if (i > 0)
 192  0 buffer.append(", ");
 193   
 194  0 if (keys[i] == null)
 195  0 buffer.append("<null>");
 196    else
 197  0 buffer.append(keys[i]);
 198    }
 199   
 200  0 buffer.append(']');
 201   
 202  0 return buffer.toString();
 203    }
 204   
 205    /**
 206    * Writes a count of the keys, then writes each individual key.
 207    *
 208    **/
 209   
 210  0 public void writeExternal(ObjectOutput out) throws IOException
 211    {
 212  0 out.writeInt(keys.length);
 213   
 214  0 for (int i = 0; i < keys.length; i++)
 215  0 out.writeObject(keys[i]);
 216    }
 217   
 218    /**
 219    * Reads the state previously written by {@link #writeExternal(ObjectOutput)}.
 220    *
 221    **/
 222   
 223  0 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
 224    {
 225  0 int count;
 226   
 227  0 count = in.readInt();
 228  0 keys = new Object[count];
 229   
 230  0 for (int i = 0; i < count; i++)
 231  0 keys[i] = in.readObject();
 232    }
 233    }