View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
32   *
33   * $Id$
34   */
35  package org.exolab.castor.xml.validators;
36  
37  import org.exolab.castor.xml.TypeValidator;
38  import org.exolab.castor.xml.ValidationContext;
39  import org.exolab.castor.xml.ValidationException;
40  import org.exolab.castor.xml.XMLConstants;
41  
42  /**
43   * The String Validation class.
44   *
45   * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
46   * @version $Revision$ $Date: 2004-12-11 02:13:52 -0700 (Sat, 11 Dec 2004) $
47   */
48  public class StringValidator extends PatternValidator implements TypeValidator {
49  
50    /** If true, this field must be present. */
51    private boolean _required = false;
52    /** Whitespace handing for this String. (Really belongs elsewhere.) */
53    private String _whiteSpace = XMLConstants.WHITESPACE_PRESERVE;
54    /** Exact length required of the string. 0 means no length requirement. */
55    private int _length = 0;
56    /** Minimum length required of the string. 0 means no minimum length requirement. */
57    private int _minLength = 0;
58    /** Maximum length required of the string. -1 means no maximum length requirement. */
59    private int _maxLength = -1;
60    /** Fixed value of this string, null if none is set. */
61    private String _fixed = null;
62  
63    /**
64     * Creates a new StringValidator with no restrictions.
65     */
66    public StringValidator() {
67      super();
68    } // -- StringValidator
69  
70    /**
71     * Clears the fixed value for this ShortValidator.
72     */
73    public void clearFixed() {
74      _fixed = null;
75    } // -- clearFixed
76  
77    /**
78     * Sets the fixed value in which all valid Strings must match.
79     *
80     * @param fixedValue the fixed value that all Strings must match
81     */
82    public void setFixed(final String fixedValue) {
83      this._fixed = fixedValue;
84    } // -- setFixedValue
85  
86    /**
87     * Only used for backward compatibility for object model generated with an old version of Castor.
88     *
89     * @param fixedValue the fixed value that all Strings must match
90     *
91     * @deprecated since 0.9.4_beta
92     */
93    public void setFixedValue(final String fixedValue) {
94      setFixed(fixedValue);
95    }
96  
97    /**
98     * Sets the maximum string length for String validation. To pass validation, a String must be
99     * shorter than or equal to this length. To remove this facet, set a negative value.
100    *
101    * @param maxLength the maximum length for valid Strings
102    */
103   public void setMaxLength(final int maxLength) {
104     this._maxLength = maxLength;
105   } // -- setMaxLength
106 
107   /**
108    * Sets the minimum string length for String validation. To pass validation, a String must be
109    * longer than or equal to this length. To remove this facet, set a negative value.
110    *
111    * @param minLength the minimum length for valid Strings
112    */
113   public void setMinLength(final int minLength) {
114     this._minLength = minLength;
115   } // -- setMinLength
116 
117   /**
118    * Sets the required string length for String validation. To pass validation, a String must be
119    * exactly this many characters long. To remove this facet, set a negative value.
120    *
121    * @param length the required length for valid Strings
122    */
123   public void setLength(final int length) {
124     this._length = length;
125     setMaxLength(length);
126     setMinLength(length);
127   } // -- setLength
128 
129   /**
130    * Sets whether or not a String is required (non null).
131    *
132    * @param required the flag indicating whether this string must be non-null.
133    */
134   public void setRequired(final boolean required) {
135     this._required = required;
136   } // -- setRequired
137 
138   /**
139    * Sets the whiteSpace facet of the validator.
140    * <p>
141    * The value of the whiteSpace facet must be one of the following:
142    * <ul>
143    * <li>preserve</li>
144    * <li>replace</li>
145    * <li>collapse</li>
146    * </ul>
147    * any other value will generate a Warning and set the whiteSpace to preserve.
148    * <p>
149    * FIXME: This is not really a function of validation, but of XML processing before the string is
150    * returned from the XML processor. This should be moved to the FieldHandler, or somewhere else,
151    * but not here.
152    *
153    * @param value the whiteSpace value
154    */
155   public void setWhiteSpace(final String value) {
156     if (value.equals(XMLConstants.WHITESPACE_PRESERVE)) {
157       this._whiteSpace = value;
158     } else if (value.equals(XMLConstants.WHITESPACE_REPLACE)) {
159       this._whiteSpace = value;
160     } else if (value.equals(XMLConstants.WHITESPACE_COLLAPSE)) {
161       this._whiteSpace = value;
162     } else {
163       System.out.println("Warning: '" + value + "' is a bad entry for the whiteSpace value");
164       this._whiteSpace = XMLConstants.WHITESPACE_PRESERVE;
165     }
166   } // -- setWhiteSpace
167 
168   /**
169    * Normalizes the given string according to the whiteSpace facet used.
170    * <p>
171    * FIXME: THIS METHOD SHOULD NOT BE HERE..SHOULD BE MOVED TO A FieldHandler or to the
172    * Unmarshaller...but not here!!! (kvisco 20030125)
173    * 
174    * @param value the String to normalize
175    * @return the normalized string.
176    */
177   public String normalize(final String value) {
178     if (value == null) {
179       return null;
180     }
181 
182     if (value.length() == 0) {
183       return value;
184     }
185 
186     char[] chars = value.toCharArray();
187     int length = chars.length;
188 
189     for (int i = 0; i < length; i++) {
190       switch (chars[i]) {
191         case '\t':
192         case '\r':
193         case '\n':
194           chars[i] = ' ';
195           break;
196         default:
197           continue;
198       }
199     }
200 
201     if (_whiteSpace.equals(XMLConstants.WHITESPACE_COLLAPSE)) {
202       // -- heavy method to keep compatibility
203       // -- with JDK 1.1 (can't use Vector.toArray)
204       char[] temp = new char[chars.length];
205       int tempCount = 0;
206       int i = 0;
207       while (i < length - 1) {
208         if (chars[i] == ' ') {
209           // --put the first space
210           temp[tempCount] = chars[i];
211           tempCount++;
212           // --skip the others
213           i++;
214           while (i < length - 1 && chars[i] == ' ') {
215             i++;
216           }
217           continue;
218         }
219         temp[tempCount] = chars[i];
220         tempCount++;
221         i++;
222       }
223       // --we are at the end
224       if (chars[i] != ' ') {
225         temp[tempCount] = chars[i];
226       }
227 
228       length = ++tempCount;
229       chars = temp;
230     }
231 
232     return new String(chars, 0, length);
233   }
234 
235   /**
236    * Validates the given Object.
237    *
238    * @param value the string to validate
239    * @param context the ValidationContext
240    * @throws ValidationException if the object fails validation.
241    */
242   public void validate(final String value, final ValidationContext context)
243       throws ValidationException {
244     if (value == null) {
245       if (_required && !isNillable()) {
246         String err = "this is a required field and cannot be null.";
247         throw new ValidationException(err);
248       }
249       return;
250     }
251 
252     if (_fixed != null && !_fixed.equals(value)) {
253       String err = "strings of this type must be equal to the fixed value of " + _fixed;
254       throw new ValidationException(err);
255     }
256 
257     int len = value.length();
258 
259     if (_length > 0 && len != _length) {
260       String err = "Strings of this type must have a length of " + _length + " characters";
261       throw new ValidationException(err);
262     }
263 
264     if (_minLength > 0 && len < _minLength) {
265       String err =
266           "Strings of this type must have a minimum length of " + _minLength + " characters";
267       throw new ValidationException(err);
268     }
269 
270     if (_maxLength >= 0 && len > _maxLength) {
271       String err =
272           "Strings of this type must have a maximum length of " + _maxLength + " characters";
273       throw new ValidationException(err);
274     }
275 
276     if (hasPattern()) {
277       super.validate(value, context);
278     }
279 
280     if (!this._whiteSpace.equals(XMLConstants.WHITESPACE_PRESERVE)) {
281       normalize(value);
282     }
283   } // -- validate
284 
285   /**
286    * Validates the given Object.
287    *
288    * @param object the Object to validate
289    * @throws ValidationException if the object fails validation.
290    */
291   public void validate(final Object object) throws ValidationException {
292     validate(object, (ValidationContext) null);
293   } // -- validate
294 
295   /**
296    * Validates the given Object.
297    *
298    * @param object the Object to validate
299    * @param context the ValidationContext
300    * @throws ValidationException if the object fails validation.
301    */
302   public void validate(final Object object, final ValidationContext context)
303       throws ValidationException {
304     if (object == null) {
305       if (_required) {
306         String err = "this is a required field and cannot be null.";
307         throw new ValidationException(err);
308       }
309       return;
310     }
311 
312     validate(object.toString(), context);
313   } // -- validate
314 
315 } // -- StringValidator