| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: WCMatcher.java,v 1.1.1.1 2004/05/09 16:57:56 vlad_r Exp $ |
| */ |
| package com.vladium.util; |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @author Vlad Roubtsov, (C) 2002 |
| */ |
| public |
| abstract class WCMatcher |
| { |
| // public: ................................................................ |
| |
| |
| public static WCMatcher compile (final String pattern) |
| { |
| if (pattern == null) throw new IllegalArgumentException ("null input: pattern"); |
| |
| final char [] chars = pattern.toCharArray (); // is this faster than using charAt()? |
| final int charsLength = chars.length; |
| |
| if (charsLength == 0) |
| return EMPTY_MATCHER; // TODO: should be an EMPTY_MATCHER |
| else |
| { |
| int patternLength = 0, starCount = 0, questionCount = 0; |
| boolean star = false; |
| |
| for (int c = 0; c < charsLength; ++ c) |
| { |
| final char ch = chars [c]; |
| if (ch == '*') |
| { |
| if (! star) |
| { |
| star = true; |
| ++ starCount; |
| chars [patternLength ++] = '*'; |
| } |
| } |
| else |
| { |
| star = false; |
| if (ch == '?') ++ questionCount; |
| chars [patternLength ++] = ch; |
| } |
| } |
| |
| // [assertion: patternLength > 0] |
| |
| if ((starCount == 1) && (questionCount == 0)) |
| { |
| if (patternLength == 1) |
| return ALL_MATCHER; |
| else if (chars [0] == '*') |
| return new EndsWithMatcher (chars, patternLength); |
| else if (chars [patternLength - 1] == '*') |
| return new StartsWithMatcher (chars, patternLength); |
| } |
| |
| return new PatternMatcher (chars, patternLength); |
| } |
| } |
| |
| public abstract boolean matches (String s); |
| public abstract boolean matches (char [] chars); |
| |
| |
| // private boolean matches (int pi, int si, final char [] string) |
| // { |
| // System.out.println ("pi = " + pi + ", si = " + si); |
| // |
| // if (pi == m_pattern.length) |
| // return si == string.length; |
| // else |
| // { |
| // switch (m_pattern [pi]) |
| // { |
| // case '?': |
| // { |
| // return (si < string.length) && matches (pi + 1, si + 1, string); |
| // } |
| // |
| // case '*': |
| // { |
| // return matches (pi + 1, si, string) || ((si < string.length) && matches (pi, si + 1, string)); |
| // } |
| // |
| // default: |
| // { |
| // return (si < string.length) && (m_pattern [pi] == string [si]) && matches (pi + 1, si + 1, string); |
| // } |
| // |
| // } // end of switch |
| // } |
| // } |
| |
| |
| |
| // protected: ............................................................. |
| |
| // package: ............................................................... |
| |
| |
| WCMatcher () {} |
| |
| // private: ............................................................... |
| |
| |
| private static final class AllMatcher extends WCMatcher |
| { |
| public final boolean matches (final String s) |
| { |
| if (s == null) throw new IllegalArgumentException ("null input: s"); |
| |
| return true; |
| } |
| |
| public final boolean matches (final char [] chars) |
| { |
| if (chars == null) throw new IllegalArgumentException ("null input: chars"); |
| |
| return true; |
| } |
| |
| } // end of nested class |
| |
| |
| private static final class EmptyMatcher extends WCMatcher |
| { |
| public final boolean matches (final String s) |
| { |
| if (s == null) throw new IllegalArgumentException ("null input: s"); |
| |
| return false; |
| } |
| |
| public final boolean matches (final char [] chars) |
| { |
| if (chars == null) throw new IllegalArgumentException ("null input: chars"); |
| |
| return chars.length == 0; |
| } |
| |
| } // end of nested class |
| |
| |
| private static final class StartsWithMatcher extends WCMatcher |
| { |
| public final boolean matches (final String s) |
| { |
| if (s == null) throw new IllegalArgumentException ("null input: s"); |
| |
| return s.startsWith (m_prefix); |
| } |
| |
| public final boolean matches (final char [] chars) |
| { |
| if (chars == null) throw new IllegalArgumentException ("null input: chars"); |
| |
| final char [] prefixChars = m_prefixChars; |
| final int prefixLength = prefixChars.length - 1; |
| |
| if (chars.length < prefixLength) return false; |
| |
| for (int c = 0; c < prefixLength; ++ c) |
| { |
| if (chars [c] != prefixChars [c]) return false; |
| } |
| |
| return true; |
| } |
| |
| StartsWithMatcher (final char [] pattern, final int patternLength) |
| { |
| m_prefixChars = pattern; |
| m_prefix = new String (pattern, 0, patternLength - 1); |
| } |
| |
| private final char [] m_prefixChars; |
| private final String m_prefix; |
| |
| } // end of nested class |
| |
| |
| private static final class EndsWithMatcher extends WCMatcher |
| { |
| public final boolean matches (final String s) |
| { |
| if (s == null) throw new IllegalArgumentException ("null input: s"); |
| |
| return s.endsWith (m_suffix); |
| } |
| |
| public final boolean matches (final char [] chars) |
| { |
| if (chars == null) throw new IllegalArgumentException ("null input: chars"); |
| |
| final char [] suffixChars = m_suffixChars; |
| final int suffixLength = suffixChars.length - 1; |
| final int charsLength = chars.length; |
| |
| if (charsLength < suffixLength) return false; |
| |
| for (int c = 0; c < suffixLength; ++ c) |
| { |
| if (chars [charsLength - 1 - c] != suffixChars [suffixLength - c]) return false; |
| } |
| |
| return true; |
| } |
| |
| EndsWithMatcher (final char [] pattern, final int patternLength) |
| { |
| m_suffixChars = pattern; |
| m_suffix = new String (pattern, 1, patternLength - 1); |
| } |
| |
| private final char [] m_suffixChars; |
| private final String m_suffix; |
| |
| } // end of nested class |
| |
| |
| private static final class PatternMatcher extends WCMatcher |
| { |
| public final boolean matches (final String s) |
| { |
| if (s == null) throw new IllegalArgumentException ("null input: s"); |
| |
| final char [] string = s.toCharArray (); // implies an array copy; is this faster than using charAt()? |
| final int stringLength = string.length; |
| |
| final char [] pattern = m_pattern; |
| final int patternLength = m_patternLength; |
| |
| // [assertion: patternLength > 0] |
| |
| int si = 0, pi = 0; |
| boolean star = false; |
| |
| |
| next: while (true) |
| { |
| //System.out.println ("pi = " + pi + ", si = " + si); |
| |
| int i = 0; |
| for ( ; pi + i < patternLength; ++ i) |
| { |
| final char patternChar = pattern [pi + i]; |
| |
| if (patternChar == '*') |
| { |
| si += i; |
| pi += (i + 1); |
| |
| star = true; |
| continue next; |
| } |
| |
| final int si_i = si + i; |
| |
| if (si_i == stringLength) return false; |
| |
| if (patternChar != string [si_i]) |
| { |
| if (patternChar == '?') continue; |
| |
| if (! star) return false; |
| ++ si; |
| |
| continue next; |
| } |
| |
| } // end of for |
| |
| if (si + i == stringLength) return true; |
| |
| if (! star) return false; |
| ++ si; |
| |
| // [continue next;] |
| } |
| } |
| |
| |
| public final boolean matches (final char [] string) |
| { |
| if (string == null) throw new IllegalArgumentException ("null input: string"); |
| |
| final int stringLength = string.length; |
| |
| final char [] pattern = m_pattern; |
| final int patternLength = m_patternLength; |
| |
| // [assertion: patternLength > 0] |
| |
| int si = 0, pi = 0; |
| boolean star = false; |
| |
| |
| next: while (true) |
| { |
| //System.out.println ("pi = " + pi + ", si = " + si); |
| |
| int i = 0; |
| for ( ; pi + i < patternLength; ++ i) |
| { |
| final char patternChar = pattern [pi + i]; |
| |
| if (patternChar == '*') |
| { |
| si += i; |
| pi += (i + 1); |
| |
| star = true; |
| continue next; |
| } |
| |
| final int si_i = si + i; |
| |
| if (si_i == stringLength) return false; |
| |
| if (patternChar != string [si_i]) |
| { |
| if (patternChar == '?') continue; |
| |
| if (! star) return false; |
| ++ si; |
| |
| continue next; |
| } |
| |
| } // end of for |
| |
| if (si + i == stringLength) return true; |
| |
| if (! star) return false; |
| ++ si; |
| |
| // [continue next;] |
| } |
| } |
| |
| PatternMatcher (final char [] pattern, final int patternLength) |
| { |
| m_pattern = pattern; |
| m_patternLength = patternLength; |
| } |
| |
| |
| private final char [] m_pattern; |
| private final int m_patternLength; |
| |
| } // end of nested class |
| |
| |
| private static final WCMatcher ALL_MATCHER = new AllMatcher (); |
| private static final WCMatcher EMPTY_MATCHER = new EmptyMatcher (); |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |