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
getAllowedFacets()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
getActualValue(String content, ValidationContext context)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
compare(Object value1, Object value2)57     public final int compare(Object value1, Object value2){
58         return ((XDecimal)value1).compareTo((XDecimal)value2);
59     }
60 
61     @Override
getTotalDigits(Object value)62     public final int getTotalDigits(Object value){
63         return ((XDecimal)value).totalDigits;
64     }
65 
66     @Override
getFractionDigits(Object value)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 
XDecimal(String content)88         XDecimal(String content) throws NumberFormatException {
89             initD(content);
90         }
XDecimal(String content, boolean integer)91         XDecimal(String content, boolean integer) throws NumberFormatException {
92             if (integer)
93                 initI(content);
94             else
95                 initD(content);
96         }
initD(String content)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         }
initI(String content)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
equals(Object val)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
hashCode()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 
compareTo(XDecimal val)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         }
intComp(XDecimal val)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
toString()275         public synchronized String toString() {
276             if (canonical == null) {
277                 makeCanonical();
278             }
279             return canonical;
280         }
281 
makeCanonical()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
getBigDecimal()315         public BigDecimal getBigDecimal() {
316             if (sign == 0) {
317                 return new BigDecimal(BigInteger.ZERO);
318             }
319             return new BigDecimal(toString());
320         }
321 
322         @Override
getBigInteger()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
getLong()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
getInt()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
getShort()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
getByte()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
393