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 org.apache.xerces.impl.dv.InvalidDatatypeValueException;
021: import org.apache.xerces.impl.dv.ValidationContext;
022:
023: /**
024: * Validator for <precisionDecimal> datatype (W3C Schema 1.1)
025: *
026: * @xerces.experimental
027: *
028: * @author Ankit Pasricha, IBM
029: *
030: * @version $Id: PrecisionDecimalDV.java 446745 2006-09-15 21:43:58Z mrglavas $
031: */
032: class PrecisionDecimalDV extends TypeValidator {
033:
034: static class XPrecisionDecimal {
035:
036: // sign: 0 for absent; 1 for positive values; -1 for negative values (except in case of INF, -INF)
037: int sign = 1;
038: // total digits. >= 1
039: int totalDigits = 0;
040: // integer digits when sign != 0
041: int intDigits = 0;
042: // fraction digits when sign != 0
043: int fracDigits = 0;
044: //precision
045: //int precision = 0;
046: // the string representing the integer part
047: String ivalue = "";
048: // the string representing the fraction part
049: String fvalue = "";
050:
051: int pvalue = 0;
052:
053: XPrecisionDecimal(String content) throws NumberFormatException {
054: if (content.equals("NaN")) {
055: ivalue = content;
056: sign = 0;
057: }
058: if (content.equals("+INF") || content.equals("INF")
059: || content.equals("-INF")) {
060: ivalue = content.charAt(0) == '+' ? content
061: .substring(1) : content;
062: return;
063: }
064: initD(content);
065: }
066:
067: void initD(String content) throws NumberFormatException {
068: int len = content.length();
069: if (len == 0)
070: throw new NumberFormatException();
071:
072: // these 4 variables are used to indicate where the integre/fraction
073: // parts start/end.
074: int intStart = 0, intEnd = 0, fracStart = 0, fracEnd = 0;
075:
076: // Deal with leading sign symbol if present
077: if (content.charAt(0) == '+') {
078: // skip '+', so intStart should be 1
079: intStart = 1;
080: } else if (content.charAt(0) == '-') {
081: intStart = 1;
082: sign = -1;
083: }
084:
085: // skip leading zeroes in integer part
086: int actualIntStart = intStart;
087: while (actualIntStart < len
088: && content.charAt(actualIntStart) == '0') {
089: actualIntStart++;
090: }
091:
092: // Find the ending position of the integer part
093: for (intEnd = actualIntStart; intEnd < len
094: && TypeValidator.isDigit(content.charAt(intEnd)); intEnd++)
095: ;
096:
097: // Not reached the end yet
098: if (intEnd < len) {
099: // the remaining part is not ".DDD" or "EDDD" or "eDDD", error
100: if (content.charAt(intEnd) != '.'
101: && content.charAt(intEnd) != 'E'
102: && content.charAt(intEnd) != 'e')
103: throw new NumberFormatException();
104:
105: if (content.charAt(intEnd) == '.') {
106: // fraction part starts after '.', and ends at the end of the input
107: fracStart = intEnd + 1;
108:
109: // find location of E or e (if present)
110: // Find the ending position of the fracion part
111: for (fracEnd = fracStart; fracEnd < len
112: && TypeValidator.isDigit(content
113: .charAt(fracEnd)); fracEnd++)
114: ;
115: } else {
116: pvalue = Integer.parseInt(content.substring(
117: intEnd + 1, len));
118: }
119: }
120:
121: // no integer part, no fraction part, error.
122: if (intStart == intEnd && fracStart == fracEnd)
123: throw new NumberFormatException();
124:
125: // ignore trailing zeroes in fraction part
126: /*while (fracEnd > fracStart && content.charAt(fracEnd-1) == '0') {
127: fracEnd--;
128: }*/
129:
130: // check whether there is non-digit characters in the fraction part
131: for (int fracPos = fracStart; fracPos < fracEnd; fracPos++) {
132: if (!TypeValidator.isDigit(content.charAt(fracPos)))
133: throw new NumberFormatException();
134: }
135:
136: intDigits = intEnd - actualIntStart;
137: fracDigits = fracEnd - fracStart;
138:
139: if (intDigits > 0) {
140: ivalue = content.substring(actualIntStart, intEnd);
141: }
142:
143: if (fracDigits > 0) {
144: fvalue = content.substring(fracStart, fracEnd);
145: if (fracEnd < len) {
146: pvalue = Integer.parseInt(content.substring(
147: fracEnd + 1, len));
148: }
149: }
150: totalDigits = intDigits + fracDigits;
151: }
152:
153: public boolean equals(Object val) {
154: if (val == this )
155: return true;
156:
157: if (!(val instanceof XPrecisionDecimal))
158: return false;
159: XPrecisionDecimal oval = (XPrecisionDecimal) val;
160:
161: return this .compareTo(oval) == EQUAL;
162: }
163:
164: /**
165: * @return
166: */
167: private int compareFractionalPart(XPrecisionDecimal oval) {
168: if (fvalue.equals(oval.fvalue))
169: return EQUAL;
170:
171: StringBuffer temp1 = new StringBuffer(fvalue);
172: StringBuffer temp2 = new StringBuffer(oval.fvalue);
173:
174: truncateTrailingZeros(temp1, temp2);
175: return temp1.toString().compareTo(temp2.toString());
176: }
177:
178: private void truncateTrailingZeros(StringBuffer fValue,
179: StringBuffer otherFValue) {
180: for (int i = fValue.length() - 1; i >= 0; i--)
181: if (fValue.charAt(i) == '0')
182: fValue.deleteCharAt(i);
183: else
184: break;
185:
186: for (int i = otherFValue.length() - 1; i >= 0; i--)
187: if (otherFValue.charAt(i) == '0')
188: otherFValue.deleteCharAt(i);
189: else
190: break;
191: }
192:
193: public int compareTo(XPrecisionDecimal val) {
194:
195: // seen NaN
196: if (sign == 0)
197: return INDETERMINATE;
198:
199: //INF is greater than everything and equal to itself
200: if (ivalue.equals("INF") || val.ivalue.equals("INF")) {
201: if (ivalue.equals(val.ivalue))
202: return EQUAL;
203: else if (ivalue.equals("INF"))
204: return GREATER_THAN;
205: return LESS_THAN;
206: }
207:
208: //-INF is smaller than everything and equal itself
209: if (ivalue.equals("-INF") || val.ivalue.equals("-INF")) {
210: if (ivalue.equals(val.ivalue))
211: return EQUAL;
212: else if (ivalue.equals("-INF"))
213: return LESS_THAN;
214: return GREATER_THAN;
215: }
216:
217: if (sign != val.sign)
218: return sign > val.sign ? GREATER_THAN : LESS_THAN;
219:
220: return sign * compare(val);
221: }
222:
223: // To enable comparison - the exponent part of the decimal will be limited
224: // to the max value of int.
225: private int compare(XPrecisionDecimal val) {
226:
227: if (pvalue != 0 || val.pvalue != 0) {
228: if (pvalue == val.pvalue)
229: return intComp(val);
230: else {
231:
232: if (intDigits + pvalue != val.intDigits
233: + val.pvalue)
234: return intDigits + pvalue > val.intDigits
235: + val.pvalue ? GREATER_THAN : LESS_THAN;
236:
237: //otherwise the 2 combined values are the same
238: if (pvalue > val.pvalue) {
239: int expDiff = pvalue - val.pvalue;
240: StringBuffer buffer = new StringBuffer(ivalue);
241: StringBuffer fbuffer = new StringBuffer(fvalue);
242: for (int i = 0; i < expDiff; i++) {
243: if (i < fracDigits) {
244: buffer.append(fvalue.charAt(i));
245: fbuffer.deleteCharAt(i);
246: } else
247: buffer.append('0');
248: }
249: return compareDecimal(buffer.toString(),
250: val.ivalue, fbuffer.toString(),
251: val.fvalue);
252: } else {
253: int expDiff = val.pvalue - pvalue;
254: StringBuffer buffer = new StringBuffer(
255: val.ivalue);
256: StringBuffer fbuffer = new StringBuffer(
257: val.fvalue);
258: for (int i = 0; i < expDiff; i++) {
259: if (i < val.fracDigits) {
260: buffer.append(val.fvalue.charAt(i));
261: fbuffer.deleteCharAt(i);
262: } else
263: buffer.append('0');
264: }
265: return compareDecimal(ivalue,
266: buffer.toString(), fvalue, fbuffer
267: .toString());
268: }
269: }
270: } else {
271: return intComp(val);
272: }
273: }
274:
275: /**
276: * @param val
277: * @return
278: */
279: private int intComp(XPrecisionDecimal val) {
280: if (intDigits != val.intDigits)
281: return intDigits > val.intDigits ? GREATER_THAN
282: : LESS_THAN;
283:
284: return compareDecimal(ivalue, val.ivalue, fvalue,
285: val.fvalue);
286: }
287:
288: /**
289: * @param val
290: * @return
291: */
292: private int compareDecimal(String iValue, String fValue,
293: String otherIValue, String otherFValue) {
294: int ret = iValue.compareTo(otherIValue);
295: if (ret != 0)
296: return ret > 0 ? GREATER_THAN : LESS_THAN;
297:
298: if (fValue.equals(otherFValue))
299: return EQUAL;
300:
301: StringBuffer temp1 = new StringBuffer(fValue);
302: StringBuffer temp2 = new StringBuffer(otherFValue);
303:
304: truncateTrailingZeros(temp1, temp2);
305: ret = temp1.toString().compareTo(temp2.toString());
306: return ret == 0 ? EQUAL : (ret > 0 ? GREATER_THAN
307: : LESS_THAN);
308: }
309:
310: private String canonical;
311:
312: public synchronized String toString() {
313: if (canonical == null) {
314: makeCanonical();
315: }
316: return canonical;
317: }
318:
319: private void makeCanonical() {
320: // REVISIT: to be determined by working group
321: canonical = "TBD by Working Group";
322: }
323:
324: /**
325: * @param decimal
326: * @return
327: */
328: public boolean isIdentical(XPrecisionDecimal decimal) {
329: if (ivalue.equals(decimal.ivalue)
330: && (ivalue.equals("INF") || ivalue.equals("-INF") || ivalue
331: .equals("NaN")))
332: return true;
333:
334: if (sign == decimal.sign && intDigits == decimal.intDigits
335: && fracDigits == decimal.fracDigits
336: && pvalue == decimal.pvalue
337: && ivalue.equals(decimal.ivalue)
338: && fvalue.equals(decimal.fvalue))
339: return true;
340: return false;
341: }
342:
343: }
344:
345: /* (non-Javadoc)
346: * @see org.apache.xerces.impl.dv.xs.TypeValidator#getAllowedFacets()
347: */
348: public short getAllowedFacets() {
349: return (XSSimpleTypeDecl.FACET_PATTERN
350: | XSSimpleTypeDecl.FACET_WHITESPACE
351: | XSSimpleTypeDecl.FACET_ENUMERATION
352: | XSSimpleTypeDecl.FACET_MAXINCLUSIVE
353: | XSSimpleTypeDecl.FACET_MININCLUSIVE
354: | XSSimpleTypeDecl.FACET_MAXEXCLUSIVE
355: | XSSimpleTypeDecl.FACET_MINEXCLUSIVE
356: | XSSimpleTypeDecl.FACET_TOTALDIGITS | XSSimpleTypeDecl.FACET_FRACTIONDIGITS);
357: }
358:
359: /* (non-Javadoc)
360: * @see org.apache.xerces.impl.dv.xs.TypeValidator#getActualValue(java.lang.String, org.apache.xerces.impl.dv.ValidationContext)
361: */
362: public Object getActualValue(String content,
363: ValidationContext context)
364: throws InvalidDatatypeValueException {
365: try {
366: return new XPrecisionDecimal(content);
367: } catch (NumberFormatException nfe) {
368: throw new InvalidDatatypeValueException(
369: "cvc-datatype-valid.1.2.1", new Object[] { content,
370: "precisionDecimal" });
371: }
372: }
373:
374: public int compare(Object value1, Object value2) {
375: return ((XPrecisionDecimal) value1)
376: .compareTo((XPrecisionDecimal) value2);
377: }
378:
379: public int getFractionDigits(Object value) {
380: return ((XPrecisionDecimal) value).fracDigits;
381: }
382:
383: public int getTotalDigits(Object value) {
384: return ((XPrecisionDecimal) value).totalDigits;
385: }
386:
387: public boolean isIdentical(Object value1, Object value2) {
388: if (!(value2 instanceof XPrecisionDecimal)
389: || !(value1 instanceof XPrecisionDecimal))
390: return false;
391: return ((XPrecisionDecimal) value1)
392: .isIdentical((XPrecisionDecimal) value2);
393: }
394: }
|