You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sc...@apache.org on 2002/08/16 00:37:30 UTC
cvs commit: jakarta-commons/lang/src/test/org/apache/commons/lang HashCodeBuilderTest.java LangTestSuite.java HashCodeUtilsTest.java
scolebourne 2002/08/15 15:37:30
Modified: lang/src/test/org/apache/commons/lang LangTestSuite.java
Added: lang/src/java/org/apache/commons/lang HashCodeBuilder.java
lang/src/test/org/apache/commons/lang
HashCodeBuilderTest.java
Removed: lang/src/java/org/apache/commons/lang HashCodeUtils.java
lang/src/test/org/apache/commons/lang HashCodeUtilsTest.java
Log:
Change HashCodeUtils to HashCodeBuilder
Revision Changes Path
1.1 jakarta-commons/lang/src/java/org/apache/commons/lang/HashCodeBuilder.java
Index: HashCodeBuilder.java
===================================================================
package org.apache.commons.lang;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements 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/>.
*/
/**
* <code>HashCode</code> generation routines.
* <p>
* This class enables a good hashcode to be built for any class. It follows
* the rules laid out in the book Effective Java, by Joshua Bloch. Writing a
* good hashCode is actually quite difficult. This class aims to simplify the
* process.
* <p>
* All relevant fields from the object should be included in the hashCode. Derived
* fields may be excluded. In general, any field used in the equals method must be
* used in the hashCode method.
* <p>
* To use this class write code as follows:
* <code>
* public class Person {
* String name;
* int age;
* boolean isSmoker;
*
* ...
*
* public int hashCode() {
* // you pick a hard-coded, randomly chosen, non-zero, odd number
* // ideally different for each class
* return new HashCodeBuilder(17).
* append(name).
* append(age).
* append(smoker).
* toHashCode();
* }
* }
* </code>
*
* @author <a href="mailto:scolebourne@joda.org">Stephen Colebourne</a>
* @version $Id: HashCodeBuilder.java,v 1.1 2002/08/15 22:37:29 scolebourne Exp $
*/
public class HashCodeBuilder {
/**
* Constant to use in building the hashCode
*/
private final int iConstant;
/**
* Running total of the hashCode
*/
private int iTotal = 0;
/**
* Constructor for HashCodeBuilder.
* This constructor uses two hard coded choices for the constants needed
* to build a hashCode.
*/
public HashCodeBuilder() {
super();
iConstant = 37;
iTotal = 17;
}
/**
* Constructor for HashCodeBuilder.
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
* these should be different for each class, however this is not vital.
* Prime numbers are preferred, especially for the multiplier.
*
* @param initialNonZeroOddNumber
* @param multiplierNonZeroOddNumber
* @throws IllegalArgumentException if the number is zero or even
*/
public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) {
super();
if (initialNonZeroOddNumber == 0) {
throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value");
}
if (initialNonZeroOddNumber % 2 == 0) {
throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value");
}
if (multiplierNonZeroOddNumber == 0) {
throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier");
}
if (multiplierNonZeroOddNumber % 2 == 0) {
throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier");
}
iConstant = multiplierNonZeroOddNumber;
iTotal = initialNonZeroOddNumber;
}
/**
* Append a hashCode for an Object.
*
* @param object the object to add to the hashCode
* @return updated totalSoFar
*/
public HashCodeBuilder append(Object object) {
if (object == null) {
iTotal = iTotal * iConstant;
} else {
iTotal = iTotal * iConstant + object.hashCode();
}
return this;
}
/**
* Append a hashCode for a long.
*
* @param value the long to add to the hashCode
* @return this
*/
public HashCodeBuilder append(long value) {
iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
return this;
}
/**
* Append a hashCode for an int.
*
* @param value the int to add to the hashCode
* @return this
*/
public HashCodeBuilder append(int value) {
iTotal = iTotal * iConstant + value;
return this;
}
/**
* Append a hashCode for a short.
*
* @param value the short to add to the hashCode
* @return this
*/
public HashCodeBuilder append(short value) {
iTotal = iTotal * iConstant + (int) value;
return this;
}
/**
* Append a hashCode for a char.
*
* @param value the char to add to the hashCode
* @return this
*/
public HashCodeBuilder append(char value) {
iTotal = iTotal * iConstant + (int) value;
return this;
}
/**
* Append a hashCode for a byte.
*
* @param value the byte to add to the hashCode
* @return this
*/
public HashCodeBuilder append(byte value) {
iTotal = iTotal * iConstant + (int) value;
return this;
}
/**
* Append a hashCode for a double.
*
* @param value the double to add to the hashCode
* @return this
*/
public HashCodeBuilder append(double value) {
return append(Double.doubleToLongBits(value));
}
/**
* Append a hashCode for a float.
*
* @param value the float to add to the hashCode
* @return this
*/
public HashCodeBuilder append(float value) {
iTotal = iTotal * iConstant + Float.floatToIntBits(value);
return this;
}
/**
* Append a hashCode for a long.
*
* @param value the long to add to the hashCode
* @return this
*/
public HashCodeBuilder append(boolean value) {
iTotal = iTotal * iConstant + (value ? 0 : 1);
return this;
}
/**
* Append a hashCode for an Object array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(Object[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Append a hashCode for a long array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(long[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Append a hashCode for an int array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(int[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Append a hashCode for a short array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(short[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Append a hashCode for a char array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(char[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Append a hashCode for a byte array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(byte[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Append a hashCode for a double array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(double[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Append a hashCode for a float array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(float[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Append a hashCode for a boolean array.
*
* @param array the array to add to the hashCode
* @return this
*/
public HashCodeBuilder append(boolean[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* Return the computed hashCode
*
* @return int hashCode based on the fields appended
*/
public int toHashCode() {
return iTotal;
}
}
1.3 +2 -2 jakarta-commons/lang/src/test/org/apache/commons/lang/LangTestSuite.java
Index: LangTestSuite.java
===================================================================
RCS file: /home/cvs/jakarta-commons/lang/src/test/org/apache/commons/lang/LangTestSuite.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- LangTestSuite.java 10 Aug 2002 12:13:28 -0000 1.2
+++ LangTestSuite.java 15 Aug 2002 22:37:29 -0000 1.3
@@ -87,7 +87,7 @@
TestSuite suite = new TestSuite();
suite.setName("Commons-Lang Tests");
suite.addTest(CharSetUtilsTest.suite());
- suite.addTest(HashCodeUtilsTest.suite());
+ suite.addTest(HashCodeBuilderTest.suite());
suite.addTest(NumberRangeTest.suite());
suite.addTest(NumberUtilsTest.suite());
suite.addTest(ObjectUtilsTest.suite());
1.1 jakarta-commons/lang/src/test/org/apache/commons/lang/HashCodeBuilderTest.java
Index: HashCodeBuilderTest.java
===================================================================
package org.apache.commons.lang;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements 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/>.
*/
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
/**
* Unit tests {@link org.apache.commons.lang.HashCodeBuilder}.
*
* @author <a href="mailto:scolebourne@joda.org">Stephen Colebourne</a>
* @version $Id: HashCodeBuilderTest.java,v 1.1 2002/08/15 22:37:30 scolebourne Exp $
*/
public class HashCodeBuilderTest extends TestCase {
public HashCodeBuilderTest(String name) {
super(name);
}
public static void main(String[] args) {
TestRunner.run(suite());
}
public static Test suite() {
TestSuite suite = new TestSuite(HashCodeBuilderTest.class);
suite.setName("HashCodeBuilder Tests");
return suite;
}
protected void setUp() throws Exception {
super.setUp();
}
protected void tearDown() throws Exception {
super.tearDown();
}
//-----------------------------------------------------------------------
public void testConstructorEx1() {
try {
new HashCodeBuilder(0, 0);
} catch (IllegalArgumentException ex) {
return;
}
fail();
}
public void testConstructorEx2() {
try {
new HashCodeBuilder(2, 2);
} catch (IllegalArgumentException ex) {
return;
}
fail();
}
public void testObject() {
Object obj = null;
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj = new Object();
assertEquals(17 * 37 + obj.hashCode(), new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testLong() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((long) 0L).toHashCode());
assertEquals(17 * 37 + (int) (123456789L ^ (123456789L >> 32)), new HashCodeBuilder(17, 37).append((long) 123456789L).toHashCode());
}
public void testInt() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((int) 0).toHashCode());
assertEquals(17 * 37 + 123456, new HashCodeBuilder(17, 37).append((int) 123456).toHashCode());
}
public void testShort() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((short) 0).toHashCode());
assertEquals(17 * 37 + 12345, new HashCodeBuilder(17, 37).append((short) 12345).toHashCode());
}
public void testChar() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((char) 0).toHashCode());
assertEquals(17 * 37 + 1234, new HashCodeBuilder(17, 37).append((char) 1234).toHashCode());
}
public void testByte() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((byte) 0).toHashCode());
assertEquals(17 * 37 + 123, new HashCodeBuilder(17, 37).append((byte) 123).toHashCode());
}
public void testDouble() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((double) 0d).toHashCode());
double d = 1234567.89;
long l = Double.doubleToLongBits(d);
assertEquals(17 * 37 + (int) (l ^ (l >> 32)), new HashCodeBuilder(17, 37).append(d).toHashCode());
}
public void testFloat() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((float) 0f).toHashCode());
float f = 1234.89f;
int i = Float.floatToIntBits(f);
assertEquals(17 * 37 + i, new HashCodeBuilder(17, 37).append(f).toHashCode());
}
public void testBoolean() {
assertEquals(17 * 37 + 0, new HashCodeBuilder(17, 37).append(true).toHashCode());
assertEquals(17 * 37 + 1, new HashCodeBuilder(17, 37).append(false).toHashCode());
}
public void testObjectArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((Object[]) null).toHashCode());
Object[] obj = new Object[2];
assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = new Object();
assertEquals((17 * 37 + obj[0].hashCode()) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = new Object();
assertEquals( (17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testLongArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((long[]) null).toHashCode());
long[] obj = new long[2];
assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = 5L;
int h1 = (int) (5L ^ (5L >> 32));
assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = 6L;
int h2 = (int) (6L ^ (6L >> 32));
assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testIntArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((int[]) null).toHashCode());
int[] obj = new int[2];
assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = 5;
assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = 6;
assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testShortArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((short[]) null).toHashCode());
short[] obj = new short[2];
assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = (short) 5;
assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = (short) 6;
assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testCharArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((char[]) null).toHashCode());
char[] obj = new char[2];
assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = (char) 5;
assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = (char) 6;
assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testByteArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((byte[]) null).toHashCode());
byte[] obj = new byte[2];
assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = (byte) 5;
assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = (byte) 6;
assertEquals( (17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testDoubleArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((double[]) null).toHashCode());
double[] obj = new double[2];
assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = 5.4d;
long l1 = Double.doubleToLongBits(5.4d);
int h1 = (int) (l1 ^ (l1 >> 32));
assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = 6.3d;
long l2 = Double.doubleToLongBits(6.3d);
int h2 = (int) (l2 ^ (l2 >> 32));
assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testFloatArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((float[]) null).toHashCode());
float[] obj = new float[2];
assertEquals((17 * 37) * 37 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = 5.4f;
int h1 = Float.floatToIntBits(5.4f);
assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = 6.3f;
int h2 = Float.floatToIntBits(6.3f);
assertEquals( (17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
public void testBooleanArray() {
assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((boolean[]) null).toHashCode());
boolean[] obj = new boolean[2];
assertEquals((17 * 37 + 1) * 37 + 1 , new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[0] = true;
assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode());
obj[1] = false;
assertEquals( (17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode());
}
}
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>
Re: cvs commit: jakarta-commons/lang/src/test/org/apache/commons/lang HashCodeBuilderTest.java LangTestSuite.java HashCodeUtilsTest.java
Posted by Daniel Rall <dl...@finemaltcoding.com>.
I like it, much nicer.
--
Daniel Rall <dl...@finemaltcoding.com>
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>