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 }