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