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