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) throw new NullPointerException("context");
120         if (component == null) throw new NullPointerException("component");
121 
122         if (value == null)
123         {
124             return;
125         }
126         if (!(value instanceof String))
127         {
128             throw new ValidatorException(_MessageUtils.getErrorMessage(context, NOT_MATCHED_MESSAGE_ID, null));
129         }
130 
131         String string = (String) value;
132 
133         Pattern thePattern;
134         if (pattern == null
135          || pattern.equals(EMPTY_STRING))
136         {
137             throw new ValidatorException(_MessageUtils.getErrorMessage(context, PATTERN_NOT_SET_MESSAGE_ID, null));
138         }
139 
140         try
141         {
142             thePattern = Pattern.compile(pattern);
143         }
144         catch (PatternSyntaxException pse)
145         {
146             throw new ValidatorException(_MessageUtils.getErrorMessage(context, MATCH_EXCEPTION_MESSAGE_ID, null));
147         }
148 
149         if (!thePattern.matcher(string).matches())
150         {
151             //TODO: Present the patternExpression in a more user friendly way
152             Object[] args = {thePattern, _MessageUtils.getLabel(context, component)};
153             throw new ValidatorException(_MessageUtils.getErrorMessage(context, NOT_MATCHED_MESSAGE_ID, args));
154         }
155     }
156 
157     // RESTORE & SAVE STATE
158 
159     /** {@inheritDoc} */
160     public Object saveState(FacesContext context)
161     {
162         if (!initialStateMarked())
163         {
164             return pattern;
165         }
166         return null;
167     }
168 
169     /** {@inheritDoc} */
170     public void restoreState(FacesContext context, Object state)
171     {
172         if (state != null)
173         {
174             //Since pattern is required, if state is null
175             //nothing has changed
176             this.pattern = (String) state;
177         }
178     }
179 
180     // SETTER & GETTER
181 
182     /** {@inheritDoc} */
183     public boolean isTransient()
184     {
185         return isTransient;
186     }
187 
188     /** {@inheritDoc} */
189     public void setTransient(boolean isTransient)
190     {
191         this.isTransient = isTransient;
192     }
193 
194     /**
195      * The Regular Expression property to validate against. This property must be a ValueExpression
196      * that resolves to a String in the format of the java.util.regex patterns.
197      *
198      * @param pattern a ValueExpression that evaluates to a String that is the regular expression pattern
199      */
200     public void setPattern(String pattern)
201     {
202         //TODO: Validate input parameter
203         this.pattern = pattern;
204         clearInitialState();
205     }
206 
207     /**
208      * Return the ValueExpression that yields the regular expression pattern when evaluated.
209      *
210      * @return The pattern.
211      */
212     @JSFProperty(required = true)
213     public String getPattern()
214     {
215         return this.pattern;
216     }
217 
218     private boolean _initialStateMarked = false;
219 
220     public void clearInitialState()
221     {
222         _initialStateMarked = false;
223     }
224 
225     public boolean initialStateMarked()
226     {
227         return _initialStateMarked;
228     }
229 
230     public void markInitialState()
231     {
232         _initialStateMarked = true;
233     }
234     
235     @JSFProperty(faceletsOnly=true)
236     private Boolean isDisabled()
237     {
238         return null;
239     }
240     
241     @JSFProperty(faceletsOnly=true)
242     private String getFor()
243     {
244         return null;
245     }
246 }