001:/*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 1999, 2000 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation (http://www.apache.org/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Xerces" and "Apache Software Foundation" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation and was
052: * originally based on software copyright (c) 1999, International
053: * Business Machines, Inc., http://www.apache.org. For more
054: * information on the Apache Software Foundation, please see
055: * <http://www.apache.org/>.
056: */
057:
058:package org.apache.xerces.validators.datatype;
059:
060:import java.math.BigDecimal;
061:import java.util.Enumeration;
062:import java.util.Hashtable;
063:import java.util.Vector;
064:import java.io.IOException;
065:import org.apache.xerces.validators.schema.SchemaSymbols;
066:import org.apache.xerces.utils.regex.RegularExpression;
067:
068:/**
069: *
070: * DecimalDatatypeValidator validates that content satisfies the W3C XML Datatype for decimal
071: *
072: * @author Elena Litani
073: * @author Ted Leung
074: * @author Jeffrey Rodriguez
075: * @author Mark Swinkles - List Validation refactoring
076: * @version $Id: DecimalDatatypeValidator.java,v 1.26 2001/06/08 20:55:22 sandygao Exp $
077: */
078:
079:public class DecimalDatatypeValidator extends AbstractNumericValidator {
080:
081: protected int fTotalDigits;
082: protected int fFractionDigits;
083:
084: public DecimalDatatypeValidator () throws InvalidDatatypeFacetException {
085: this ( null, null, false ); // Native, No Facets defined, Restriction
086: }
087:
088: public DecimalDatatypeValidator ( DatatypeValidator base, Hashtable facets,
089: boolean derivedByList ) throws InvalidDatatypeFacetException {
090: super (base, facets, derivedByList);
091: }
092:
093: public int compare( String value1, String value2) {
094: try {
095: BigDecimal d1 = new BigDecimal(stripPlusIfPresent(value1));
096: BigDecimal d2 = new BigDecimal(stripPlusIfPresent(value2));
097: return d1.compareTo(d2);
098: }
099: catch ( NumberFormatException e ) {
100: //REVISIT: should we throw exception??
101: return -1;
102: }
103: catch ( Exception e){
104: return -1;
105: }
106: }
107:
108: protected void inheritAdditionalFacets() {
109:
110: // inherit totalDigits
111: if ( (( ((DecimalDatatypeValidator)fBaseValidator).fFacetsDefined & DatatypeValidator.FACET_TOTALDIGITS) != 0) &&
112: !((fFacetsDefined & DatatypeValidator.FACET_TOTALDIGITS) != 0) ) {
113: fFacetsDefined |= FACET_TOTALDIGITS;
114: fTotalDigits = ((DecimalDatatypeValidator)fBaseValidator).fTotalDigits;
115: }
116: // inherit fractionDigits
117: if ( (( ((DecimalDatatypeValidator)fBaseValidator).fFacetsDefined & DatatypeValidator.FACET_FRACTIONDIGITS) != 0)
118: && !((fFacetsDefined & DatatypeValidator.FACET_FRACTIONDIGITS) != 0) ) {
119: fFacetsDefined |= FACET_FRACTIONDIGITS;
120: fFractionDigits = ((DecimalDatatypeValidator)fBaseValidator).fFractionDigits;
121: }
122: }
123:
124: protected void checkFacetConstraints() throws InvalidDatatypeFacetException{
125: // check 4.3.12.c1 must: fractionDigits <= totalDigits
126: if ( ((fFacetsDefined & DatatypeValidator.FACET_FRACTIONDIGITS) != 0) &&
127: ((fFacetsDefined & DatatypeValidator.FACET_TOTALDIGITS) != 0) ) {
128: if ( fFractionDigits > fTotalDigits )
129: throw new InvalidDatatypeFacetException( "fractionDigits value ='" + this .fFractionDigits + "'must be <= totalDigits value ='" +
130: this .fTotalDigits + "'. " );
131: }
132: }
133:
134: protected void checkBaseFacetConstraints() throws InvalidDatatypeFacetException{
135:
136: // check 4.3.11.c1 error: totalDigits > base.totalDigits
137: if ( ((fFacetsDefined & DatatypeValidator.FACET_TOTALDIGITS) != 0) ) {
138: if ( (( ((DecimalDatatypeValidator)fBaseValidator).fFacetsDefined & DatatypeValidator.FACET_TOTALDIGITS) != 0)){
139: if ((((DecimalDatatypeValidator)fBaseValidator).fFlags & DatatypeValidator.FACET_TOTALDIGITS) != 0 &&
140: fTotalDigits != ((DecimalDatatypeValidator)fBaseValidator).fTotalDigits){
141: throw new InvalidDatatypeFacetException("totalDigits value = '" + fTotalDigits +
142: "' must be equal to base.totalDigits value = '" +
143: ((DecimalDatatypeValidator)fBaseValidator).fTotalDigits +
144: "' with attribute {fixed} = true" );
145: }
146: if (fTotalDigits > ((DecimalDatatypeValidator)fBaseValidator).fTotalDigits ){
147: throw new InvalidDatatypeFacetException( "totalDigits value ='" + fTotalDigits + "' must be <= base.totalDigits value ='" +
148: ((DecimalDatatypeValidator)fBaseValidator).fTotalDigits + "'." );
149: }
150: }
151: }
152: // check fixed value for fractionDigits
153: if ( ((fFacetsDefined & DatatypeValidator.FACET_FRACTIONDIGITS) != 0) ) {
154: if ( (( ((DecimalDatatypeValidator)fBaseValidator).fFacetsDefined & DatatypeValidator.FACET_FRACTIONDIGITS) != 0)){
155: if ((((DecimalDatatypeValidator)fBaseValidator).fFlags & DatatypeValidator.FACET_FRACTIONDIGITS) != 0 &&
156: fFractionDigits != ((DecimalDatatypeValidator)fBaseValidator).fFractionDigits){
157: throw new InvalidDatatypeFacetException("fractionDigits value = '" + fFractionDigits +
158: "' must be equal to base.fractionDigits value = '" +
159: ((DecimalDatatypeValidator)fBaseValidator).fFractionDigits +
160: "' with attribute {fixed} = true" );
161: }
162: }
163: }
164: }
165:
166: protected void assignAdditionalFacets(String key, Hashtable facets ) throws InvalidDatatypeFacetException{
167:
168: String value = null;
169: try {
170: if ( key.equals(SchemaSymbols.ELT_TOTALDIGITS) ) {
171: value = ((String) facets.get(key ));
172: fFacetsDefined |= DatatypeValidator.FACET_TOTALDIGITS;
173: fTotalDigits = Integer.parseInt(value );
174: // check 4.3.11.c0 must: totalDigits > 0
175: if ( fTotalDigits <= 0 )
176: throw new InvalidDatatypeFacetException("totalDigits value '"+fTotalDigits+"' must be a positiveInteger.");
177: }
178: else if ( key.equals(SchemaSymbols.ELT_FRACTIONDIGITS) ) {
179: value = ((String) facets.get(key ));
180: fFacetsDefined |= DatatypeValidator.FACET_FRACTIONDIGITS;
181: fFractionDigits = Integer.parseInt( value );
182: // check 4.3.12.c0 must: fractionDigits > 0
183: if ( fFractionDigits < 0 )
184: throw new InvalidDatatypeFacetException("fractionDigits value '"+fFractionDigits+"' must be a positiveInteger.");
185: }
186: else {
187: throw new InvalidDatatypeFacetException( getErrorString( DatatypeMessageProvider.ILLEGAL_DECIMAL_FACET,
188: DatatypeMessageProvider.MSG_NONE, new Object [] { value, key}));
189: }
190: }
191: catch ( Exception ex ) {
192: throw new InvalidDatatypeFacetException( getErrorString( DatatypeMessageProvider.ILLEGAL_FACET_VALUE,
193: DatatypeMessageProvider.MSG_NONE, new Object [] { value, key}));
194: }
195: }
196:
197: protected int compareValues (Object value1, Object value2) {
198: return((BigDecimal)value1).compareTo((BigDecimal)value2);
199: }
200:
201: protected void setMaxInclusive (String value) {
202: fMaxInclusive = new BigDecimal(stripPlusIfPresent(value));
203: }
204: protected void setMinInclusive (String value) {
205: fMinInclusive = new BigDecimal(stripPlusIfPresent(value));
206:
207: }
208: protected void setMaxExclusive (String value) {
209: fMaxExclusive = new BigDecimal(stripPlusIfPresent(value));
210:
211: }
212: protected void setMinExclusive (String value) {
213: fMinExclusive = new BigDecimal(stripPlusIfPresent(value));
214:
215: }
216: protected void setEnumeration (Vector enumeration) throws InvalidDatatypeValueException{
217: if ( enumeration != null ) {
218: fEnumeration = new BigDecimal[enumeration.size()];
219: Object baseEnum=null;
220: try {
221:
222: for ( int i = 0; i < enumeration.size(); i++ ) {
223: fEnumeration[i] = new BigDecimal( stripPlusIfPresent(((String) enumeration.elementAt(i))));;
224: ((DecimalDatatypeValidator)fBaseValidator).validate((String)enumeration.elementAt(i), null);
225: }
226: }
227: catch ( Exception e ) {
228: throw new InvalidDatatypeValueException(e.getMessage());
229: }
230: }
231: }
232:
233:
234: protected String getMaxInclusive (boolean isBase) {
235: return(isBase)?(((DecimalDatatypeValidator)fBaseValidator).fMaxInclusive.toString())
236: :((BigDecimal)fMaxInclusive).toString();
237: }
238: protected String getMinInclusive (boolean isBase) {
239: return(isBase)?(((DecimalDatatypeValidator)fBaseValidator).fMinInclusive.toString())
240: :((BigDecimal)fMinInclusive).toString();
241: }
242: protected String getMaxExclusive (boolean isBase) {
243: return(isBase)?(((DecimalDatatypeValidator)fBaseValidator).fMaxExclusive.toString())
244: :((BigDecimal)fMaxExclusive).toString();
245: }
246: protected String getMinExclusive (boolean isBase) {
247: return(isBase)?(((DecimalDatatypeValidator)fBaseValidator).fMinExclusive.toString())
248: :((BigDecimal)fMinExclusive).toString();
249: }
250:
251:
252:
253: protected void checkContent(String content, Object state, Vector enumeration, boolean asBase)
254: throws InvalidDatatypeValueException {
255: // validate against parent type if any
256: if ( this .fBaseValidator != null ) {
257: // validate content as a base type
258: ((DecimalDatatypeValidator)fBaseValidator).checkContent(content, state, enumeration, true);
259: }
260:
261: // we check pattern first
262: if ( (fFacetsDefined & DatatypeValidator.FACET_PATTERN ) != 0 ) {
263: if ( fRegex == null || fRegex.matches( content) == false )
264: throw new InvalidDatatypeValueException("Value'"+content+
265: "' does not match regular expression facet " + fRegex.getPattern() );
266: }
267:
268: // if this is a base validator, we only need to check pattern facet
269: // all other facet were inherited by the derived type
270: if ( asBase )
271: return;
272:
273: BigDecimal d = null; // Is content a Decimal
274: try {
275: d = new BigDecimal( stripPlusIfPresent( content));
276: }
277: catch ( Exception nfe ) {
278: throw new InvalidDatatypeValueException( getErrorString(DatatypeMessageProvider.NOT_DECIMAL,
279: DatatypeMessageProvider.MSG_NONE,
280: new Object[] { "'" + content +"'"}));
281: }
282:
283: if ( enumeration != null ) { //the call was made from List or Union
284: int size= enumeration.size();
285: BigDecimal[] enumDecimal = new BigDecimal[size];
286: int i = 0;
287: try {
288: for ( ; i < size; i++ )
289: enumDecimal[i] = new BigDecimal( stripPlusIfPresent(((String) enumeration.elementAt(i))));
290: }
291: catch ( NumberFormatException nfe ) {
292: throw new InvalidDatatypeValueException( getErrorString(DatatypeMessageProvider.INVALID_ENUM_VALUE,
293: DatatypeMessageProvider.MSG_NONE,
294: new Object [] { enumeration.elementAt(i)}));
295: }
296: enumCheck(d, enumDecimal);
297: }
298:
299: if ( (fFacetsDefined & DatatypeValidator.FACET_FRACTIONDIGITS)!=0 ) {
300: if ( d.scale() > fFractionDigits )
301: throw new InvalidDatatypeValueException(
302: getErrorString(DatatypeMessageProvider.FRACTION_EXCEEDED,
303: DatatypeMessageProvider.MSG_NONE,
304: new Object[] { "'" + content + "'" + " with fractionDigits = '"+ d.scale() +"'"
305: , "'" + fFractionDigits + "'"}));
306: }
307: if ( (fFacetsDefined & DatatypeValidator.FACET_TOTALDIGITS)!=0 ) {
308: int totalDigits = d.movePointRight(d.scale()).toString().length() -
309: ((d.signum() < 0) ? 1 : 0); // account for minus sign
310: if ( totalDigits > fTotalDigits )
311: throw new InvalidDatatypeValueException(
312: getErrorString(DatatypeMessageProvider.TOTALDIGITS_EXCEEDED,
313: DatatypeMessageProvider.MSG_NONE,
314: new Object[] { "'" + content + "'" + " with totalDigits = '"+ totalDigits +"'"
315: , "'" + fTotalDigits + "'"} ));
316: }
317: boundsCheck(d);
318: if ( fEnumeration != null )
319: enumCheck(d, (BigDecimal[]) fEnumeration);
320:
321: return;
322:
323: }
324:
325: /**
326: * This class deals with a bug in BigDecimal class
327: * present up to version 1.1.2. 1.1.3 knows how
328: * to deal with the + sign.
329: *
330: * This method strips the first '+' if it found
331: * alone such as.
332: * +33434.344
333: *
334: * If we find +- then nothing happens we just
335: * return the string passed
336: *
337: * @param value
338: * @return
339: */
340: static private String stripPlusIfPresent( String value ) {
341: String strippedPlus = value;
342:
343: if ( value.length() >= 2 && value.charAt(0) == '+' && value.charAt(1) != '-' ) {
344: strippedPlus = value.substring(1);
345: }
346: return strippedPlus;
347: }
348:
349: private void enumCheck(BigDecimal v, BigDecimal[] enum) throws InvalidDatatypeValueException {
350: for ( int i = 0; i < enum.length; i++ ) {
351: if ( v.equals(enum[i] ) ) {
352: return;
353: }
354: }
355: throw new InvalidDatatypeValueException(
356: getErrorString(DatatypeMessageProvider.NOT_ENUM_VALUE,
357: DatatypeMessageProvider.MSG_NONE,
358: new Object [] { v}));
359: }
360:
361:}
|