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