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.util.Hashtable;
061:import java.util.Vector;
062:import java.util.Enumeration;
063:import java.util.StringTokenizer;
064:import java.util.NoSuchElementException;
065:import org.apache.xerces.validators.schema.SchemaSymbols;
066:import org.apache.xerces.utils.regex.RegularExpression;
067:
068:/**
069: * StringValidator validates that XML content is a W3C string type.
070: * @author Elena Litani
071: * @author Jeffrey Rodriguez
072: * @author Mark Swinkles - List Validation refactoring
073: * @version $Id: ListDatatypeValidator.java,v 1.11 2001/06/04 23:53:07 elena Exp $
074: */
075:public class ListDatatypeValidator extends AbstractDatatypeValidator{
076:
077: private int fLength = 0;
078: private int fMaxLength = Integer.MAX_VALUE;
079: private int fMinLength = 0;
080: private Vector fEnumeration = null;
081:
082: public ListDatatypeValidator () throws InvalidDatatypeFacetException{
083: this ( null, null, false ); // Native, No Facets defined, Restriction
084:
085: }
086:
087: public ListDatatypeValidator ( DatatypeValidator base, Hashtable facets,
088: boolean derivedByList ) throws InvalidDatatypeFacetException {
089:
090: fBaseValidator = base; // Set base type
091:
092: if ( facets != null ){
093: for (Enumeration e = facets.keys(); e.hasMoreElements();) {
094: String key = (String) e.nextElement();
095: if ( key.equals(SchemaSymbols.ELT_LENGTH) ) {
096: fFacetsDefined |= DatatypeValidator.FACET_LENGTH;
097: String lengthValue = (String)facets.get(key);
098: try {
099: fLength = Integer.parseInt( lengthValue );
100: } catch (NumberFormatException nfe) {
101: throw new InvalidDatatypeFacetException("Length value '"+lengthValue+"' is invalid.");
102: }
103: if ( fLength < 0 )
104: throw new InvalidDatatypeFacetException("Length value '"+lengthValue+"' must be a nonNegativeInteger.");
105:
106: }
107: else if (key.equals(SchemaSymbols.ELT_MINLENGTH) ) {
108: fFacetsDefined |= DatatypeValidator.FACET_MINLENGTH;
109: String minLengthValue = (String)facets.get(key);
110: try {
111: fMinLength = Integer.parseInt( minLengthValue );
112: } catch (NumberFormatException nfe) {
113: throw new InvalidDatatypeFacetException("maxLength value '"+minLengthValue+"' is invalid.");
114: }
115: }
116: else if (key.equals(SchemaSymbols.ELT_MAXLENGTH) ) {
117: fFacetsDefined |= DatatypeValidator.FACET_MAXLENGTH;
118: String maxLengthValue = (String)facets.get(key);
119: try {
120: fMaxLength = Integer.parseInt( maxLengthValue );
121: } catch (NumberFormatException nfe) {
122: throw new InvalidDatatypeFacetException("maxLength value '"+maxLengthValue+"' is invalid.");
123: }
124: }
125: else if (key.equals(SchemaSymbols.ELT_ENUMERATION)) {
126: fFacetsDefined |= DatatypeValidator.FACET_ENUMERATION;
127: fEnumeration = (Vector)facets.get(key);
128: }
129: else if ( key.equals(SchemaSymbols.ELT_PATTERN) ) {
130: fFacetsDefined |= DatatypeValidator.FACET_PATTERN;
131: fPattern = (String)facets.get(key);
132: if ( fPattern != null )
133: fRegex = new RegularExpression(fPattern, "X");
134: }
135: else {
136: throw new InvalidDatatypeFacetException( getErrorString(DatatypeMessageProvider.ILLEGAL_LIST_FACET,
137: DatatypeMessageProvider.MSG_NONE, new Object[] { key }));
138: }
139: }
140: if (((fFacetsDefined & DatatypeValidator.FACET_LENGTH ) != 0 ) ) {
141: if (((fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH ) != 0 ) ) {
142: throw new InvalidDatatypeFacetException(
143: "It is an error for both length and maxLength to be members of facets." );
144: } else if (((fFacetsDefined & DatatypeValidator.FACET_MINLENGTH ) != 0 ) ) {
145: throw new InvalidDatatypeFacetException(
146: "It is an error for both length and minLength to be members of facets." );
147: }
148: }
149:
150: if ( ( (fFacetsDefined & ( DatatypeValidator.FACET_MINLENGTH |
151: DatatypeValidator.FACET_MAXLENGTH) ) != 0 ) ) {
152: if ( fMinLength > fMaxLength ) {
153: throw new InvalidDatatypeFacetException( "Value of minLength = " + fMinLength +
154: "must be greater that the value of maxLength" + fMaxLength );
155: }
156: }
157:
158: // check 4.3.5.c0 must: enumeration values from the value space of base
159: //REVISIT: we should try either to delay it till validate() or
160: // store enumeration values in _value_space
161: // otherwise we end up creating and throwing objects
162: if ( base != null &&
163: (fFacetsDefined & DatatypeValidator.FACET_ENUMERATION) != 0 &&
164: (fEnumeration != null) ) {
165: int i = 0;
166: try {
167: for (; i < fEnumeration.size(); i++) {
168: base.validate ((String)fEnumeration.elementAt(i), null);
169: }
170: } catch ( Exception idve ){
171: throw new InvalidDatatypeFacetException( "Value of enumeration = '" + fEnumeration.elementAt(i) +
172: "' must be from the value space of base.");
173: }
174: }
175: }// End of Facets Setting
176: }
177:
178:
179:
180:
181: /**
182: * validate that a string is a W3C string type
183: *
184: * @param content A string containing the content to be validated
185: * @param list
186: * @exception throws InvalidDatatypeException if the content is
187: * not a W3C string type
188: * @exception InvalidDatatypeValueException
189: */
190: public Object validate(String content, Object state) throws InvalidDatatypeValueException
191: {
192: if ( content == null && state != null ) {
193: this .fBaseValidator.validate( content, state );//Passthrough setup information
194: //for state validators
195: } else {
196: checkContentEnum( content, state , null);
197: }
198: return null;
199: }
200:
201:
202: /**
203: *
204: * @return A Hashtable containing the facets
205: * for this datatype.
206: */
207: public Hashtable getFacets(){
208: return null;
209: }
210:
211: public int compare( String value1, String value2 ){
212: if (fBaseValidator instanceof ListDatatypeValidator) { //derived by restriction
213: return ((ListDatatypeValidator)this .fBaseValidator).compare( value1, value2 );
214: }
215: // <list> datatype
216: StringTokenizer pList1 = new StringTokenizer( value1 );
217: StringTokenizer pList2 = new StringTokenizer( value2 );
218: int numberOfTokens = pList1.countTokens();
219: if (numberOfTokens < pList2.countTokens()) {
220: return -1;
221: }
222: else if (numberOfTokens > pList2.countTokens()) {
223: return 1;
224: }
225: else { //compare each token
226: int i=0;
227: while(i++<numberOfTokens) {
228: if ( this .fBaseValidator != null ) {
229: int returnValue = this .fBaseValidator.compare( pList1.nextToken(), pList2.nextToken());
230: if (returnValue!=0) {
231: return returnValue; //REVISIT: does it make sense to return -1 or +1..?
232: }
233: }
234:
235: }
236: return 0;
237: }
238: }
239:
240: /**
241: * Returns a copy of this object.
242: */
243: public Object clone() throws CloneNotSupportedException {
244: ListDatatypeValidator newObj = null;
245: try {
246: newObj = new ListDatatypeValidator();
247:
248: newObj.fLocale = this .fLocale;
249: newObj.fBaseValidator = this .fBaseValidator;
250: newObj.fLength = this .fLength;
251: newObj.fMaxLength = this .fMaxLength;
252: newObj.fMinLength = this .fMinLength;
253: newObj.fPattern = this .fPattern;
254: newObj.fEnumeration = this .fEnumeration;
255: newObj.fFacetsDefined = this .fFacetsDefined;
256: } catch ( InvalidDatatypeFacetException ex) {
257: ex.printStackTrace();
258: }
259: return newObj;
260: }
261:
262: /**
263: * validate that a content is valid against base datatype and facets (if any)
264: *
265: * @param content A string containing the content to be validated
266: * @param state used with IDREF(s) datatypes
267: * @param enumeration enumeration facet
268: * @exception throws InvalidDatatypeException if the content is not valid
269: * @exception InvalidDatatypeValueException
270: */
271: protected void checkContentEnum( String content, Object state, Vector enumeration )
272: throws InvalidDatatypeValueException {
273:
274: //REVISIT: attemt to make enumeration to be validated against value space.
275: //a redesign of Datatypes might help to reduce complexity of this validation
276:
277: StringTokenizer parsedList = new StringTokenizer( content );
278: int numberOfTokens = parsedList.countTokens();
279: if (fBaseValidator instanceof ListDatatypeValidator) {
280: //<simpleType name="fRestriction"><restriction base="fList">...</restriction></simpleType>
281: try {
282: if ( (fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0 ) {
283: if ( numberOfTokens > fMaxLength ) {
284: throw new InvalidDatatypeValueException("Value '"+content+
285: "' with length ='"+ numberOfTokens + "' tokens"+
286: "' exceeds maximum length facet of '"+fMaxLength+"' tokens.");
287: }
288: }
289: if ( (fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0 ) {
290: if ( numberOfTokens < fMinLength ) {
291: throw new InvalidDatatypeValueException("Value '"+content+
292: "' with length ='"+ numberOfTokens+ "' tokens" +
293: "' is less than minimum length facet of '"+fMinLength+"' tokens." );
294: }
295: }
296:
297: if ( (fFacetsDefined & DatatypeValidator.FACET_LENGTH) != 0 ) {
298: if ( numberOfTokens != fLength ) {
299: throw new InvalidDatatypeValueException("Value '"+content+
300: "' with length ='"+ numberOfTokens+ "' tokens" +
301: "' is not equal to length facet of '"+fLength+"' tokens.");
302: }
303: }
304: if (enumeration!=null) {
305: if (!verifyEnum(enumeration)) {
306: throw new InvalidDatatypeValueException(
307: getErrorString(DatatypeMessageProvider.NOT_ENUM_VALUE,
308: DatatypeMessageProvider.MSG_NONE,
309: new Object [] { enumeration}));
310: }
311: }else {
312: enumeration = (fEnumeration!=null) ? fEnumeration : null;
313: }
314:
315: // enumeration must be passed till we know what "itemType" is
316: // to be able to validate against value space
317: ((ListDatatypeValidator)this .fBaseValidator).checkContentEnum( content, state, enumeration );
318: } catch ( NoSuchElementException e ) {
319: e.printStackTrace();
320: }
321: }
322: else {
323: //the case:
324: //<simpleType name="fList"><list itemType="float"/></simpleType>
325:
326: if (enumeration !=null) {
327: StringTokenizer eTokens = null; //temporary list of enumeration tokens
328: StringTokenizer cTokens = null; //list of content tokens
329: String token= null; //content token
330: String eToken= null; //enumeration token
331: boolean valid = true;
332:
333: int eSize = enumeration.size();
334: Vector enumTemp = new Vector(); //temporary vector to store enumeration token
335: enumTemp.setSize(1);
336: String currentEnumeration = null; //enum value: <enumeration value="1 2 3"/>
337:
338: for (int i=0;i<eSize;i++) { //loop through each enumeration
339: currentEnumeration = (String)enumeration.elementAt(i);
340: eTokens = new StringTokenizer (currentEnumeration);
341: valid = true;
342:
343: cTokens = (i==0)?parsedList:new StringTokenizer( content );
344:
345: if (numberOfTokens == eTokens.countTokens()) {
346: try {
347: //try string comparison first
348: if (currentEnumeration.equals(content)) {
349: for (int k=0; k<numberOfTokens;k++) { //validate against base type each token
350: if ( this .fBaseValidator != null ) {
351: this .fBaseValidator.validate( cTokens.nextToken(), state );
352: }
353: }
354: } else { //content="1.0 2" ; enumeration = "1 2"
355: for (int j=0;j<numberOfTokens;j++) {
356: token = cTokens.nextToken();
357: eToken = eTokens.nextToken();
358: enumTemp.setElementAt(eToken,0);
359: //REVISIT: date/time enumeration support
360: if (fBaseValidator instanceof AbstractNumericValidator) {
361: ((AbstractNumericValidator)fBaseValidator).checkContentEnum(token, state, enumTemp);
362: } else {
363: if (!token.equals(eToken)) { //validate enumeration for all other types
364: throw new InvalidDatatypeValueException("Value '"+content+ "' must be one of "+enumeration);
365: }
366: this .fBaseValidator.validate( token, state );
367: }
368: }
369: }
370: } catch (InvalidDatatypeValueException e) {
371: valid = false;
372: }
373: } else //numOfTokens in enumeration != numOfTokens in content
374: valid = false;
375: if (valid) break;
376: } //end for loop
377: if (!valid) {
378: throw new InvalidDatatypeValueException("Value '"+content+ "' does not match list type");
379: }
380: }//enumeration != null
381: else { //no enumeration
382: for (int k=0; k<numberOfTokens;k++) {
383: if ( this .fBaseValidator != null ) {//validate against parent type if any
384: this .fBaseValidator.validate( parsedList.nextToken(), state );
385: }
386: }
387: }
388:
389: } //end native list type
390:
391: } //end checkContentEnum
392:
393:
394:
395: // Private methods
396: /**
397: * check if enum is subset of fEnumeration
398: * enum 1: <enumeration value="1 2"/>
399: * enum 2: <enumeration value="1.0 2"/>
400: *
401: * @param enumeration facet
402: *
403: * @returns true if enumeration is subset of fEnumeration, false otherwise
404: */
405: private boolean verifyEnum (Vector enum){
406:
407: /* REVISIT: won't work for list datatypes in some cases: */
408: if ((fFacetsDefined & DatatypeValidator.FACET_ENUMERATION ) != 0) {
409: for (Enumeration e = enum.elements() ; e.hasMoreElements() ;) {
410: if (fEnumeration.contains(e.nextElement()) == false) {
411: return false;
412: }
413: }
414: }
415: return true;
416: }
417:
418:
419:}
|