2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package gov.nasa.jpf.util;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
24 * simple utility that can be used to check for string matches in
25 * sets with '*' wildcards, e.g. to check for class name lists such as
27 * vm.halt_on_throw=java.lang.reflect.*:my.own.Exception
29 * Only meta chars in patterns are '*' and '!', i.e. '.' is a regular char to match
30 * A '!' prefix inverts the match
32 public class StringSetMatcher {
34 public static final char WILDCARD = '*';
35 public static final char INVERTED = '!';
37 boolean hasAnyPattern; // do we have a universal '*' pattern?
44 * convenience method for matcher pairs containing of explicit excludes and
47 public static boolean isMatch (String s, StringSetMatcher includes, StringSetMatcher excludes){
48 if (excludes != null) {
49 if (excludes.matchesAny(s)){
54 if (includes != null) {
55 if (!includes.matchesAny(s)){
63 public static StringSetMatcher getNonEmpty(String[] set){
64 if (set != null && set.length > 0){
65 return new StringSetMatcher(set);
71 public StringSetMatcher (String... set){
73 pattern = new Pattern[n];
74 matcher = new Matcher[n];
75 inverted = new boolean[n];
77 for (int i=0; i<n; i++){
82 // no need to compile this
85 Pattern p = createPattern(s);
87 matcher[i] = p.matcher(""); // gets reset upon use
88 inverted[i] = isInverted(s);
94 public String toString() {
96 StringBuilder sb = new StringBuilder(64);
97 sb.append("StringSetMatcher {patterns=");
104 for (int i=0; i<pattern.length; i++) {
105 if (pattern[i] != null) {
112 sb.append(pattern[i]);
116 return sb.toString();
119 public void addPattern (String s){
121 if (s.equals("*")) { // no need to compile
122 // note that this doesn't include the - pointless - "!*", which would match nothing
123 hasAnyPattern = true;
126 int n = pattern.length;
128 Pattern[] pNew = new Pattern[n+1];
129 System.arraycopy(pattern, 0, pNew, 0, n);
130 pNew[n] = createPattern(s);
132 Matcher[] mNew = new Matcher[pNew.length];
133 System.arraycopy(matcher, 0, mNew, 0, n);
134 mNew[n] = pNew[n].matcher("");
136 boolean[] iNew = new boolean[pNew.length];
137 System.arraycopy( inverted, 0, iNew, 0, n);
138 iNew[n] = isInverted(s);
146 public static boolean isInverted (String s){
147 return (!s.isEmpty() && s.charAt(0) == INVERTED);
150 protected Pattern createPattern (String s){
153 int len = s.length();
155 // inversion is better done outside of regex
156 if ((len > 0) && s.charAt(0) == INVERTED){
157 j++; // skip INVERTED char
160 StringBuilder sb = new StringBuilder();
163 char c = s.charAt(j);
165 case '.' : sb.append("\\."); break;
166 case '$' : sb.append("\\$"); break;
167 case '[' : sb.append("\\["); break;
168 case ']' : sb.append("\\]"); break;
169 case '*' : sb.append(".*"); break;
170 case '(' : sb.append("\\("); break;
171 case ')' : sb.append("\\)"); break;
172 // <2do> and probably more..
173 default: sb.append(c);
177 p = Pattern.compile(sb.toString());
182 * does 's' match at least one of our patterns
184 public boolean matchesAny (String s){
187 return true; // no need to check
190 for (int i=0; i<matcher.length; i++){
191 Matcher m = matcher[i];
194 if (m.matches() != inverted[i]){
204 * does 's' match ALL of our patterns
206 public boolean matchesAll (String s){
208 if (hasAnyPattern && pattern.length == 1) { // there might be other patterns
209 return true; // no need to check
212 for (int i=0; i<pattern.length; i++){
213 Pattern p = pattern[i];
215 Matcher m = matcher[i];
218 if (m.matches() == inverted[i]){
236 * do all elements of 'set' match at least one of our patterns?
238 public boolean allMatch (String[] set){
243 for (int i=0; i<set.length; i++){
244 if (!matchesAny(set[i])){
252 public static void main (String[] args){
253 String[] p = args[0].split(":");
254 String[] s = args[1].split(":");
256 StringSetMatcher sm = new StringSetMatcher(p);
257 if (sm.matchesAny(s[0])){
258 System.out.println("Bingo, \"" + s[0] + "\" matches " + sm);
260 System.out.println("nope, \"" + s[0] + "\" doesn't match " + sm);