| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package javax.faces.validator; |
| |
| import org.apache.myfaces.core.api.shared.MessageUtils; |
| import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty; |
| import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty; |
| import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFValidator; |
| |
| import javax.faces.component.PartialStateHolder; |
| import javax.faces.component.UIComponent; |
| import javax.faces.context.FacesContext; |
| import java.util.regex.Pattern; |
| import java.util.regex.PatternSyntaxException; |
| |
| /** |
| * <p> |
| * <strong>RegexValidator</strong> is a {@link javax.faces.validator.Validator} |
| * that checks the value of the corresponding component against specified |
| * pattern using Java regular expression syntax. |
| * |
| * The regular expression syntax accepted by the RegexValidator class is |
| * same as mentioned in class {@link java.util.regex.Pattern} in package |
| * <code>java.util.regex</code>. |
| * </p> |
| * |
| * <p> |
| * The following algorithm is implemented: |
| * </p> |
| * |
| * <ul> |
| * <li>If the passed value is <code>null</code>, exit immediately.</li> |
| * <li> |
| * If the passed value is not a String, exit with a {@link #NOT_MATCHED_MESSAGE_ID} |
| * error message. |
| * </li> |
| * <li> |
| * If no pattern has been set, or pattern resolves to <code>null</code> or an |
| * empty String, throw a {@link javax.faces.validator.ValidatorException} |
| * with a {@link #PATTERN_NOT_SET_MESSAGE_ID} message. |
| * </li> |
| * <li> |
| * If pattern is not a valid regular expression, according to the rules as defined |
| * in class {@link java.util.regex.Pattern}, throw a {@link ValidatorException} |
| * with a (@link #MATCH_EXCEPTION_MESSAGE_ID} message. |
| * </li> |
| * <li> |
| * If a <code>pattern</code> property has been configured on this |
| * {@link javax.faces.validator.Validator}, check the passed value against this pattern. |
| * If value does not match pattern throw a {@link ValidatorException} |
| * containing a {@link #NOT_MATCHED_MESSAGE_ID} message. |
| * </li> |
| * </ul> |
| * |
| * @since 2.0 |
| */ |
| @JSFValidator( |
| name="f:validateRegex", |
| bodyContent="empty", |
| tagClass="org.apache.myfaces.taglib.core.ValidateRegexTag") |
| @JSFJspProperty( |
| name="binding", |
| returnType = "javax.faces.validator.RegexValidator", |
| longDesc = "A ValueExpression that evaluates to a RegexValidator.") |
| public class RegexValidator implements Validator, PartialStateHolder |
| { |
| |
| /** |
| * Converter ID, as defined by the JSF 2.0 specification. |
| */ |
| public static final String VALIDATOR_ID = "javax.faces.RegularExpression"; |
| |
| /** |
| * This message ID is used when the pattern is <code>null</code>, or an empty String. |
| */ |
| public static final String PATTERN_NOT_SET_MESSAGE_ID = "javax.faces.validator.RegexValidator.PATTERN_NOT_SET"; |
| |
| /** |
| * This message ID is used when the passed value is not a String, or when |
| * the pattern does not match the passed value. |
| */ |
| public static final String NOT_MATCHED_MESSAGE_ID = "javax.faces.validator.RegexValidator.NOT_MATCHED"; |
| |
| /** |
| * This message ID is used when the pattern is not a valid regular expression, according |
| * to the rules as defined in class {@link java.util.regex.Pattern} |
| */ |
| public static final String MATCH_EXCEPTION_MESSAGE_ID = "javax.faces.validator.RegexValidator.MATCH_EXCEPTION"; |
| |
| //TODO: Find a better place for such a common constant |
| private static final String EMPTY_STRING = ""; |
| |
| private String pattern; |
| |
| private boolean _transient = false; |
| private boolean _initialStateMarked = false; |
| |
| // VALIDATE |
| /** {@inheritDoc} */ |
| @Override |
| public void validate(FacesContext context, UIComponent component, Object value) |
| { |
| if (context == null) |
| { |
| throw new NullPointerException("context"); |
| } |
| if (component == null) |
| { |
| throw new NullPointerException("component"); |
| } |
| |
| if (value == null) |
| { |
| return; |
| } |
| if (!(value instanceof CharSequence)) |
| { |
| // The value must be CharSequence - java.util.regex.Pattern.matcher(CharSequence) |
| // this instanceof check indicates disagreement between local value (converted) |
| // and type expected in validator but behaviour is not specified. |
| // We use message for String conversion here: |
| // javax.faces.converter.STRING={1}: Could not convert ''{0}'' to a string |
| Object[] args = {value, MessageUtils.getLabel(context, component)}; |
| throw new ValidatorException(MessageUtils.getErrorMessage(context, "javax.faces.converter.STRING", args)); |
| } |
| |
| CharSequence charSequence = (CharSequence) value; |
| |
| Pattern thePattern; |
| if (pattern == null || pattern.equals(EMPTY_STRING)) |
| { |
| throw new ValidatorException(MessageUtils.getErrorMessage(context, PATTERN_NOT_SET_MESSAGE_ID, null)); |
| } |
| |
| try |
| { |
| thePattern = Pattern.compile(pattern); |
| } |
| catch (PatternSyntaxException pse) |
| { |
| throw new ValidatorException(MessageUtils.getErrorMessage(context, MATCH_EXCEPTION_MESSAGE_ID, null)); |
| } |
| |
| if (!thePattern.matcher(charSequence).matches()) |
| { |
| //TODO: Present the patternExpression in a more user friendly way |
| Object[] args = {thePattern, MessageUtils.getLabel(context, component)}; |
| throw new ValidatorException(MessageUtils.getErrorMessage(context, NOT_MATCHED_MESSAGE_ID, args)); |
| } |
| } |
| |
| // RESTORE & SAVE STATE |
| |
| /** {@inheritDoc} */ |
| @Override |
| public Object saveState(FacesContext context) |
| { |
| if (context == null) |
| { |
| throw new NullPointerException("context"); |
| } |
| |
| if (!initialStateMarked()) |
| { |
| return pattern; |
| } |
| return null; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void restoreState(FacesContext context, Object state) |
| { |
| if (context == null) |
| { |
| throw new NullPointerException("context"); |
| } |
| |
| if (state != null) |
| { |
| //Since pattern is required, if state is null |
| //nothing has changed |
| this.pattern = (String) state; |
| } |
| } |
| |
| // SETTER & GETTER |
| |
| /** {@inheritDoc} */ |
| @Override |
| public boolean isTransient() |
| { |
| return _transient; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void setTransient(boolean isTransient) |
| { |
| this._transient = isTransient; |
| } |
| |
| /** |
| * The Regular Expression property to validate against. This property must be a ValueExpression |
| * that resolves to a String in the format of the java.util.regex patterns. |
| * |
| * @param pattern a ValueExpression that evaluates to a String that is the regular expression pattern |
| */ |
| public void setPattern(String pattern) |
| { |
| //TODO: Validate input parameter |
| this.pattern = pattern; |
| clearInitialState(); |
| } |
| |
| /** |
| * Return the ValueExpression that yields the regular expression pattern when evaluated. |
| * |
| * @return The pattern. |
| */ |
| @JSFProperty(required = true) |
| public String getPattern() |
| { |
| return this.pattern; |
| } |
| |
| @Override |
| public void clearInitialState() |
| { |
| _initialStateMarked = false; |
| } |
| |
| @Override |
| public boolean initialStateMarked() |
| { |
| return _initialStateMarked; |
| } |
| |
| @Override |
| public void markInitialState() |
| { |
| _initialStateMarked = true; |
| } |
| |
| @JSFProperty(faceletsOnly=true) |
| @SuppressWarnings("unused") |
| private Boolean isDisabled() |
| { |
| return null; |
| } |
| |
| @JSFProperty(faceletsOnly=true) |
| @SuppressWarnings("unused") |
| private String getFor() |
| { |
| return null; |
| } |
| } |