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   * @since 2.0
71   */
72  @JSFValidator(
73      name="f:validateRegex",
74      bodyContent="empty",
75      tagClass="org.apache.myfaces.taglib.core.ValidateRegexTag")
76  @JSFJspProperty(
77      name="binding",
78      returnType = "javax.faces.validator.RegexValidator",
79      longDesc = "A ValueExpression that evaluates to a RegexValidator.")
80  public class RegexValidator implements Validator, PartialStateHolder
81  {
82  
83      /**
84       * Converter ID, as defined by the JSF 2.0 specification.
85       */
86      public static final String VALIDATOR_ID = "javax.faces.RegularExpression";
87  
88      /**
89       * This message ID is used when the pattern is <code>null</code>, or an empty String.
90       */
91      public static final String PATTERN_NOT_SET_MESSAGE_ID = "javax.faces.validator.RegexValidator.PATTERN_NOT_SET";
92  
93      /**
94       * This message ID is used when the passed value is not a String, or when
95       * the pattern does not match the passed value.
96       */
97      public static final String NOT_MATCHED_MESSAGE_ID = "javax.faces.validator.RegexValidator.NOT_MATCHED";
98  
99      /**
100      * This message ID is used when the pattern is not a valid regular expression, according
101      * to the rules as defined in class {@link java.util.regex.Pattern}
102      */
103     public static final String MATCH_EXCEPTION_MESSAGE_ID = "javax.faces.validator.RegexValidator.MATCH_EXCEPTION";
104 
105     //TODO: Find a better place for such a common constant
106     private static final String EMPTY_STRING = "";
107 
108     private String pattern;
109 
110     private boolean isTransient = false;
111 
112     // VALIDATE
113     /** {@inheritDoc} */
114     public void validate(FacesContext context,
115                          UIComponent component,
116                          Object value)
117     {
118         if (context == null)
119         {
120             throw new NullPointerException("context");
121         }
122         if (component == null)
123         {
124             throw new NullPointerException("component");
125         }
126 
127         if (value == null)
128         {
129             return;
130         }
131         if (!(value instanceof String))
132         {
133             throw new ValidatorException(_MessageUtils.getErrorMessage(context, NOT_MATCHED_MESSAGE_ID, null));
134         }
135 
136         String string = (String) value;
137 
138         Pattern thePattern;
139         if (pattern == null
140          || pattern.equals(EMPTY_STRING))
141         {
142             throw new ValidatorException(_MessageUtils.getErrorMessage(context, PATTERN_NOT_SET_MESSAGE_ID, null));
143         }
144 
145         try
146         {
147             thePattern = Pattern.compile(pattern);
148         }
149         catch (PatternSyntaxException pse)
150         {
151             throw new ValidatorException(_MessageUtils.getErrorMessage(context, MATCH_EXCEPTION_MESSAGE_ID, null));
152         }
153 
154         if (!thePattern.matcher(string).matches())
155         {
156             //TODO: Present the patternExpression in a more user friendly way
157             Object[] args = {thePattern, _MessageUtils.getLabel(context, component)};
158             throw new ValidatorException(_MessageUtils.getErrorMessage(context, NOT_MATCHED_MESSAGE_ID, args));
159         }
160     }
161 
162     // RESTORE & SAVE STATE
163 
164     /** {@inheritDoc} */
165     public Object saveState(FacesContext context)
166     {
167         if (!initialStateMarked())
168         {
169             return pattern;
170         }
171         return null;
172     }
173 
174     /** {@inheritDoc} */
175     public void restoreState(FacesContext context, Object state)
176     {
177         if (state != null)
178         {
179             //Since pattern is required, if state is null
180             //nothing has changed
181             this.pattern = (String) state;
182         }
183     }
184 
185     // SETTER & GETTER
186 
187     /** {@inheritDoc} */
188     public boolean isTransient()
189     {
190         return isTransient;
191     }
192 
193     /** {@inheritDoc} */
194     public void setTransient(boolean isTransient)
195     {
196         this.isTransient = isTransient;
197     }
198 
199     /**
200      * The Regular Expression property to validate against. This property must be a ValueExpression
201      * that resolves to a String in the format of the java.util.regex patterns.
202      *
203      * @param pattern a ValueExpression that evaluates to a String that is the regular expression pattern
204      */
205     public void setPattern(String pattern)
206     {
207         //TODO: Validate input parameter
208         this.pattern = pattern;
209         clearInitialState();
210     }
211 
212     /**
213      * Return the ValueExpression that yields the regular expression pattern when evaluated.
214      *
215      * @return The pattern.
216      */
217     @JSFProperty(required = true)
218     public String getPattern()
219     {
220         return this.pattern;
221     }
222 
223     private boolean _initialStateMarked = false;
224 
225     public void clearInitialState()
226     {
227         _initialStateMarked = false;
228     }
229 
230     public boolean initialStateMarked()
231     {
232         return _initialStateMarked;
233     }
234 
235     public void markInitialState()
236     {
237         _initialStateMarked = true;
238     }
239     
240     @JSFProperty(faceletsOnly=true)
241     @SuppressWarnings("unused")
242     private Boolean isDisabled()
243     {
244         return null;
245     }
246     
247     @JSFProperty(faceletsOnly=true)
248     @SuppressWarnings("unused")
249     private String getFor()
250     {
251         return null;
252     }
253 }