You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by "Alan M. Feldstein (JIRA)" <ji...@apache.org> on 2006/03/17 20:35:00 UTC

[jira] Commented: (AXIS2-492) new UnsignedLong( 0xffffffffffffffffL ) should not throw a NumberFormatException

    [ 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