View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces.validator;
20  
21  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
22  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
23  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFValidator;
24  
25  import javax.faces.component.PartialStateHolder;
26  import javax.faces.component.UIComponent;
27  import javax.faces.context.FacesContext;
28  import java.util.regex.Pattern;
29  import java.util.regex.PatternSyntaxException;
30  
31  /**
32   * <p>
33   *   <strong>RegexValidator</strong> is a {@link javax.faces.validator.Validator}
34   *   that checks the value of the corresponding component against specified
35   *   pattern using Java regular expression syntax.
36   *
37   *   The regular expression syntax accepted by the RegexValidator class is
38   *   same as mentioned in class {@link java.util.regex.Pattern} in package
39   *   <code>java.util.regex</code>.
40   * </p>
41   *
42   * <p>
43   *   The following algorithm is implemented:
44   * </p>
45   *
46   * <ul>
47   *   <li>If the passed value is <code>null</code>, exit immediately.</li>
48   *   <li>
49   *     If the passed value is not a String, exit with a {@link #NOT_MATCHED_MESSAGE_ID}
50   *     error message.
51   *   </li>
52   *   <li>
53   *     If no pattern has been set, or pattern resolves to <code>null</code> or an
54   *     empty String, throw a {@link javax.faces.validator.ValidatorException}
55   *     with a {@link #PATTERN_NOT_SET_MESSAGE_ID} message.
56   *   </li>
57   *   <li>
58   *     If pattern is not a valid regular expression, according to the rules as defined
59   *     in class {@link java.util.regex.Pattern}, throw a {@link ValidatorException}
60   *     with a (@link #MATCH_EXCEPTION_MESSAGE_ID} message.
61   *   </li>
62   *   <li>
63   *     If a <code>pattern</code> property has been configured on this
64   *     {@link javax.faces.validator.Validator}, check the passed value against this pattern.
65   *     If value does not match pattern throw a {@link ValidatorException}
66   *     containing a {@link #NOT_MATCHED_MESSAGE_ID} message.
67   *   </li>
68   * </ul>
69   *
70   * @author Jan-Kees van Andel
71   * @since 2.0
72   */
73  @JSFValidator(
74      name="f:validateRegex",
75      bodyContent="empty",
76      tagClass="org.apache.myfaces.taglib.core.ValidateRegexTag")
77  @JSFJspProperty(
78      name="binding",
79      returnType = "javax.faces.validator.RegexValidator",
80      longDesc = "A ValueExpression that evaluates to a RegexValidator.")
81  public class RegexValidator implements Validator, PartialStateHolder
82  {
83  
84      /**
85       * Converter ID, as defined by the JSF 2.0 specification.
86       */
87      public static final String VALIDATOR_ID = "javax.faces.RegularExpression";
88  
89      /**
90       * This message ID is used when the pattern is <code>null</code>, or an empty String.
91       */
92      public static final String PATTERN_NOT_SET_MESSAGE_ID = "javax.faces.validator.RegexValidator.PATTERN_NOT_SET";
93  
94      /**
95       * This message ID is used when the passed value is not a String, or when
96       * the pattern does not match the passed value.
97       */
98      public static final String NOT_MATCHED_MESSAGE_ID = "javax.faces.validator.RegexValidator.NOT_MATCHED";
99  
100     /**
101      * This message ID is used when the pattern is not a valid regular expression, according
102      * to the rules as defined in class {@link java.util.regex.Pattern}
103      */
104     public static final String MATCH_EXCEPTION_MESSAGE_ID = "javax.faces.validator.RegexValidator.MATCH_EXCEPTION";
105 
106     //TODO: Find a better place for such a common constant
107     private static final String EMPTY_STRING = "";
108 
109     private String pattern;
110 
111     private boolean isTransient = false;
112 
113     // VALIDATE
114     /** {@inheritDoc} */
115     public void validate(FacesContext context,
116                          UIComponent component,
117                          Object value)
118     {
119         if (context == null)
120         {
121             throw new NullPointerException("context");
122         }
123         if (component == null)
124         {
125             throw new NullPointerException("component");
126         }
127 
128         if (value == null)
129         {
130             return;
131         }
132         if (!(value instanceof String))
133         {
134             throw new ValidatorException(_MessageUtils.getErrorMessage(context, NOT_MATCHED_MESSAGE_ID, null));
135         }
136 
137         String string = (String) value;
138 
139         Pattern thePattern;
140         if (pattern == null
141          || pattern.equals(EMPTY_STRING))
142         {
143             throw new ValidatorException(_MessageUtils.getErrorMessage(context, PATTERN_NOT_SET_MESSAGE_ID, null));
144         }
145 
146         try
147         {
148             thePattern = Pattern.compile(pattern);
149         }
150         catch (PatternSyntaxException pse)
151         {
152             throw new ValidatorException(_MessageUtils.getErrorMessage(context, MATCH_EXCEPTION_MESSAGE_ID, null));
153         }
154 
155         if (!thePattern.matcher(string).matches())
156         {
157             //TODO: Present the patternExpression in a more user friendly way
158             Object[] args = {thePattern, _MessageUtils.getLabel(context, component)};
159             throw new ValidatorException(_MessageUtils.getErrorMessage(context, NOT_MATCHED_MESSAGE_ID, args));
160         }
161     }
162 
163     // RESTORE & SAVE STATE
164 
165     /** {@inheritDoc} */
166     public Object saveState(FacesContext context)
167     {
168         if (!initialStateMarked())
169         {
170             return pattern;
171         }
172         return null;
173     }
174 
175     /** {@inheritDoc} */
176     public void restoreState(FacesContext context, Object state)
177     {
178         if (state != null)
179         {
180             //Since pattern is required, if state is null
181             //nothing has changed
182             this.pattern = (String) state;
183         }
184     }
185 
186     // SETTER & GETTER
187 
188     /** {@inheritDoc} */
189     public boolean isTransient()
190     {
191         return isTransient;
192     }
193 
194     /** {@inheritDoc} */
195     public void setTransient(boolean isTransient)
196     {
197         this.isTransient = isTransient;
198     }
199 
200     /**
201      * The Regular Expression property to validate against. This property must be a ValueExpression
202      * that resolves to a String in the format of the java.util.regex patterns.
203      *
204      * @param pattern a ValueExpression that evaluates to a String that is the regular expression pattern
205      */
206     public void setPattern(String pattern)
207     {
208         //TODO: Validate input parameter
209         this.pattern = pattern;
210         clearInitialState();
211     }
212 
213     /**
214      * Return the ValueExpression that yields the regular expression pattern when evaluated.
215      *
216      * @return The pattern.
217      */
218     @JSFProperty(required = true)
219     public String getPattern()
220     {
221         return this.pattern;
222     }
223 
224     private boolean _initialStateMarked = false;
225 
226     public void clearInitialState()
227     {
228         _initialStateMarked = false;
229     }
230 
231     public boolean initialStateMarked()
232     {
233         return _initialStateMarked;
234     }
235 
236     public void markInitialState()
237     {
238         _initialStateMarked = true;
239     }
240     
241     @JSFProperty(faceletsOnly=true)
242     private Boolean isDisabled()
243     {
244         return null;
245     }
246     
247     @JSFProperty(faceletsOnly=true)
248     private String getFor()
249     {
250         return null;
251     }
252 }