View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.validator.routines;
18  
19  import java.io.Serializable;
20  import java.util.List;
21  import java.util.regex.Matcher;
22  import java.util.regex.Pattern;
23  
24  /**
25   * <strong>Regular Expression</strong> validation (using the JRE's regular expression support).
26   * <p>
27   * Constructs the validator either for a single regular expression or a set (array) of regular expressions. By default, validation is <em>case sensitive</em> but
28   * constructors are provided to allow <em>case in-sensitive</em> validation. For example to create a validator which does <em>case in-sensitive</em> validation
29   * for a set of regular expressions:
30   * </p>
31   *
32   * <pre>
33   * <code>
34   * String[] regexs = new String[] {...};
35   * RegexValidator validator = new RegexValidator(regexs, false);
36   * </code>
37   * </pre>
38   *
39   * <ul>
40   * <li>Validate {@code true} or {@code false}:</li>
41   * <li>
42   * <ul>
43   * <li>{@code boolean valid = validator.isValid(value);}</li>
44   * </ul>
45   * </li>
46   * <li>Validate returning an aggregated String of the matched groups:</li>
47   * <li>
48   * <ul>
49   * <li>{@code String result = validator.validate(value);}</li>
50   * </ul>
51   * </li>
52   * <li>Validate returning the matched groups:</li>
53   * <li>
54   * <ul>
55   * <li>{@code String[] result = validator.match(value);}</li>
56   * </ul>
57   * </li>
58   * </ul>
59   *
60   * <strong>Note that patterns are matched against the entire input.</strong>
61   *
62   * <p>
63   * Cached instances pre-compile and re-use {@link Pattern}(s) - which according to the {@link Pattern} API are safe to use in a multi-threaded environment.
64   * </p>
65   *
66   * @since 1.4
67   */
68  public class RegexValidator implements Serializable {
69  
70      private static final long serialVersionUID = -8832409930574867162L;
71  
72      private static final int CASE_SENSITIVE = 0;
73  
74      private static int toCompileFlags(final boolean caseSensitive) {
75          return caseSensitive ? CASE_SENSITIVE : Pattern.CASE_INSENSITIVE;
76      }
77  
78      /**
79       * Compiled RE patterns from constructors.
80       */
81      private final Pattern[] patterns;
82  
83      /**
84       * Constructs a new instance that matches any one of the set of regular expressions with the specified case sensitivity.
85       *
86       * @param regexs The set of regular expressions this validator will validate against
87       * @param flags  See {@link Pattern#compile(String, int)}. sensitive</i>, otherwise matching is <em>case in-sensitive</em>
88       */
89      private RegexValidator(final int flags, final String... regexs) {
90          if (regexs == null || regexs.length == 0) {
91              throw new IllegalArgumentException("Regular expressions are missing");
92          }
93          patterns = new Pattern[regexs.length];
94          for (int i = 0; i < regexs.length; i++) {
95              final String regex = regexs[i];
96              if (regex == null || regex.isEmpty()) {
97                  throw new IllegalArgumentException("Regular expression[" + i + "] is missing");
98              }
99              patterns[i] = Pattern.compile(regex, flags);
100         }
101     }
102 
103     /**
104      * Constructs a new <em>case sensitive</em> instance that matches any one in the list of regular expressions.
105      *
106      * @param regexs The set of regular expressions this validator will validate against
107      */
108     RegexValidator(final List<String> regexs) {
109         this(CASE_SENSITIVE, regexs.toArray(new String[] {}));
110     }
111 
112     /**
113      * Constructs a new <em>case sensitive</em> instance for a single regular expression.
114      *
115      * @param regex The regular expression this validator will validate against
116      */
117     public RegexValidator(final String regex) {
118         this(CASE_SENSITIVE, regex);
119     }
120 
121     /**
122      * Constructs a new <em>case sensitive</em> instance that matches any one in the array of regular expressions.
123      *
124      * @param regexs The set of regular expressions this validator will validate against
125      */
126     public RegexValidator(final String... regexs) {
127         this(CASE_SENSITIVE, regexs);
128     }
129 
130     /**
131      * Constructs a new instance for a single regular expression with the specified case sensitivity.
132      *
133      * @param regex         The regular expression this validator will validate against
134      * @param caseSensitive when {@code true} matching is <em>case sensitive</em>, otherwise matching is <em>case in-sensitive</em>
135      */
136     public RegexValidator(final String regex, final boolean caseSensitive) {
137         this(toCompileFlags(caseSensitive), regex);
138     }
139 
140     /**
141      * Constructs a new instance that matches any one of the set of regular expressions with the specified case sensitivity.
142      *
143      * @param regexs        The set of regular expressions this validator will validate against
144      * @param caseSensitive when {@code true} matching is <em>case sensitive</em>, otherwise matching is <em>case in-sensitive</em>
145      */
146     public RegexValidator(final String[] regexs, final boolean caseSensitive) {
147         this(toCompileFlags(caseSensitive), regexs);
148     }
149 
150     /**
151      * Gets a copy of the Patterns.
152      *
153      * @return a copy of the Patterns.
154      * @since 1.8
155      */
156     public Pattern[] getPatterns() {
157         return patterns.clone();
158     }
159 
160     /**
161      * Validates a value against the set of regular expressions.
162      *
163      * @param value The value to validate.
164      * @return {@code true} if the value is valid otherwise {@code false}.
165      */
166     public boolean isValid(final String value) {
167         if (value == null) {
168             return false;
169         }
170         for (final Pattern pattern : patterns) {
171             if (pattern.matcher(value).matches()) {
172                 return true;
173             }
174         }
175         return false;
176     }
177 
178     /**
179      * Validates a value against the set of regular expressions returning the array of matched groups.
180      *
181      * @param value The value to validate.
182      * @return String array of the <em>groups</em> matched if valid or {@code null} if invalid
183      */
184     public String[] match(final String value) {
185         if (value == null) {
186             return null;
187         }
188         for (final Pattern pattern : patterns) {
189             final Matcher matcher = pattern.matcher(value);
190             if (matcher.matches()) {
191                 final int count = matcher.groupCount();
192                 final String[] groups = new String[count];
193                 for (int j = 0; j < count; j++) {
194                     groups[j] = matcher.group(j + 1);
195                 }
196                 return groups;
197             }
198         }
199         return null;
200     }
201 
202     /**
203      * Provides a String representation of this validator.
204      *
205      * @return A String representation of this validator.
206      */
207     @Override
208     public String toString() {
209         final StringBuilder buffer = new StringBuilder();
210         buffer.append("RegexValidator{");
211         for (int i = 0; i < patterns.length; i++) {
212             if (i > 0) {
213                 buffer.append(",");
214             }
215             buffer.append(patterns[i].pattern());
216         }
217         buffer.append("}");
218         return buffer.toString();
219     }
220 
221     /**
222      * Validates a value against the set of regular expressions returning a String value of the aggregated groups.
223      *
224      * @param value The value to validate.
225      * @return Aggregated String value comprised of the <em>groups</em> matched if valid or {@code null} if invalid
226      */
227     public String validate(final String value) {
228         if (value == null) {
229             return null;
230         }
231         for (final Pattern pattern : patterns) {
232             final Matcher matcher = pattern.matcher(value);
233             if (matcher.matches()) {
234                 final int count = matcher.groupCount();
235                 if (count == 1) {
236                     return matcher.group(1);
237                 }
238                 final StringBuilder buffer = new StringBuilder();
239                 for (int j = 0; j < count; j++) {
240                     final String component = matcher.group(j + 1);
241                     if (component != null) {
242                         buffer.append(component);
243                     }
244                 }
245                 return buffer.toString();
246             }
247         }
248         return null;
249     }
250 
251 }