1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: WCMatcher.java,v 1.1.1.1 2004/05/09 16:57:56 vlad_r Exp $ 8 */ 9 package com.vladium.util; 10 11 // ---------------------------------------------------------------------------- 12 /** 13 * @author Vlad Roubtsov, (C) 2002 14 */ 15 public 16 abstract class WCMatcher 17 { 18 // public: ................................................................ 19 20 21 public static WCMatcher compile (final String pattern) 22 { 23 if (pattern == null) throw new IllegalArgumentException ("null input: pattern"); 24 25 final char [] chars = pattern.toCharArray (); // is this faster than using charAt()? 26 final int charsLength = chars.length; 27 28 if (charsLength == 0) 29 return EMPTY_MATCHER; // TODO: should be an EMPTY_MATCHER 30 else 31 { 32 int patternLength = 0, starCount = 0, questionCount = 0; 33 boolean star = false; 34 35 for (int c = 0; c < charsLength; ++ c) 36 { 37 final char ch = chars [c]; 38 if (ch == '*') 39 { 40 if (! star) 41 { 42 star = true; 43 ++ starCount; 44 chars [patternLength ++] = '*'; 45 } 46 } 47 else 48 { 49 star = false; 50 if (ch == '?') ++ questionCount; 51 chars [patternLength ++] = ch; 52 } 53 } 54 55 // [assertion: patternLength > 0] 56 57 if ((starCount == 1) && (questionCount == 0)) 58 { 59 if (patternLength == 1) 60 return ALL_MATCHER; 61 else if (chars [0] == '*') 62 return new EndsWithMatcher (chars, patternLength); 63 else if (chars [patternLength - 1] == '*') 64 return new StartsWithMatcher (chars, patternLength); 65 } 66 67 return new PatternMatcher (chars, patternLength); 68 } 69 } 70 71 public abstract boolean matches (String s); 72 public abstract boolean matches (char [] chars); 73 74 75 // private boolean matches (int pi, int si, final char [] string) 76 // { 77 // System.out.println ("pi = " + pi + ", si = " + si); 78 // 79 // if (pi == m_pattern.length) 80 // return si == string.length; 81 // else 82 // { 83 // switch (m_pattern [pi]) 84 // { 85 // case '?': 86 // { 87 // return (si < string.length) && matches (pi + 1, si + 1, string); 88 // } 89 // 90 // case '*': 91 // { 92 // return matches (pi + 1, si, string) || ((si < string.length) && matches (pi, si + 1, string)); 93 // } 94 // 95 // default: 96 // { 97 // return (si < string.length) && (m_pattern [pi] == string [si]) && matches (pi + 1, si + 1, string); 98 // } 99 // 100 // } // end of switch 101 // } 102 // } 103 104 105 106 // protected: ............................................................. 107 108 // package: ............................................................... 109 110 111 WCMatcher () {} 112 113 // private: ............................................................... 114 115 116 private static final class AllMatcher extends WCMatcher 117 { 118 public final boolean matches (final String s) 119 { 120 if (s == null) throw new IllegalArgumentException ("null input: s"); 121 122 return true; 123 } 124 125 public final boolean matches (final char [] chars) 126 { 127 if (chars == null) throw new IllegalArgumentException ("null input: chars"); 128 129 return true; 130 } 131 132 } // end of nested class 133 134 135 private static final class EmptyMatcher extends WCMatcher 136 { 137 public final boolean matches (final String s) 138 { 139 if (s == null) throw new IllegalArgumentException ("null input: s"); 140 141 return false; 142 } 143 144 public final boolean matches (final char [] chars) 145 { 146 if (chars == null) throw new IllegalArgumentException ("null input: chars"); 147 148 return chars.length == 0; 149 } 150 151 } // end of nested class 152 153 154 private static final class StartsWithMatcher extends WCMatcher 155 { 156 public final boolean matches (final String s) 157 { 158 if (s == null) throw new IllegalArgumentException ("null input: s"); 159 160 return s.startsWith (m_prefix); 161 } 162 163 public final boolean matches (final char [] chars) 164 { 165 if (chars == null) throw new IllegalArgumentException ("null input: chars"); 166 167 final char [] prefixChars = m_prefixChars; 168 final int prefixLength = prefixChars.length - 1; 169 170 if (chars.length < prefixLength) return false; 171 172 for (int c = 0; c < prefixLength; ++ c) 173 { 174 if (chars [c] != prefixChars [c]) return false; 175 } 176 177 return true; 178 } 179 180 StartsWithMatcher (final char [] pattern, final int patternLength) 181 { 182 m_prefixChars = pattern; 183 m_prefix = new String (pattern, 0, patternLength - 1); 184 } 185 186 private final char [] m_prefixChars; 187 private final String m_prefix; 188 189 } // end of nested class 190 191 192 private static final class EndsWithMatcher extends WCMatcher 193 { 194 public final boolean matches (final String s) 195 { 196 if (s == null) throw new IllegalArgumentException ("null input: s"); 197 198 return s.endsWith (m_suffix); 199 } 200 201 public final boolean matches (final char [] chars) 202 { 203 if (chars == null) throw new IllegalArgumentException ("null input: chars"); 204 205 final char [] suffixChars = m_suffixChars; 206 final int suffixLength = suffixChars.length - 1; 207 final int charsLength = chars.length; 208 209 if (charsLength < suffixLength) return false; 210 211 for (int c = 0; c < suffixLength; ++ c) 212 { 213 if (chars [charsLength - 1 - c] != suffixChars [suffixLength - c]) return false; 214 } 215 216 return true; 217 } 218 219 EndsWithMatcher (final char [] pattern, final int patternLength) 220 { 221 m_suffixChars = pattern; 222 m_suffix = new String (pattern, 1, patternLength - 1); 223 } 224 225 private final char [] m_suffixChars; 226 private final String m_suffix; 227 228 } // end of nested class 229 230 231 private static final class PatternMatcher extends WCMatcher 232 { 233 public final boolean matches (final String s) 234 { 235 if (s == null) throw new IllegalArgumentException ("null input: s"); 236 237 final char [] string = s.toCharArray (); // implies an array copy; is this faster than using charAt()? 238 final int stringLength = string.length; 239 240 final char [] pattern = m_pattern; 241 final int patternLength = m_patternLength; 242 243 // [assertion: patternLength > 0] 244 245 int si = 0, pi = 0; 246 boolean star = false; 247 248 249 next: while (true) 250 { 251 //System.out.println ("pi = " + pi + ", si = " + si); 252 253 int i = 0; 254 for ( ; pi + i < patternLength; ++ i) 255 { 256 final char patternChar = pattern [pi + i]; 257 258 if (patternChar == '*') 259 { 260 si += i; 261 pi += (i + 1); 262 263 star = true; 264 continue next; 265 } 266 267 final int si_i = si + i; 268 269 if (si_i == stringLength) return false; 270 271 if (patternChar != string [si_i]) 272 { 273 if (patternChar == '?') continue; 274 275 if (! star) return false; 276 ++ si; 277 278 continue next; 279 } 280 281 } // end of for 282 283 if (si + i == stringLength) return true; 284 285 if (! star) return false; 286 ++ si; 287 288 // [continue next;] 289 } 290 } 291 292 293 public final boolean matches (final char [] string) 294 { 295 if (string == null) throw new IllegalArgumentException ("null input: string"); 296 297 final int stringLength = string.length; 298 299 final char [] pattern = m_pattern; 300 final int patternLength = m_patternLength; 301 302 // [assertion: patternLength > 0] 303 304 int si = 0, pi = 0; 305 boolean star = false; 306 307 308 next: while (true) 309 { 310 //System.out.println ("pi = " + pi + ", si = " + si); 311 312 int i = 0; 313 for ( ; pi + i < patternLength; ++ i) 314 { 315 final char patternChar = pattern [pi + i]; 316 317 if (patternChar == '*') 318 { 319 si += i; 320 pi += (i + 1); 321 322 star = true; 323 continue next; 324 } 325 326 final int si_i = si + i; 327 328 if (si_i == stringLength) return false; 329 330 if (patternChar != string [si_i]) 331 { 332 if (patternChar == '?') continue; 333 334 if (! star) return false; 335 ++ si; 336 337 continue next; 338 } 339 340 } // end of for 341 342 if (si + i == stringLength) return true; 343 344 if (! star) return false; 345 ++ si; 346 347 // [continue next;] 348 } 349 } 350 351 PatternMatcher (final char [] pattern, final int patternLength) 352 { 353 m_pattern = pattern; 354 m_patternLength = patternLength; 355 } 356 357 358 private final char [] m_pattern; 359 private final int m_patternLength; 360 361 } // end of nested class 362 363 364 private static final WCMatcher ALL_MATCHER = new AllMatcher (); 365 private static final WCMatcher EMPTY_MATCHER = new EmptyMatcher (); 366 367 } // end of class 368 // ----------------------------------------------------------------------------