jaxp/src/com/sun/org/apache/xerces/internal/impl/dv/xs/PrecisionDecimalDV.java

Print this page

        

@@ -30,11 +30,11 @@
  * @author Ankit Pasricha, IBM
  *
  */
 class PrecisionDecimalDV extends TypeValidator {
 
-    static class XPrecisionDecimal {
+    static final class XPrecisionDecimal {
 
         // sign: 0 for absent; 1 for positive values; -1 for negative values (except in case of INF, -INF)
         int sign = 1;
         // total digits. >= 1
         int totalDigits = 0;

@@ -142,11 +142,75 @@
                 }
             }
             totalDigits = intDigits + fracDigits;
         }
 
+        // Construct a canonical String representation of this number
+        // for the purpose of deriving a hashCode value compliant with 
+        // equals.
+        // The toString representation will be:
+        // NaN for NaN, INF for +infinity, -INF for -infinity, 0 for zero,
+        // and [1-9].[0-9]*[1-9]?(E[1-9][0-9]*)? for other numbers.
+        private static String canonicalToStringForHashCode(String ivalue, String fvalue, int sign, int pvalue) {
+            if ("NaN".equals(ivalue)) {
+                return "NaN";
+            }
+            if ("INF".equals(ivalue)) {
+                return sign < 0 ? "-INF" : "INF";
+            }
+            final StringBuilder builder = new StringBuilder();
+            final int ilen = ivalue.length();
+            final int flen0 = fvalue.length();
+            int lastNonZero;
+            for (lastNonZero = flen0; lastNonZero > 0 ; lastNonZero--) {
+                if (fvalue.charAt(lastNonZero -1 ) != '0') break;
+            }
+            final int flen = lastNonZero;
+            int iStart;
+            int exponent = pvalue;
+            for (iStart = 0; iStart < ilen; iStart++) {
+                if (ivalue.charAt(iStart) != '0') break;
+            }
+            int fStart = 0;
+            if (iStart < ivalue.length()) {
+                builder.append(sign == -1 ? "-" : "");
+                builder.append(ivalue.charAt(iStart));
+                iStart++;
+            } else {
+                if (flen > 0) {
+                    for (fStart = 0; fStart < flen; fStart++) {
+                        if (fvalue.charAt(fStart) != '0') break;
+                    }
+                    if (fStart < flen) {
+                        builder.append(sign == -1 ? "-" : "");
+                        builder.append(fvalue.charAt(fStart));
+                        exponent -= ++fStart;
+                    } else {
+                        return "0";
+                    }
+                } else {
+                    return "0";
+                }
+            }
+
+            if (iStart < ilen || fStart < flen) {
+                builder.append('.');
+            }
+            while (iStart < ilen) {
+                builder.append(ivalue.charAt(iStart++));
+                exponent++;
+            }
+            while (fStart < flen) {
+                builder.append(fvalue.charAt(fStart++));
+            }
+            if (exponent != 0) {
+                builder.append("E").append(exponent);
+            }
+            return builder.toString();
+        }
 
+        @Override
         public boolean equals(Object val) {
             if (val == this)
                 return true;
 
             if (!(val instanceof XPrecisionDecimal))

@@ -154,10 +218,24 @@
             XPrecisionDecimal oval = (XPrecisionDecimal)val;
 
             return this.compareTo(oval) == EQUAL;
         }
 
+        @Override
+        public int hashCode() {
+            // There's nothing else we can use easily, because equals could
+            // return true for widely different representation of the
+            // same number - and we don't have any canonical representation.
+            // The problem here is that we must ensure that if two numbers
+            // are equals then their hash code must also be equals.
+            // hashCode for 1.01E1 should be the same as hashCode for 0.101E2
+            // So we call cannonicalToStringForHashCode - which implements an
+            // algorithm that invents a normalized string representation
+            // for this number, and we return a hash for that.
+            return canonicalToStringForHashCode(ivalue, fvalue, sign, pvalue).hashCode();
+        }
+
         /**
          * @return
          */
         private int compareFractionalPart(XPrecisionDecimal oval) {
             if(fvalue.equals(oval.fvalue))

@@ -293,10 +371,11 @@
             return ret == 0 ? EQUAL : (ret > 0 ? GREATER_THAN : LESS_THAN);
         }
 
         private String canonical;
 
+        @Override
         public synchronized String toString() {
             if (canonical == null) {
                 makeCanonical();
             }
             return canonical;

@@ -323,38 +402,44 @@
 
     }
     /* (non-Javadoc)
      * @see com.sun.org.apache.xerces.internal.impl.dv.xs.TypeValidator#getAllowedFacets()
      */
+    @Override
     public short getAllowedFacets() {
         return ( XSSimpleTypeDecl.FACET_PATTERN | XSSimpleTypeDecl.FACET_WHITESPACE | XSSimpleTypeDecl.FACET_ENUMERATION |XSSimpleTypeDecl.FACET_MAXINCLUSIVE |XSSimpleTypeDecl.FACET_MININCLUSIVE | XSSimpleTypeDecl.FACET_MAXEXCLUSIVE  | XSSimpleTypeDecl.FACET_MINEXCLUSIVE | XSSimpleTypeDecl.FACET_TOTALDIGITS | XSSimpleTypeDecl.FACET_FRACTIONDIGITS);
     }
 
     /* (non-Javadoc)
      * @see com.sun.org.apache.xerces.internal.impl.dv.xs.TypeValidator#getActualValue(java.lang.String, com.sun.org.apache.xerces.internal.impl.dv.ValidationContext)
      */
+    @Override
     public Object getActualValue(String content, ValidationContext context)
     throws InvalidDatatypeValueException {
         try {
             return new XPrecisionDecimal(content);
         } catch (NumberFormatException nfe) {
             throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{content, "precisionDecimal"});
         }
     }
 
+    @Override
     public int compare(Object value1, Object value2) {
         return ((XPrecisionDecimal)value1).compareTo((XPrecisionDecimal)value2);
     }
 
+    @Override
     public int getFractionDigits(Object value) {
         return ((XPrecisionDecimal)value).fracDigits;
     }
 
+    @Override
     public int getTotalDigits(Object value) {
         return ((XPrecisionDecimal)value).totalDigits;
     }
 
+    @Override
     public boolean isIdentical(Object value1, Object value2) {
         if(!(value2 instanceof XPrecisionDecimal) || !(value1 instanceof XPrecisionDecimal))
             return false;
         return ((XPrecisionDecimal)value1).isIdentical((XPrecisionDecimal)value2);
     }