1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001,2002,2004,2005 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl.dv.xs;
  22 
  23 import java.math.BigDecimal;
  24 import java.math.BigInteger;
  25 
  26 import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
  27 import com.sun.org.apache.xerces.internal.impl.dv.ValidationContext;
  28 import com.sun.org.apache.xerces.internal.xs.datatypes.XSDecimal;
  29 
  30 /**
  31  * Represent the schema type "decimal"
  32  *
  33  * @xerces.internal
  34  *
  35  * @author Neeraj Bajaj, Sun Microsystems, inc.
  36  * @author Sandy Gao, IBM
  37  *
  38  */
  39 public class DecimalDV extends TypeValidator {
  40 
  41     public final short getAllowedFacets(){
  42         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);
  43     }
  44 
  45     public Object getActualValue(String content, ValidationContext context) throws InvalidDatatypeValueException {
  46         try {
  47             return new XDecimal(content);
  48         } catch (NumberFormatException nfe) {
  49             throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{content, "decimal"});
  50         }
  51     }
  52 
  53     public final int compare(Object value1, Object value2){
  54         return ((XDecimal)value1).compareTo((XDecimal)value2);
  55     }
  56 
  57     public final int getTotalDigits(Object value){
  58         return ((XDecimal)value).totalDigits;
  59     }
  60 
  61     public final int getFractionDigits(Object value){
  62         return ((XDecimal)value).fracDigits;
  63     }
  64 
  65     // Avoid using the heavy-weight java.math.BigDecimal
  66     static class XDecimal implements XSDecimal {
  67         // sign: 0 for vlaue 0; 1 for positive values; -1 for negative values
  68         int sign = 1;
  69         // total digits. >= 1
  70         int totalDigits = 0;
  71         // integer digits when sign != 0
  72         int intDigits = 0;
  73         // fraction digits when sign != 0
  74         int fracDigits = 0;
  75         // the string representing the integer part
  76         String ivalue = "";
  77         // the string representing the fraction part
  78         String fvalue = "";
  79         // whether the canonical form contains decimal point
  80         boolean integer = false;
  81 
  82         XDecimal(String content) throws NumberFormatException {
  83             initD(content);
  84         }
  85         XDecimal(String content, boolean integer) throws NumberFormatException {
  86             if (integer)
  87                 initI(content);
  88             else
  89                 initD(content);
  90         }
  91         void initD(String content) throws NumberFormatException {
  92             int len = content.length();
  93             if (len == 0)
  94                 throw new NumberFormatException();
  95 
  96             // these 4 variables are used to indicate where the integre/fraction
  97             // parts start/end.
  98             int intStart = 0, intEnd = 0, fracStart = 0, fracEnd = 0;
  99 
 100             // Deal with leading sign symbol if present
 101             if (content.charAt(0) == '+') {
 102                 // skip '+', so intStart should be 1
 103                 intStart = 1;
 104             }
 105             else if (content.charAt(0) == '-') {
 106                 // keep '-', so intStart is stil 0
 107                 intStart = 1;
 108                 sign = -1;
 109             }
 110 
 111             // skip leading zeroes in integer part
 112             int actualIntStart = intStart;
 113             while (actualIntStart < len && content.charAt(actualIntStart) == '0') {
 114                 actualIntStart++;
 115             }
 116 
 117             // Find the ending position of the integer part
 118             for (intEnd = actualIntStart;
 119                  intEnd < len && TypeValidator.isDigit(content.charAt(intEnd));
 120                  intEnd++);
 121 
 122             // Not reached the end yet
 123             if (intEnd < len) {
 124                 // the remaining part is not ".DDD", error
 125                 if (content.charAt(intEnd) != '.')
 126                     throw new NumberFormatException();
 127 
 128                 // fraction part starts after '.', and ends at the end of the input
 129                 fracStart = intEnd + 1;
 130                 fracEnd = len;
 131             }
 132 
 133             // no integer part, no fraction part, error.
 134             if (intStart == intEnd && fracStart == fracEnd)
 135                 throw new NumberFormatException();
 136 
 137             // ignore trailing zeroes in fraction part
 138             while (fracEnd > fracStart && content.charAt(fracEnd-1) == '0') {
 139                 fracEnd--;
 140             }
 141 
 142             // check whether there is non-digit characters in the fraction part
 143             for (int fracPos = fracStart; fracPos < fracEnd; fracPos++) {
 144                 if (!TypeValidator.isDigit(content.charAt(fracPos)))
 145                     throw new NumberFormatException();
 146             }
 147 
 148             intDigits = intEnd - actualIntStart;
 149             fracDigits = fracEnd - fracStart;
 150             totalDigits = intDigits + fracDigits;
 151 
 152             if (intDigits > 0) {
 153                 ivalue = content.substring(actualIntStart, intEnd);
 154                 if (fracDigits > 0)
 155                     fvalue = content.substring(fracStart, fracEnd);
 156             }
 157             else {
 158                 if (fracDigits > 0) {
 159                     fvalue = content.substring(fracStart, fracEnd);
 160                 }
 161                 else {
 162                     // ".00", treat it as "0"
 163                     sign = 0;
 164                 }
 165             }
 166         }
 167         void initI(String content) throws NumberFormatException {
 168             int len = content.length();
 169             if (len == 0)
 170                 throw new NumberFormatException();
 171 
 172             // these 2 variables are used to indicate where the integre start/end.
 173             int intStart = 0, intEnd = 0;
 174 
 175             // Deal with leading sign symbol if present
 176             if (content.charAt(0) == '+') {
 177                 // skip '+', so intStart should be 1
 178                 intStart = 1;
 179             }
 180             else if (content.charAt(0) == '-') {
 181                 // keep '-', so intStart is stil 0
 182                 intStart = 1;
 183                 sign = -1;
 184             }
 185 
 186             // skip leading zeroes in integer part
 187             int actualIntStart = intStart;
 188             while (actualIntStart < len && content.charAt(actualIntStart) == '0') {
 189                 actualIntStart++;
 190             }
 191 
 192             // Find the ending position of the integer part
 193             for (intEnd = actualIntStart;
 194                  intEnd < len && TypeValidator.isDigit(content.charAt(intEnd));
 195                  intEnd++);
 196 
 197             // Not reached the end yet, error
 198             if (intEnd < len)
 199                 throw new NumberFormatException();
 200 
 201             // no integer part, error.
 202             if (intStart == intEnd)
 203                 throw new NumberFormatException();
 204 
 205             intDigits = intEnd - actualIntStart;
 206             fracDigits = 0;
 207             totalDigits = intDigits;
 208 
 209             if (intDigits > 0) {
 210                 ivalue = content.substring(actualIntStart, intEnd);
 211             }
 212             else {
 213                 // "00", treat it as "0"
 214                 sign = 0;
 215             }
 216 
 217             integer = true;
 218         }
 219         public boolean equals(Object val) {
 220             if (val == this)
 221                 return true;
 222 
 223             if (!(val instanceof XDecimal))
 224                 return false;
 225             XDecimal oval = (XDecimal)val;
 226 
 227             if (sign != oval.sign)
 228                return false;
 229             if (sign == 0)
 230                 return true;
 231 
 232             return intDigits == oval.intDigits && fracDigits == oval.fracDigits &&
 233                    ivalue.equals(oval.ivalue) && fvalue.equals(oval.fvalue);
 234         }
 235         public int compareTo(XDecimal val) {
 236             if (sign != val.sign)
 237                 return sign > val.sign ? 1 : -1;
 238             if (sign == 0)
 239                 return 0;
 240             return sign * intComp(val);
 241         }
 242         private int intComp(XDecimal val) {
 243             if (intDigits != val.intDigits)
 244                 return intDigits > val.intDigits ? 1 : -1;
 245             int ret = ivalue.compareTo(val.ivalue);
 246             if (ret != 0)
 247                 return ret > 0 ? 1 : -1;;
 248             ret = fvalue.compareTo(val.fvalue);
 249             return ret == 0 ? 0 : (ret > 0 ? 1 : -1);
 250         }
 251         private String canonical;
 252         public synchronized String toString() {
 253             if (canonical == null) {
 254                 makeCanonical();
 255             }
 256             return canonical;
 257         }
 258 
 259         private void makeCanonical() {
 260             if (sign == 0) {
 261                 if (integer)
 262                     canonical = "0";
 263                 else
 264                     canonical = "0.0";
 265                 return;
 266             }
 267             if (integer && sign > 0) {
 268                 canonical = ivalue;
 269                 return;
 270             }
 271             // for -0.1, total digits is 1, so we need 3 extra spots
 272             StringBuffer buffer = new StringBuffer(totalDigits+3);
 273             if (sign == -1)
 274                 buffer.append('-');
 275             if (intDigits != 0)
 276                 buffer.append(ivalue);
 277             else
 278                 buffer.append('0');
 279             if (!integer) {
 280                 buffer.append('.');
 281                 if (fracDigits != 0) {
 282                     buffer.append(fvalue);
 283                 }
 284                 else {
 285                     buffer.append('0');
 286                 }
 287             }
 288             canonical = buffer.toString();
 289         }
 290 
 291         public BigDecimal getBigDecimal() {
 292             if (sign == 0) {
 293                 return new BigDecimal(BigInteger.ZERO);
 294             }
 295             return new BigDecimal(toString());
 296         }
 297 
 298         public BigInteger getBigInteger() throws NumberFormatException {
 299             if (fracDigits != 0) {
 300                 throw new NumberFormatException();
 301             }
 302             if (sign == 0) {
 303                 return BigInteger.ZERO;
 304             }
 305             if (sign == 1) {
 306                 return new BigInteger(ivalue);
 307             }
 308             return new BigInteger("-" + ivalue);
 309         }
 310 
 311         public long getLong() throws NumberFormatException {
 312             if (fracDigits != 0) {
 313                 throw new NumberFormatException();
 314             }
 315             if (sign == 0) {
 316                 return 0L;
 317             }
 318             if (sign == 1) {
 319                 return Long.parseLong(ivalue);
 320             }
 321             return Long.parseLong("-" + ivalue);
 322         }
 323 
 324         public int getInt() throws NumberFormatException {
 325             if (fracDigits != 0) {
 326                 throw new NumberFormatException();
 327             }
 328             if (sign == 0) {
 329                 return 0;
 330             }
 331             if (sign == 1) {
 332                 return Integer.parseInt(ivalue);
 333             }
 334             return Integer.parseInt("-" + ivalue);
 335         }
 336 
 337         public short getShort() throws NumberFormatException {
 338             if (fracDigits != 0) {
 339                 throw new NumberFormatException();
 340             }
 341             if (sign == 0) {
 342                 return 0;
 343             }
 344             if (sign == 1) {
 345                 return Short.parseShort(ivalue);
 346             }
 347             return Short.parseShort("-" + ivalue);
 348         }
 349 
 350         public byte getByte() throws NumberFormatException {
 351             if (fracDigits != 0) {
 352                 throw new NumberFormatException();
 353             }
 354             if (sign == 0) {
 355                 return 0;
 356             }
 357             if (sign == 1) {
 358                 return Byte.parseByte(ivalue);
 359             }
 360             return Byte.parseByte("-" + ivalue);
 361         }
 362     }
 363 } // class DecimalDV