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