[ http://issues.apache.org/jira/browse/AXIS2-492?page=comments#action_12370871 ]
Alan M. Feldstein commented on AXIS2-492: ----------------------------------------- Index: modules/codegen/test/org/apache/axis2/schema/populate/derived/DerivedTypeUnsignedLongPopulateTest.java =================================================================== --- modules/codegen/test/org/apache/axis2/schema/populate/derived/DerivedTypeUnsignedLongPopulateTest.java (revision 386676) +++ modules/codegen/test/org/apache/axis2/schema/populate/derived/DerivedTypeUnsignedLongPopulateTest.java (working copy) @@ -25,6 +25,7 @@ "1", "0", "26758223334334" , + "18446744073709551615", "-1" , "-267582233" @@ -35,7 +36,8 @@ "<DerivedUnsignedLong xmlns=\"http://soapinterop.org/xsd\">"+values[1]+"</DerivedUnsignedLong>", "<DerivedUnsignedLong xmlns=\"http://soapinterop.org/xsd\">"+values[2]+"</DerivedUnsignedLong>", "<DerivedUnsignedLong xmlns=\"http://soapinterop.org/xsd\">"+values[3]+"</DerivedUnsignedLong>", - "<DerivedUnsignedLong xmlns=\"http://soapinterop.org/xsd\">"+values[4]+"</DerivedUnsignedLong>" + "<DerivedUnsignedLong xmlns=\"http://soapinterop.org/xsd\">"+values[4]+"</DerivedUnsignedLong>", + "<DerivedUnsignedLong xmlns=\"http://soapinterop.org/xsd\">"+values[5]+"</DerivedUnsignedLong>" }; @@ -49,11 +51,11 @@ // force others to implement this method public void testPopulate() throws Exception { - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { checkValue(xmlString[i],values[i]); } - for (int i = 3; i < values.length; i++) { + for (int i = 4; i < values.length; i++) { try { checkValue(xmlString[i],values[i]); fail(); Index: modules/adb/src/org/apache/axis2/databinding/types/UnsignedLong.java =================================================================== --- modules/adb/src/org/apache/axis2/databinding/types/UnsignedLong.java (revision 386676) +++ modules/adb/src/org/apache/axis2/databinding/types/UnsignedLong.java (working copy) @@ -15,6 +15,10 @@ */ package org.apache.axis2.databinding.types; +// Consider removing this. +// All operations behave as if BigIntegers were represented in two's-complement notation. +// In its place, consider using primitive type long (which is already the right size) to hold the data. +// This class can hide the fact that the data is stored in a signed entity, by careful implementation of the class' methods. import java.math.BigInteger; /** @@ -22,7 +26,7 @@ * * @see <a href="http://www.w3.org/TR/xmlschema-2/#unsignedLong">XML Schema 3.3.21</a> */ -public class UnsignedLong extends java.lang.Number { +public class UnsignedLong extends java.lang.Number implements Comparable { private static final long serialVersionUID = -5919942584284897583L; @@ -40,17 +44,41 @@ setValue(value); } - public UnsignedLong(long lValue) throws NumberFormatException { + public UnsignedLong(long lValue) throws IllegalArgumentException { + // new UnsignedLong( 0xffffffffffffffffL ) + // should not throw any Exception because, as an UnsignedLong, it is in range and nonnegative. setValue(BigInteger.valueOf(lValue)); } public UnsignedLong(String stValue) throws NumberFormatException { - setValue(new BigInteger(stValue)); + + // If stValue starts with a minus sign, that will be acceptable to the BigInteger constructor, + // but it is not acceptable to us. + // Once encoded into binary, it is too late to detect that the client intended a negative integer. + // That detection must be performed here. + try { + if (stValue.charAt(0) == '\u002d') + { + throw new NumberFormatException("A String that starts with a minus sign is not a valid representation of an UnsignedLong."); + } + setValue(new BigInteger(stValue)); + } + + catch ( NumberFormatException numberFormatException ) { + throw numberFormatException; + } + + catch ( IndexOutOfBoundsException indexOutOfBoundsException ) { + // This could happen if stValue is empty when we attempt to detect a minus sign. + // From the client's point of view, the empty String should cause a NumberFormatException. + throw new NumberFormatException("An empty string is not a valid representation of an UnsignedLong."); + } + } private void setValue(BigInteger val) { if (!UnsignedLong.isValid(val)) { - throw new NumberFormatException( + throw new IllegalArgumentException( // Messages.getMessage("badUnsignedLong00") + String.valueOf(val) + "]"); } @@ -58,8 +86,15 @@ } public static boolean isValid(BigInteger value) { - return !(value.compareTo(BigInteger.ZERO) == -1 || // less than zero - value.compareTo(MAX) == 1); + + // Converts this BigInteger to a long. + // This conversion is analogous to a narrowing primitive conversion from long to int as defined in the Java Language Specification: + // if this BigInteger is too big to fit in a long, only the low-order 64 bits are returned. + // Note that this conversion can lose information about the overall magnitude of the BigInteger value as well as return a result with the opposite sign. + long unsignedLongValue = value.longValue(); + + return !(compare(unsignedLongValue, BigInteger.ZERO.longValue()) < 0 || // less than zero + compare(unsignedLongValue, MAX.longValue()) > 0); } public String toString() { @@ -117,4 +152,88 @@ return lValue.floatValue(); } + /** + * @return the value 0 if the argument is an UnsignedLong numerically equal to this UnsignedLong; a value less than 0 if the argument is an UnsignedLong numerically greater than this UnsignedLong; and a value greater than 0 if the argument is an UnsignedLong numerically less than this UnsignedLong. + */ + public int compareTo( Object o ) + throws ClassCastException, NullPointerException + { + int retVal = 0; // arbitrary default value in case of exception; required return value in case this object is equal to the specified object + + try { + if ( o == null ) + { + throw new NullPointerException( "Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException." ); + } + if ( ! ( o instanceof UnsignedLong ) ) + { + throw new ClassCastException( "The argument is not an UnsignedLong." ); + } + // Only need to change retVal if this object is not equal to the specified object. + retVal = compare( longValue(), ( (UnsignedLong) o ).longValue() ); + } + + catch ( NullPointerException nullPointerException ) { + throw nullPointerException; + } + + catch ( ClassCastException classCastException ) { + throw classCastException; + } + + finally { + return retVal; + } + + } + + /** + * @return the value 0 if thatLong is a long numerically equal to thisLong; a value less than 0 if thatLong is a long numerically greater than thisLong; and a value greater than 0 if thatLong is a long numerically less than thisLong (unsigned comparison). + */ + private static int compare( long thisLong, long thatLong ) + { + // To avoid infinite recursion, do not instantiate UnsignedLong in this method, which may be called during UnsignedLong instantiation. + + if ( thisLong == thatLong ) + { + return 0; + } + else + { + boolean isLessThan; // This is less than that. + + // Prepare the most significant half of the data for comparison. + // The shift distance can be any number from 1 to 32 inclusive (1 is probably fastest). + // A shift distance of one is sufficient to move the significant data off of the sign bit, allowing for a signed comparison of positive numbers (i.e. an unsigned comparison). + long thisHalfLong = ( thisLong & 0xffffffff00000000L ) >>> 1; + long thatHalfLong = ( thatLong & 0xffffffff00000000L ) >>> 1; + + if ( thisHalfLong == thatHalfLong ) + { + // We must also look at the least significant half of the data. + + // Prepare the least significant half of the data for comparison. + thisHalfLong = ( thisLong & 0x00000000ffffffffL ); + thatHalfLong = ( thatLong & 0x00000000ffffffffL ); + + // We already know that the data is not equal. + isLessThan = thisHalfLong < thatHalfLong; + } + else + { + // The answer is in the most significant half of the data. + isLessThan = thisHalfLong < thatHalfLong; + } + + if ( isLessThan ) + { + return -1; // Returns a negative integer as this object is less than than the specified object. + } + else + { + return 1; // Returns a positive integer as this object is greater than than the specified object. + } + } + } + } > new UnsignedLong( 0xffffffffffffffffL ) should not throw a > NumberFormatException > -------------------------------------------------------------------------------- > > Key: AXIS2-492 > URL: http://issues.apache.org/jira/browse/AXIS2-492 > Project: Apache Axis 2.0 (Axis2) > Type: Bug > Components: databinding > Versions: 0.95 > Environment: Java 2 Platform SE 5.0 > Reporter: Alan M. Feldstein > > new UnsignedLong( 0xffffffffffffffffL ) > should not throw any Exception because, as an UnsignedLong, it is in range > and nonnegative. > Constructor > UnsignedLong(long lValue) > should never throw a NumberFormatException because that indicates that the > application has attempted to convert a string to one of the numeric types. > There is no string involved. > IllegalArgumentException would be more correct. -- This message is automatically generated by JIRA. - If you think it was sent incorrectly contact one of the administrators: http://issues.apache.org/jira/secure/Administrators.jspa - For more information on JIRA, see: http://www.atlassian.com/software/jira