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 }