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: import org.apache.xerces.xs.datatypes.XSDouble;
023:
024: /**
025: * Represent the schema type "double"
026: *
027: * @xerces.internal
028: *
029: * @author Neeraj Bajaj, Sun Microsystems, inc.
030: * @author Sandy Gao, IBM
031: *
032: * @version $Id: DoubleDV.java 572095 2007-09-02 18:32:43Z mrglavas $
033: */
034: public class DoubleDV extends TypeValidator {
035:
036: public short getAllowedFacets() {
037: return (XSSimpleTypeDecl.FACET_PATTERN
038: | XSSimpleTypeDecl.FACET_WHITESPACE
039: | XSSimpleTypeDecl.FACET_ENUMERATION
040: | XSSimpleTypeDecl.FACET_MAXINCLUSIVE
041: | XSSimpleTypeDecl.FACET_MININCLUSIVE
042: | XSSimpleTypeDecl.FACET_MAXEXCLUSIVE | XSSimpleTypeDecl.FACET_MINEXCLUSIVE);
043: }//getAllowedFacets()
044:
045: //convert a String to Double form, we have to take care of cases specified in spec like INF, -INF and NaN
046: public Object getActualValue(String content,
047: ValidationContext context)
048: throws InvalidDatatypeValueException {
049: try {
050: return new XDouble(content);
051: } catch (NumberFormatException ex) {
052: throw new InvalidDatatypeValueException(
053: "cvc-datatype-valid.1.2.1", new Object[] { content,
054: "double" });
055: }
056: }//getActualValue()
057:
058: // Can't call Double#compareTo method, because it's introduced in jdk 1.2
059: public int compare(Object value1, Object value2) {
060: return ((XDouble) value1).compareTo((XDouble) value2);
061: }//compare()
062:
063: //distinguishes between identity and equality for double datatype
064: //0.0 is equal but not identical to -0.0
065: public boolean isIdentical(Object value1, Object value2) {
066: if (value2 instanceof XDouble) {
067: return ((XDouble) value1).isIdentical((XDouble) value2);
068: }
069: return false;
070: }//isIdentical()
071:
072: /**
073: * Returns true if it's possible that the given
074: * string represents a valid floating point value
075: * (excluding NaN, INF and -INF).
076: */
077: static boolean isPossibleFP(String val) {
078: final int length = val.length();
079: for (int i = 0; i < length; ++i) {
080: char c = val.charAt(i);
081: if (!(c >= '0' && c <= '9' || c == '.' || c == '-'
082: || c == '+' || c == 'E' || c == 'e')) {
083: return false;
084: }
085: }
086: return true;
087: }
088:
089: private static final class XDouble implements XSDouble {
090: private final double value;
091:
092: public XDouble(String s) throws NumberFormatException {
093: if (isPossibleFP(s)) {
094: value = Double.parseDouble(s);
095: } else if (s.equals("INF")) {
096: value = Double.POSITIVE_INFINITY;
097: } else if (s.equals("-INF")) {
098: value = Double.NEGATIVE_INFINITY;
099: } else if (s.equals("NaN")) {
100: value = Double.NaN;
101: } else {
102: throw new NumberFormatException(s);
103: }
104: }
105:
106: public boolean equals(Object val) {
107: if (val == this )
108: return true;
109:
110: if (!(val instanceof XDouble))
111: return false;
112: XDouble oval = (XDouble) val;
113:
114: // NOTE: we don't distinguish 0.0 from -0.0
115: if (value == oval.value)
116: return true;
117:
118: if (value != value && oval.value != oval.value)
119: return true;
120:
121: return false;
122: }
123:
124: public int hashCode() {
125: // This check is necessary because doubleToLongBits(+0) != doubleToLongBits(-0)
126: if (value == 0d) {
127: return 0;
128: }
129: long v = Double.doubleToLongBits(value);
130: return (int) (v ^ (v >>> 32));
131: }
132:
133: // NOTE: 0.0 is equal but not identical to -0.0
134: public boolean isIdentical(XDouble val) {
135: if (val == this ) {
136: return true;
137: }
138:
139: if (value == val.value) {
140: return (value != 0.0d || (Double
141: .doubleToLongBits(value) == Double
142: .doubleToLongBits(val.value)));
143: }
144:
145: if (value != value && val.value != val.value)
146: return true;
147:
148: return false;
149: }
150:
151: private int compareTo(XDouble val) {
152: double oval = val.value;
153:
154: // this < other
155: if (value < oval)
156: return -1;
157: // this > other
158: if (value > oval)
159: return 1;
160: // this == other
161: // NOTE: we don't distinguish 0.0 from -0.0
162: if (value == oval)
163: return 0;
164:
165: // one of the 2 values or both is/are NaN(s)
166:
167: if (value != value) {
168: // this = NaN = other
169: if (oval != oval)
170: return 0;
171: // this is NaN <> other
172: return INDETERMINATE;
173: }
174:
175: // other is NaN <> this
176: return INDETERMINATE;
177: }
178:
179: private String canonical;
180:
181: public synchronized String toString() {
182: if (canonical == null) {
183: if (value == Double.POSITIVE_INFINITY)
184: canonical = "INF";
185: else if (value == Double.NEGATIVE_INFINITY)
186: canonical = "-INF";
187: else if (value != value)
188: canonical = "NaN";
189: // NOTE: we don't distinguish 0.0 from -0.0
190: else if (value == 0)
191: canonical = "0.0E1";
192: else {
193: // REVISIT: use the java algorithm for now, because we
194: // don't know what to output for 1.1d (which is no
195: // actually 1.1)
196: canonical = Double.toString(value);
197: // if it contains 'E', then it should be a valid schema
198: // canonical representation
199: if (canonical.indexOf('E') == -1) {
200: int len = canonical.length();
201: // at most 3 longer: E, -, 9
202: char[] chars = new char[len + 3];
203: canonical.getChars(0, len, chars, 0);
204: // expected decimal point position
205: int edp = chars[0] == '-' ? 2 : 1;
206: // for non-zero integer part
207: if (value >= 1 || value <= -1) {
208: // decimal point position
209: int dp = canonical.indexOf('.');
210: // move the digits: ddd.d --> d.ddd
211: for (int i = dp; i > edp; i--) {
212: chars[i] = chars[i - 1];
213: }
214: chars[edp] = '.';
215: // trim trailing zeros: d00.0 --> d.000 --> d.
216: while (chars[len - 1] == '0')
217: len--;
218: // add the last zero if necessary: d. --> d.0
219: if (chars[len - 1] == '.')
220: len++;
221: // append E: d.dd --> d.ddE
222: chars[len++] = 'E';
223: // how far we shifted the decimal point
224: int shift = dp - edp;
225: // append the exponent --> d.ddEd
226: // the exponent is at most 7
227: chars[len++] = (char) (shift + '0');
228: } else {
229: // non-zero digit point
230: int nzp = edp + 1;
231: // skip zeros: 0.003
232: while (chars[nzp] == '0')
233: nzp++;
234: // put the first non-zero digit to the left of '.'
235: chars[edp - 1] = chars[nzp];
236: chars[edp] = '.';
237: // move other digits (non-zero) to the right of '.'
238: for (int i = nzp + 1, j = edp + 1; i < len; i++, j++)
239: chars[j] = chars[i];
240: // adjust the length
241: len -= nzp - edp;
242: // append 0 if nessary: 0.03 --> 3. --> 3.0
243: if (len == edp + 1)
244: chars[len++] = '0';
245: // append E-: d.dd --> d.ddE-
246: chars[len++] = 'E';
247: chars[len++] = '-';
248: // how far we shifted the decimal point
249: int shift = nzp - edp;
250: // append the exponent --> d.ddEd
251: // the exponent is at most 3
252: chars[len++] = (char) (shift + '0');
253: }
254: canonical = new String(chars, 0, len);
255: }
256: }
257: }
258: return canonical;
259: }
260:
261: public double getValue() {
262: return value;
263: }
264: }
265: } // class DoubleDV
|