001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.openstreetmap.josm.data.validation.routines; 018 019import java.util.regex.Matcher; 020import java.util.regex.Pattern; 021 022/** 023 * <b>Regular Expression</b> validation (using JDK 1.4+ regex support). 024 * <p> 025 * Construct the validator either for a single regular expression or a set (array) of 026 * regular expressions. By default validation is <i>case sensitive</i> but constructors 027 * are provided to allow <i>case in-sensitive</i> validation. For example to create 028 * a validator which does <i>case in-sensitive</i> validation for a set of regular 029 * expressions: 030 * </p> 031 * <pre> 032 * <code> 033 * String[] regexs = new String[] {...}; 034 * RegexValidator validator = new RegexValidator(regexs, false); 035 * </code> 036 * </pre> 037 * 038 * <ul> 039 * <li>Validate <code>true</code> or <code>false</code>:</li> 040 * <li> 041 * <ul> 042 * <li><code>boolean valid = validator.isValid(value);</code></li> 043 * </ul> 044 * </li> 045 * <li>Validate returning an aggregated String of the matched groups:</li> 046 * <li> 047 * <ul> 048 * <li><code>String result = validator.validate(value);</code></li> 049 * </ul> 050 * </li> 051 * <li>Validate returning the matched groups:</li> 052 * <li> 053 * <ul> 054 * <li><code>String[] result = validator.match(value);</code></li> 055 * </ul> 056 * </li> 057 * </ul> 058 * 059 * <b>Note that patterns are matched against the entire input.</b> 060 * 061 * <p> 062 * Cached instances pre-compile and re-use {@link Pattern}(s) - which according 063 * to the {@link Pattern} API are safe to use in a multi-threaded environment. 064 * </p> 065 * 066 * @version $Revision: 1741724 $ 067 * @since Validator 1.4 068 */ 069public class RegexValidator extends AbstractValidator { 070 071 private final Pattern[] patterns; 072 073 /** 074 * Construct a <i>case sensitive</i> validator for a single 075 * regular expression. 076 * 077 * @param regex The regular expression this validator will 078 * validate against 079 */ 080 public RegexValidator(String regex) { 081 this(regex, true); 082 } 083 084 /** 085 * Construct a validator for a single regular expression 086 * with the specified case sensitivity. 087 * 088 * @param regex The regular expression this validator will 089 * validate against 090 * @param caseSensitive when <code>true</code> matching is <i>case 091 * sensitive</i>, otherwise matching is <i>case in-sensitive</i> 092 */ 093 public RegexValidator(String regex, boolean caseSensitive) { 094 this(new String[] {regex}, caseSensitive); 095 } 096 097 /** 098 * Construct a <i>case sensitive</i> validator that matches any one 099 * of the set of regular expressions. 100 * 101 * @param regexs The set of regular expressions this validator will 102 * validate against 103 */ 104 public RegexValidator(String ... regexs) { 105 this(regexs, true); 106 } 107 108 /** 109 * Construct a validator that matches any one of the set of regular 110 * expressions with the specified case sensitivity. 111 * 112 * @param regexs The set of regular expressions this validator will 113 * validate against 114 * @param caseSensitive when <code>true</code> matching is <i>case 115 * sensitive</i>, otherwise matching is <i>case in-sensitive</i> 116 */ 117 public RegexValidator(String[] regexs, boolean caseSensitive) { 118 if (regexs == null || regexs.length == 0) { 119 throw new IllegalArgumentException("Regular expressions are missing"); 120 } 121 patterns = new Pattern[regexs.length]; 122 int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE; 123 for (int i = 0; i < regexs.length; i++) { 124 if (regexs[i] == null || regexs[i].isEmpty()) { 125 throw new IllegalArgumentException("Regular expression[" + i + "] is missing"); 126 } 127 patterns[i] = Pattern.compile(regexs[i], flags); 128 } 129 } 130 131 /** 132 * Validate a value against the set of regular expressions. 133 * 134 * @param value The value to validate. 135 * @return <code>true</code> if the value is valid 136 * otherwise <code>false</code>. 137 */ 138 @Override 139 public boolean isValid(String value) { 140 if (value == null) { 141 return false; 142 } 143 for (int i = 0; i < patterns.length; i++) { 144 if (patterns[i].matcher(value).matches()) { 145 return true; 146 } 147 } 148 return false; 149 } 150 151 @Override 152 public String getValidatorName() { 153 return null; 154 } 155 156 /** 157 * Validate a value against the set of regular expressions 158 * returning the array of matched groups. 159 * 160 * @param value The value to validate. 161 * @return String array of the <i>groups</i> matched if 162 * valid or <code>null</code> if invalid 163 */ 164 public String[] match(String value) { 165 if (value == null) { 166 return null; 167 } 168 for (int i = 0; i < patterns.length; i++) { 169 Matcher matcher = patterns[i].matcher(value); 170 if (matcher.matches()) { 171 int count = matcher.groupCount(); 172 String[] groups = new String[count]; 173 for (int j = 0; j < count; j++) { 174 groups[j] = matcher.group(j+1); 175 } 176 return groups; 177 } 178 } 179 return null; 180 } 181 182 183 /** 184 * Validate a value against the set of regular expressions 185 * returning a String value of the aggregated groups. 186 * 187 * @param value The value to validate. 188 * @return Aggregated String value comprised of the 189 * <i>groups</i> matched if valid or <code>null</code> if invalid 190 */ 191 public String validate(String value) { 192 if (value == null) { 193 return null; 194 } 195 for (int i = 0; i < patterns.length; i++) { 196 Matcher matcher = patterns[i].matcher(value); 197 if (matcher.matches()) { 198 int count = matcher.groupCount(); 199 if (count == 1) { 200 return matcher.group(1); 201 } 202 StringBuilder buffer = new StringBuilder(); 203 for (int j = 0; j < count; j++) { 204 String component = matcher.group(j+1); 205 if (component != null) { 206 buffer.append(component); 207 } 208 } 209 return buffer.toString(); 210 } 211 } 212 return null; 213 } 214 215 /** 216 * Provide a String representation of this validator. 217 * @return A String representation of this validator 218 */ 219 @Override 220 public String toString() { 221 StringBuilder buffer = new StringBuilder(32); 222 buffer.append("RegexValidator{"); 223 for (int i = 0; i < patterns.length; i++) { 224 if (i > 0) { 225 buffer.append(','); 226 } 227 buffer.append(patterns[i].pattern()); 228 } 229 buffer.append('}'); 230 return buffer.toString(); 231 } 232}