001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl.dv.xs;
019:
020: import java.math.BigDecimal;
021: import java.math.BigInteger;
022:
023: import org.apache.xerces.impl.dv.InvalidDatatypeValueException;
024: import org.apache.xerces.impl.dv.ValidationContext;
025: import org.apache.xerces.xs.datatypes.XSDecimal;
026:
027: /**
028: * Represent the schema type "decimal"
029: *
030: * @xerces.internal
031: *
032: * @author Neeraj Bajaj, Sun Microsystems, inc.
033: * @author Sandy Gao, IBM
034: *
035: * @version $Id: DecimalDV.java 446745 2006-09-15 21:43:58Z mrglavas $
036: */
037: public class DecimalDV extends TypeValidator {
038:
039: public final short getAllowedFacets() {
040: return (XSSimpleTypeDecl.FACET_PATTERN
041: | XSSimpleTypeDecl.FACET_WHITESPACE
042: | XSSimpleTypeDecl.FACET_ENUMERATION
043: | XSSimpleTypeDecl.FACET_MAXINCLUSIVE
044: | XSSimpleTypeDecl.FACET_MININCLUSIVE
045: | XSSimpleTypeDecl.FACET_MAXEXCLUSIVE
046: | XSSimpleTypeDecl.FACET_MINEXCLUSIVE
047: | XSSimpleTypeDecl.FACET_TOTALDIGITS | XSSimpleTypeDecl.FACET_FRACTIONDIGITS);
048: }
049:
050: public Object getActualValue(String content,
051: ValidationContext context)
052: throws InvalidDatatypeValueException {
053: try {
054: return new XDecimal(content);
055: } catch (NumberFormatException nfe) {
056: throw new InvalidDatatypeValueException(
057: "cvc-datatype-valid.1.2.1", new Object[] { content,
058: "decimal" });
059: }
060: }
061:
062: public final int compare(Object value1, Object value2) {
063: return ((XDecimal) value1).compareTo((XDecimal) value2);
064: }
065:
066: public final int getTotalDigits(Object value) {
067: return ((XDecimal) value).totalDigits;
068: }
069:
070: public final int getFractionDigits(Object value) {
071: return ((XDecimal) value).fracDigits;
072: }
073:
074: // Avoid using the heavy-weight java.math.BigDecimal
075: static class XDecimal implements XSDecimal {
076: // sign: 0 for vlaue 0; 1 for positive values; -1 for negative values
077: int sign = 1;
078: // total digits. >= 1
079: int totalDigits = 0;
080: // integer digits when sign != 0
081: int intDigits = 0;
082: // fraction digits when sign != 0
083: int fracDigits = 0;
084: // the string representing the integer part
085: String ivalue = "";
086: // the string representing the fraction part
087: String fvalue = "";
088: // whether the canonical form contains decimal point
089: boolean integer = false;
090:
091: XDecimal(String content) throws NumberFormatException {
092: initD(content);
093: }
094:
095: XDecimal(String content, boolean integer)
096: throws NumberFormatException {
097: if (integer)
098: initI(content);
099: else
100: initD(content);
101: }
102:
103: void initD(String content) throws NumberFormatException {
104: int len = content.length();
105: if (len == 0)
106: throw new NumberFormatException();
107:
108: // these 4 variables are used to indicate where the integre/fraction
109: // parts start/end.
110: int intStart = 0, intEnd = 0, fracStart = 0, fracEnd = 0;
111:
112: // Deal with leading sign symbol if present
113: if (content.charAt(0) == '+') {
114: // skip '+', so intStart should be 1
115: intStart = 1;
116: } else if (content.charAt(0) == '-') {
117: // keep '-', so intStart is stil 0
118: intStart = 1;
119: sign = -1;
120: }
121:
122: // skip leading zeroes in integer part
123: int actualIntStart = intStart;
124: while (actualIntStart < len
125: && content.charAt(actualIntStart) == '0') {
126: actualIntStart++;
127: }
128:
129: // Find the ending position of the integer part
130: for (intEnd = actualIntStart; intEnd < len
131: && TypeValidator.isDigit(content.charAt(intEnd)); intEnd++)
132: ;
133:
134: // Not reached the end yet
135: if (intEnd < len) {
136: // the remaining part is not ".DDD", error
137: if (content.charAt(intEnd) != '.')
138: throw new NumberFormatException();
139:
140: // fraction part starts after '.', and ends at the end of the input
141: fracStart = intEnd + 1;
142: fracEnd = len;
143: }
144:
145: // no integer part, no fraction part, error.
146: if (intStart == intEnd && fracStart == fracEnd)
147: throw new NumberFormatException();
148:
149: // ignore trailing zeroes in fraction part
150: while (fracEnd > fracStart
151: && content.charAt(fracEnd - 1) == '0') {
152: fracEnd--;
153: }
154:
155: // check whether there is non-digit characters in the fraction part
156: for (int fracPos = fracStart; fracPos < fracEnd; fracPos++) {
157: if (!TypeValidator.isDigit(content.charAt(fracPos)))
158: throw new NumberFormatException();
159: }
160:
161: intDigits = intEnd - actualIntStart;
162: fracDigits = fracEnd - fracStart;
163: totalDigits = intDigits + fracDigits;
164:
165: if (intDigits > 0) {
166: ivalue = content.substring(actualIntStart, intEnd);
167: if (fracDigits > 0)
168: fvalue = content.substring(fracStart, fracEnd);
169: } else {
170: if (fracDigits > 0) {
171: fvalue = content.substring(fracStart, fracEnd);
172: } else {
173: // ".00", treat it as "0"
174: sign = 0;
175: }
176: }
177: }
178:
179: void initI(String content) throws NumberFormatException {
180: int len = content.length();
181: if (len == 0)
182: throw new NumberFormatException();
183:
184: // these 2 variables are used to indicate where the integre start/end.
185: int intStart = 0, intEnd = 0;
186:
187: // Deal with leading sign symbol if present
188: if (content.charAt(0) == '+') {
189: // skip '+', so intStart should be 1
190: intStart = 1;
191: } else if (content.charAt(0) == '-') {
192: // keep '-', so intStart is stil 0
193: intStart = 1;
194: sign = -1;
195: }
196:
197: // skip leading zeroes in integer part
198: int actualIntStart = intStart;
199: while (actualIntStart < len
200: && content.charAt(actualIntStart) == '0') {
201: actualIntStart++;
202: }
203:
204: // Find the ending position of the integer part
205: for (intEnd = actualIntStart; intEnd < len
206: && TypeValidator.isDigit(content.charAt(intEnd)); intEnd++)
207: ;
208:
209: // Not reached the end yet, error
210: if (intEnd < len)
211: throw new NumberFormatException();
212:
213: // no integer part, error.
214: if (intStart == intEnd)
215: throw new NumberFormatException();
216:
217: intDigits = intEnd - actualIntStart;
218: fracDigits = 0;
219: totalDigits = intDigits;
220:
221: if (intDigits > 0) {
222: ivalue = content.substring(actualIntStart, intEnd);
223: } else {
224: // "00", treat it as "0"
225: sign = 0;
226: }
227:
228: integer = true;
229: }
230:
231: public boolean equals(Object val) {
232: if (val == this )
233: return true;
234:
235: if (!(val instanceof XDecimal))
236: return false;
237: XDecimal oval = (XDecimal) val;
238:
239: if (sign != oval.sign)
240: return false;
241: if (sign == 0)
242: return true;
243:
244: return intDigits == oval.intDigits
245: && fracDigits == oval.fracDigits
246: && ivalue.equals(oval.ivalue)
247: && fvalue.equals(oval.fvalue);
248: }
249:
250: public int compareTo(XDecimal val) {
251: if (sign != val.sign)
252: return sign > val.sign ? 1 : -1;
253: if (sign == 0)
254: return 0;
255: return sign * intComp(val);
256: }
257:
258: private int intComp(XDecimal val) {
259: if (intDigits != val.intDigits)
260: return intDigits > val.intDigits ? 1 : -1;
261: int ret = ivalue.compareTo(val.ivalue);
262: if (ret != 0)
263: return ret > 0 ? 1 : -1;
264: ;
265: ret = fvalue.compareTo(val.fvalue);
266: return ret == 0 ? 0 : (ret > 0 ? 1 : -1);
267: }
268:
269: private String canonical;
270:
271: public synchronized String toString() {
272: if (canonical == null) {
273: makeCanonical();
274: }
275: return canonical;
276: }
277:
278: private void makeCanonical() {
279: if (sign == 0) {
280: if (integer)
281: canonical = "0";
282: else
283: canonical = "0.0";
284: return;
285: }
286: if (integer && sign > 0) {
287: canonical = ivalue;
288: return;
289: }
290: // for -0.1, total digits is 1, so we need 3 extra spots
291: StringBuffer buffer = new StringBuffer(totalDigits + 3);
292: if (sign == -1)
293: buffer.append('-');
294: if (intDigits != 0)
295: buffer.append(ivalue);
296: else
297: buffer.append('0');
298: if (!integer) {
299: buffer.append('.');
300: if (fracDigits != 0) {
301: buffer.append(fvalue);
302: } else {
303: buffer.append('0');
304: }
305: }
306: canonical = buffer.toString();
307: }
308:
309: public BigDecimal getBigDecimal() {
310: if (sign == 0) {
311: return new BigDecimal(BigInteger.ZERO);
312: }
313: return new BigDecimal(toString());
314: }
315:
316: public BigInteger getBigInteger() throws NumberFormatException {
317: if (fracDigits != 0) {
318: throw new NumberFormatException();
319: }
320: if (sign == 0) {
321: return BigInteger.ZERO;
322: }
323: if (sign == 1) {
324: return new BigInteger(ivalue);
325: }
326: return new BigInteger("-" + ivalue);
327: }
328:
329: public long getLong() throws NumberFormatException {
330: if (fracDigits != 0) {
331: throw new NumberFormatException();
332: }
333: if (sign == 0) {
334: return 0L;
335: }
336: if (sign == 1) {
337: return Long.parseLong(ivalue);
338: }
339: return Long.parseLong("-" + ivalue);
340: }
341:
342: public int getInt() throws NumberFormatException {
343: if (fracDigits != 0) {
344: throw new NumberFormatException();
345: }
346: if (sign == 0) {
347: return 0;
348: }
349: if (sign == 1) {
350: return Integer.parseInt(ivalue);
351: }
352: return Integer.parseInt("-" + ivalue);
353: }
354:
355: public short getShort() throws NumberFormatException {
356: if (fracDigits != 0) {
357: throw new NumberFormatException();
358: }
359: if (sign == 0) {
360: return 0;
361: }
362: if (sign == 1) {
363: return Short.parseShort(ivalue);
364: }
365: return Short.parseShort("-" + ivalue);
366: }
367:
368: public byte getByte() throws NumberFormatException {
369: if (fracDigits != 0) {
370: throw new NumberFormatException();
371: }
372: if (sign == 0) {
373: return 0;
374: }
375: if (sign == 1) {
376: return Byte.parseByte(ivalue);
377: }
378: return Byte.parseByte("-" + ivalue);
379: }
380: }
381: } // class DecimalDV
|