001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 1999, 2000, 2001 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) 2001, 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.Locale;
064: import java.text.Collator;
065: import org.apache.xerces.validators.schema.SchemaSymbols;
066: import org.apache.xerces.utils.regex.RegularExpression;
067: import org.apache.xerces.utils.XMLCharacterProperties;
068:
069: /**
070: * AbstractStringValidator is a base class for anyURI, string,
071: * hexBinary, base64Binary, QName and Notation datatypes.
072: *
073: * @author Elena Litani
074: * @version $Id: AbstractStringValidator.java,v 1.5.2.1 2001/09/12 21:04:50 sandygao Exp $
075: */
076: public abstract class AbstractStringValidator extends
077: AbstractDatatypeValidator {
078:
079: protected int fLength = 0;
080: protected int fMaxLength = Integer.MAX_VALUE;
081: protected int fMinLength = 0;
082: protected Vector fEnumeration = null;
083:
084: // _dummy_ facet for which special token parser to use
085: public static final String FACET_SPECIAL_TOKEN = "specialToken";
086: public static final String SPECIAL_TOKEN_NONE = "NONE";
087: public static final String SPECIAL_TOKEN_NMTOKEN = "NMTOKEN";
088: public static final String SPECIAL_TOKEN_NAME = "Name";
089: public static final String SPECIAL_TOKEN_IDNAME = "ID(Name)";
090: public static final String SPECIAL_TOKEN_IDREFNAME = "IDREF(Name)";
091: public static final String SPECIAL_TOKEN_NCNAME = "NCName";
092: public static final String SPECIAL_TOKEN_IDNCNAME = "ID(NCName)";
093: public static final String SPECIAL_TOKEN_IDREFNCNAME = "IDREF(NCName)";
094: public static final String SPECIAL_TOKEN_ENTITY = "ENTITY(NCName)";
095:
096: protected String fTokenType = SPECIAL_TOKEN_NONE; //flags special token parser
097:
098: public AbstractStringValidator()
099: throws InvalidDatatypeFacetException {
100: this (null, null, false); // Native, No Facets defined, Restriction
101:
102: }
103:
104: public AbstractStringValidator(DatatypeValidator base,
105: Hashtable facets, boolean derivedByList)
106: throws InvalidDatatypeFacetException {
107:
108: // Set base type
109: fBaseValidator = base;
110:
111: if (derivationList(derivedByList))
112: return;
113: // Set Facets if any defined
114: if (facets != null) {
115: for (Enumeration e = facets.keys(); e.hasMoreElements();) {
116: String key = (String) e.nextElement();
117:
118: if (key.equals(SchemaSymbols.ELT_LENGTH)) {
119: fFacetsDefined |= DatatypeValidator.FACET_LENGTH;
120: String lengthValue = (String) facets.get(key);
121: try {
122: fLength = Integer.parseInt(lengthValue);
123: } catch (NumberFormatException nfe) {
124: throw new InvalidDatatypeFacetException(
125: "Length value '" + lengthValue
126: + "' is invalid.");
127: }
128: // check 4.3.1.c0 must: length >= 0
129: if (fLength < 0)
130: throw new InvalidDatatypeFacetException(
131: "Length value '"
132: + lengthValue
133: + "' must be a nonNegativeInteger.");
134:
135: } else if (key.equals(SchemaSymbols.ELT_MINLENGTH)) {
136: fFacetsDefined |= DatatypeValidator.FACET_MINLENGTH;
137: String minLengthValue = (String) facets.get(key);
138: try {
139: fMinLength = Integer.parseInt(minLengthValue);
140: } catch (NumberFormatException nfe) {
141: throw new InvalidDatatypeFacetException(
142: "minLength value '" + minLengthValue
143: + "' is invalid.");
144: }
145: // check 4.3.2.c0 must: minLength >= 0
146: if (fMinLength < 0)
147: throw new InvalidDatatypeFacetException(
148: "minLength value '"
149: + minLengthValue
150: + "' must be a nonNegativeInteger.");
151:
152: } else if (key.equals(SchemaSymbols.ELT_MAXLENGTH)) {
153: fFacetsDefined |= DatatypeValidator.FACET_MAXLENGTH;
154: String maxLengthValue = (String) facets.get(key);
155: try {
156: fMaxLength = Integer.parseInt(maxLengthValue);
157: } catch (NumberFormatException nfe) {
158: throw new InvalidDatatypeFacetException(
159: "maxLength value '" + maxLengthValue
160: + "' is invalid.");
161: }
162: // check 4.3.3.c0 must: maxLength >= 0
163: if (fMaxLength < 0)
164: throw new InvalidDatatypeFacetException(
165: "maxLength value '"
166: + maxLengthValue
167: + "' must be a nonNegativeInteger.");
168: } else if (key.equals(SchemaSymbols.ELT_PATTERN)) {
169: fFacetsDefined |= DatatypeValidator.FACET_PATTERN;
170: fPattern = (String) facets.get(key);
171: if (fPattern != null)
172: fRegex = new RegularExpression(fPattern, "X");
173: } else if (key.equals(SchemaSymbols.ELT_ENUMERATION)) {
174: fEnumeration = (Vector) facets.get(key);
175: fFacetsDefined |= DatatypeValidator.FACET_ENUMERATION;
176: } else if (key.equals(DatatypeValidator.FACET_FIXED)) {// fixed flags
177: fFlags = ((Short) facets.get(key)).shortValue();
178: } else if (key == FACET_SPECIAL_TOKEN) {// special token parser
179: setTokenType((String) facets.get(key));
180: } else {
181: assignAdditionalFacets(key, facets);
182: }
183: }
184:
185: if (base != null) {
186: // check 4.3.5.c0 must: enumeration values from the value space of base
187: //REVISIT: we should try either to delay it till validate() or
188: // store enumeration values in _value_space
189: // otherwise we end up creating and throwing objects
190: if ((fFacetsDefined & DatatypeValidator.FACET_ENUMERATION) != 0
191: && (fEnumeration != null)) {
192: int i = 0;
193: try {
194: for (; i < fEnumeration.size(); i++) {
195: ((AbstractStringValidator) base)
196: .checkContent((String) fEnumeration
197: .elementAt(i), null, false);
198: }
199: } catch (Exception idve) {
200: throw new InvalidDatatypeFacetException(
201: "Value of enumeration = '"
202: + fEnumeration.elementAt(i)
203: + "' must be from the value space of base.");
204: }
205: }
206: }
207:
208: // check 4.3.1.c1 error: length & (maxLength | minLength)
209: if (((fFacetsDefined & DatatypeValidator.FACET_LENGTH) != 0)) {
210: if (((fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0)) {
211: throw new InvalidDatatypeFacetException(
212: "It is an error for both length and maxLength to be members of facets.");
213: } else if (((fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0)) {
214: throw new InvalidDatatypeFacetException(
215: "It is an error for both length and minLength to be members of facets.");
216: }
217: }
218:
219: // check 4.3.2.c1 must: minLength <= maxLength
220: if (((fFacetsDefined & (DatatypeValidator.FACET_MINLENGTH | DatatypeValidator.FACET_MAXLENGTH)) != 0)) {
221: if (fMinLength > fMaxLength) {
222: throw new InvalidDatatypeFacetException(
223: "Value of minLength = '"
224: + fMinLength
225: + "'must be <= the value of maxLength = '"
226: + fMaxLength + "'.");
227: }
228: }
229:
230: // if base type is string, check facets against base.facets, and inherit facets from base
231: if (base != null) {
232: AbstractStringValidator strBase = (AbstractStringValidator) base;
233:
234: // check 4.3.1.c1 error: length & (base.maxLength | base.minLength)
235: if (((fFacetsDefined & DatatypeValidator.FACET_LENGTH) != 0)) {
236: if (((strBase.fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0)) {
237: throw new InvalidDatatypeFacetException(
238: "It is an error for both length and maxLength to be members of facets.");
239: } else if (((strBase.fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0)) {
240: throw new InvalidDatatypeFacetException(
241: "It is an error for both length and minLength to be members of facets.");
242: } else if ((strBase.fFacetsDefined & DatatypeValidator.FACET_LENGTH) != 0) {
243: // check 4.3.1.c2 error: length != base.length
244: if (fLength != strBase.fLength)
245: throw new InvalidDatatypeFacetException(
246: "Value of length = '"
247: + fLength
248: + "' must be = the value of base.length = '"
249: + strBase.fLength + "'.");
250: }
251: }
252:
253: // check 4.3.1.c1 error: base.length & (maxLength | minLength)
254: if (((strBase.fFacetsDefined & DatatypeValidator.FACET_LENGTH) != 0)) {
255: if (((fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0)) {
256: throw new InvalidDatatypeFacetException(
257: "It is an error for both length and maxLength to be members of facets.");
258: } else if (((fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0)) {
259: throw new InvalidDatatypeFacetException(
260: "It is an error for both length and minLength to be members of facets.");
261: }
262: }
263:
264: // check 4.3.2.c1 must: minLength <= base.maxLength
265: if (((fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0)) {
266: if ((strBase.fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0) {
267: if (fMinLength > strBase.fMaxLength) {
268: throw new InvalidDatatypeFacetException(
269: "Value of minLength = '"
270: + fMinLength
271: + "'must be <= the value of maxLength = '"
272: + fMaxLength + "'.");
273: }
274: } else if ((strBase.fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0) {
275: if ((strBase.fFlags & DatatypeValidator.FACET_MINLENGTH) != 0
276: && fMinLength != strBase.fMinLength) {
277: throw new InvalidDatatypeFacetException(
278: "minLength value = '"
279: + fMinLength
280: + "' must be equal to base.minLength value = '"
281: + strBase.fMinLength
282: + "' with attribute {fixed} = true");
283: }
284: // check 4.3.2.c2 error: minLength < base.minLength
285: if (fMinLength < strBase.fMinLength) {
286: throw new InvalidDatatypeFacetException(
287: "Value of minLength = '"
288: + fMinLength
289: + "' must be >= the value of base.minLength = '"
290: + strBase.fMinLength + "'.");
291: }
292: }
293: }
294:
295: // check 4.3.2.c1 must: base.minLength <= maxLength
296: if (((strBase.fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0)
297: && ((fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0)) {
298: if (strBase.fMinLength > fMaxLength) {
299: throw new InvalidDatatypeFacetException(
300: "Value of minLength = '"
301: + fMinLength
302: + "'must be <= the value of maxLength = '"
303: + fMaxLength + "'.");
304: }
305: }
306:
307: // check 4.3.3.c1 error: maxLength > base.maxLength
308: if ((fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0) {
309: if ((strBase.fFlags & DatatypeValidator.FACET_MAXLENGTH) != 0
310: && fMaxLength != strBase.fMaxLength) {
311: throw new InvalidDatatypeFacetException(
312: "maxLength value = '"
313: + fMaxLength
314: + "' must be equal to base.maxLength value = '"
315: + strBase.fMaxLength
316: + "' with attribute {fixed} = true");
317: }
318: if ((strBase.fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0) {
319: if (fMaxLength > strBase.fMaxLength) {
320:
321: throw new InvalidDatatypeFacetException(
322: "Value of maxLength = '"
323: + fMaxLength
324: + "' must be <= the value of base.maxLength = '"
325: + strBase.fMaxLength + "'.");
326: }
327: }
328: }
329:
330: checkBaseFacetConstraints();
331:
332: // inherit length
333: if ((strBase.fFacetsDefined & DatatypeValidator.FACET_LENGTH) != 0) {
334: if ((fFacetsDefined & DatatypeValidator.FACET_LENGTH) == 0) {
335: fFacetsDefined |= DatatypeValidator.FACET_LENGTH;
336: fLength = strBase.fLength;
337: }
338: }
339: // inherit minLength
340: if ((strBase.fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0) {
341: if ((fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) == 0) {
342: fFacetsDefined |= DatatypeValidator.FACET_MINLENGTH;
343: fMinLength = strBase.fMinLength;
344: }
345: }
346: // inherit maxLength
347: if ((strBase.fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0) {
348: if ((fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) == 0) {
349: fFacetsDefined |= DatatypeValidator.FACET_MAXLENGTH;
350: fMaxLength = strBase.fMaxLength;
351: }
352: }
353: // inherit enumeration
354: if ((fFacetsDefined & DatatypeValidator.FACET_ENUMERATION) == 0
355: && (strBase.fFacetsDefined & DatatypeValidator.FACET_ENUMERATION) != 0) {
356: fFacetsDefined |= DatatypeValidator.FACET_ENUMERATION;
357: fEnumeration = strBase.fEnumeration;
358: }
359: inheritAdditionalFacets();
360:
361: //inherit fixed values
362: fFlags |= strBase.fFlags;
363: }
364: }// End of Facets Setting
365: }
366:
367: //
368: // string has whiteSpace facet
369: // all other datatypes will throw InvalidDatatypeFacetException
370: //
371: abstract protected void assignAdditionalFacets(String key,
372: Hashtable facets) throws InvalidDatatypeFacetException;
373:
374: //
375: // string has additional whiteSpace facet
376: //
377: protected void inheritAdditionalFacets() {
378: }
379:
380: //
381: // string needs to check constraints on whiteSpace
382: // check is done against fBaseValidator
383: //
384: protected void checkBaseFacetConstraints()
385: throws InvalidDatatypeFacetException {
386: }
387:
388: protected boolean derivationList(boolean derivedByList) {
389: // list types are handled by ListDatatypeValidator, we do nothing here.
390: return derivedByList;
391: }
392:
393: /**
394: * validate that a string is a W3C string type
395: *
396: * @param content A string containing the content to be validated
397: * @param list
398: * @exception throws InvalidDatatypeException if the content is
399: * not a W3C string type
400: * @exception InvalidDatatypeValueException
401: */
402: public Object validate(String content, Object state)
403: throws InvalidDatatypeValueException {
404: checkContent(content, state, false);
405: return null;
406: }
407:
408: private void checkContent(String content, Object state,
409: boolean asBase) throws InvalidDatatypeValueException {
410: // validate against parent type if any
411: if (this .fBaseValidator != null) {
412: // validate content as a base type
413: ((AbstractStringValidator) fBaseValidator).checkContent(
414: content, state, true);
415: }
416:
417: // we check pattern first
418: if ((fFacetsDefined & DatatypeValidator.FACET_PATTERN) != 0) {
419: if (fRegex == null || fRegex.matches(content) == false)
420: throw new InvalidDatatypeValueException("Value '"
421: + content
422: + "' does not match regular expression facet '"
423: + fPattern + "'.");
424: }
425:
426: // validate special kinds of token, in place of old pattern matching
427: if (fTokenType != SPECIAL_TOKEN_NONE) {
428: validateToken(fTokenType, content);
429: }
430:
431: // if this is a base validator, we only need to check pattern facet
432: // all other facet were inherited by the derived type
433: if (asBase)
434: return;
435:
436: checkValueSpace(content);
437: int length = getLength(content);
438:
439: if ((fFacetsDefined & DatatypeValidator.FACET_MAXLENGTH) != 0) {
440: if (length > fMaxLength) {
441: throw new InvalidDatatypeValueException("Value '"
442: + content + "' with length '" + length
443: + "' exceeds maximum length facet of '"
444: + fMaxLength + "'.");
445: }
446: }
447: if ((fFacetsDefined & DatatypeValidator.FACET_MINLENGTH) != 0) {
448: if (length < fMinLength) {
449: throw new InvalidDatatypeValueException("Value '"
450: + content + "' with length '" + length
451: + "' is less than minimum length facet of '"
452: + fMinLength + "'.");
453: }
454: }
455:
456: if ((fFacetsDefined & DatatypeValidator.FACET_LENGTH) != 0) {
457: if (length != fLength) {
458: throw new InvalidDatatypeValueException("Value '"
459: + content + "' with length '" + length
460: + "' is not equal to length facet '" + fLength
461: + "'.");
462: }
463: }
464:
465: if ((fFacetsDefined & DatatypeValidator.FACET_ENUMERATION) != 0
466: && (fEnumeration != null)) {
467: if (fEnumeration.contains(content) == false)
468: throw new InvalidDatatypeValueException("Value '"
469: + content + "' must be one of " + fEnumeration);
470: }
471: }
472:
473: protected int getLength(String content) {
474: return content.length();
475: }
476:
477: protected void checkValueSpace(String content)
478: throws InvalidDatatypeValueException {
479: }
480:
481: /**
482: * Returns a copy of this object.
483: *
484: * @return
485: * @exception CloneNotSupportedException
486: */
487: public Object clone() throws CloneNotSupportedException {
488: throw new CloneNotSupportedException(
489: "clone() is not supported in "
490: + this .getClass().getName());
491: }
492:
493: public void setTokenType(String tokenType) {
494: fTokenType = tokenType;
495: }
496:
497: protected static void validateToken(String tokenType, String content)
498: throws InvalidDatatypeValueException {
499: int len;
500: if (content == null || (len = content.length()) == 0) {
501: throw new InvalidDatatypeValueException(
502: "The length of the content must be greater than 0");
503: }
504: boolean seenErr = false;
505: if (tokenType == SPECIAL_TOKEN_NMTOKEN) {
506: // PATTERN "\\c+"
507: seenErr = !XMLCharacterProperties.validNmtoken(content);
508: } else if (tokenType == SPECIAL_TOKEN_NAME
509: || tokenType == SPECIAL_TOKEN_IDNAME
510: || tokenType == SPECIAL_TOKEN_IDREFNAME) {
511: // PATTERN "\\i\\c*"
512: seenErr = !XMLCharacterProperties.validName(content);
513: } else if (tokenType == SPECIAL_TOKEN_NCNAME
514: || tokenType == SPECIAL_TOKEN_IDNCNAME
515: || tokenType == SPECIAL_TOKEN_IDREFNCNAME
516: || tokenType == SPECIAL_TOKEN_ENTITY) {
517: // PATTERN "[\\i-[:]][\\c-[:]]*"
518: seenErr = !XMLCharacterProperties.validNCName(content);
519: }
520: if (seenErr) {
521: throw new InvalidDatatypeValueException("Value '" + content
522: + "' is not a valid " + tokenType);
523: }
524: }
525: }
|