1 package net.sourceforge.phpdt.externaltools.model;
2 /**********************************************************************
3 Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
4 This file is made available under the terms of the Common Public License v1.0
5 which accompanies this distribution, and is available at
6 http://www.eclipse.org/legal/cpl-v10.html
7 **********************************************************************/
12 * Copied from org.eclipse.jdt.internal.ui.util.StringMatcher
14 * A string pattern matcher, suppporting * and ? wildcards.
16 public class StringMatcher {
17 protected String fPattern;
18 protected int fLength; // pattern length
19 protected boolean fIgnoreWildCards;
20 protected boolean fIgnoreCase;
21 protected boolean fHasLeadingStar;
22 protected boolean fHasTrailingStar;
23 protected String fSegments[]; //the given pattern is split into * separated segments
25 /* boundary value beyond which we don't need to search in the text */
26 protected int fBound= 0;
29 protected static final char fSingleWildCard= '\u0000';
31 public static class Position {
32 int start; //inclusive
34 public Position(int start, int end) {
38 public int getStart() {
46 * StringMatcher constructor takes in a String object that is a simple
47 * pattern which may contain * for 0 and many characters and
48 * ? for exactly one character.
50 * Literal '*' and '?' characters must be escaped in the pattern
51 * e.g., "\*" means literal "*", etc.
53 * Escaping any other character (including the escape character itself),
54 * just results in that character in the pattern.
55 * e.g., "\a" means "a" and "\\" means "\"
57 * If invoking the StringMatcher with string literals in Java, don't forget
58 * escape characters are represented by "\\".
60 * @param pattern the pattern to match text against
61 * @param ignoreCase if true, case is ignored
62 * @param ignoreWildCards if true, wild cards and their escape sequences are ignored
63 * (everything is taken literally).
65 public StringMatcher(String pattern, boolean ignoreCase, boolean ignoreWildCards) {
67 throw new IllegalArgumentException();
68 fIgnoreCase= ignoreCase;
69 fIgnoreWildCards= ignoreWildCards;
71 fLength= pattern.length();
73 if (fIgnoreWildCards) {
80 * Find the first occurrence of the pattern between <code>start</code)(inclusive)
81 * and <code>end</code>(exclusive).
82 * @param <code>text</code>, the String object to search in
83 * @param <code>start</code>, the starting index of the search range, inclusive
84 * @param <code>end</code>, the ending index of the search range, exclusive
85 * @return an <code>StringMatcher.Position</code> object that keeps the starting
86 * (inclusive) and ending positions (exclusive) of the first occurrence of the
87 * pattern in the specified range of the text; return null if not found or subtext
88 * is empty (start==end). A pair of zeros is returned if pattern is empty string
89 * Note that for pattern like "*abc*" with leading and trailing stars, position of "abc"
90 * is returned. For a pattern like"*??*" in text "abcdf", (1,3) is returned
92 public StringMatcher.Position find(String text, int start, int end) {
94 throw new IllegalArgumentException();
96 int tlen= text.length();
101 if (end < 0 ||start >= end )
104 return new Position(start, start);
105 if (fIgnoreWildCards) {
106 int x= posIn(text, start, end);
109 return new Position(x, x+fLength);
112 int segCount= fSegments.length;
113 if (segCount == 0)//pattern contains only '*'(s)
114 return new Position (start, end);
119 for (i= 0; i < segCount && curPos < end; ++i) {
120 String current= fSegments[i];
121 int nextMatch= regExpPosIn(text, curPos, end, current);
125 matchStart= nextMatch;
126 curPos= nextMatch + current.length();
130 return new Position(matchStart, curPos);
133 * match the given <code>text</code> with the pattern
134 * @return true if matched eitherwise false
135 * @param <code>text</code>, a String object
137 public boolean match(String text) {
138 return match(text, 0, text.length());
141 * Given the starting (inclusive) and the ending (exclusive) positions in the
142 * <code>text</code>, determine if the given substring matches with aPattern
143 * @return true if the specified portion of the text matches the pattern
144 * @param String <code>text</code>, a String object that contains the substring to match
145 * @param int <code>start<code> marks the starting position (inclusive) of the substring
146 * @param int <code>end<code> marks the ending index (exclusive) of the substring
148 public boolean match(String text, int start, int end) {
150 throw new IllegalArgumentException();
155 if (fIgnoreWildCards)
156 return (end - start == fLength) && fPattern.regionMatches(fIgnoreCase, 0, text, start, fLength);
157 int segCount= fSegments.length;
158 if (segCount == 0 && (fHasLeadingStar || fHasTrailingStar)) // pattern contains only '*'(s)
165 int tlen= text.length();
172 int bound= end - fBound;
176 String current= fSegments[i];
177 int segLength= current.length();
179 /* process first segment */
180 if (!fHasLeadingStar){
181 if(!regExpRegionMatches(text, start, current, 0, segLength)) {
185 tCurPos= tCurPos + segLength;
189 /* process middle segments */
190 while (i < segCount) {
191 current= fSegments[i];
193 int k= current.indexOf(fSingleWildCard);
195 currentMatch= textPosIn(text, tCurPos, end, current);
196 if (currentMatch < 0)
199 currentMatch= regExpPosIn(text, tCurPos, end, current);
200 if (currentMatch < 0)
203 tCurPos= currentMatch + current.length();
207 /* process final segment */
208 if (!fHasTrailingStar && tCurPos != end) {
209 int clen= current.length();
210 return regExpRegionMatches(text, end - clen, current, 0, clen);
212 return i == segCount ;
215 * This method parses the given pattern into segments seperated by wildcard '*' characters.
216 * Since wildcards are not being used in this case, the pattern consists of a single segment.
218 private void parseNoWildCards() {
219 fSegments= new String[1];
220 fSegments[0]= fPattern;
224 * Parses the given pattern into segments seperated by wildcard '*' characters.
225 * @param p, a String object that is a simple regular expression with * and/or ?
227 private void parseWildCards() {
228 if(fPattern.startsWith("*"))//$NON-NLS-1$
229 fHasLeadingStar= true;
230 if(fPattern.endsWith("*")) {//$NON-NLS-1$
231 /* make sure it's not an escaped wildcard */
232 if (fLength > 1 && fPattern.charAt(fLength - 2) != '\\') {
233 fHasTrailingStar= true;
237 Vector temp= new Vector();
240 StringBuffer buf= new StringBuffer();
241 while (pos < fLength) {
242 char c= fPattern.charAt(pos++);
245 if (pos >= fLength) {
248 char next= fPattern.charAt(pos++);
249 /* if it's an escape sequence */
250 if (next == '*' || next == '?' || next == '\\') {
253 /* not an escape sequence, just insert literally */
260 if (buf.length() > 0) {
262 temp.addElement(buf.toString());
263 fBound += buf.length();
268 /* append special character representing single match wildcard */
269 buf.append(fSingleWildCard);
276 /* add last buffer to segment list */
277 if (buf.length() > 0) {
278 temp.addElement(buf.toString());
279 fBound += buf.length();
282 fSegments= new String[temp.size()];
283 temp.copyInto(fSegments);
286 * @param <code>text</code>, a string which contains no wildcard
287 * @param <code>start</code>, the starting index in the text for search, inclusive
288 * @param <code>end</code>, the stopping point of search, exclusive
289 * @return the starting index in the text of the pattern , or -1 if not found
291 protected int posIn(String text, int start, int end) {//no wild card in pattern
292 int max= end - fLength;
295 int i= text.indexOf(fPattern, start);
296 if (i == -1 || i > max)
301 for (int i= start; i <= max; ++i) {
302 if (text.regionMatches(true, i, fPattern, 0, fLength))
309 * @param <code>text</code>, a simple regular expression that may only contain '?'(s)
310 * @param <code>start</code>, the starting index in the text for search, inclusive
311 * @param <code>end</code>, the stopping point of search, exclusive
312 * @param <code>p</code>, a simple regular expression that may contains '?'
313 * @param <code>caseIgnored</code>, wether the pattern is not casesensitive
314 * @return the starting index in the text of the pattern , or -1 if not found
316 protected int regExpPosIn(String text, int start, int end, String p) {
317 int plen= p.length();
320 for (int i= start; i <= max; ++i) {
321 if (regExpRegionMatches(text, i, p, 0, plen))
329 * @param <code>text</code>, a String to match
330 * @param <code>start</code>, int that indicates the starting index of match, inclusive
331 * @param <code>end</code> int that indicates the ending index of match, exclusive
332 * @param <code>p</code>, String, String, a simple regular expression that may contain '?'
333 * @param <code>ignoreCase</code>, boolean indicating wether code>p</code> is case sensitive
335 protected boolean regExpRegionMatches(String text, int tStart, String p, int pStart, int plen) {
337 char tchar= text.charAt(tStart++);
338 char pchar= p.charAt(pStart++);
340 /* process wild cards */
341 if (!fIgnoreWildCards) {
342 /* skip single wild cards */
343 if (pchar == fSingleWildCard) {
350 if (Character.toUpperCase(tchar) == Character.toUpperCase(pchar))
352 // comparing after converting to upper case doesn't handle all cases;
353 // also compare after converting to lower case
354 if (Character.toLowerCase(tchar) == Character.toLowerCase(pchar))
362 * @param <code>text</code>, the string to match
363 * @param <code>start</code>, the starting index in the text for search, inclusive
364 * @param <code>end</code>, the stopping point of search, exclusive
365 * @param code>p</code>, a string that has no wildcard
366 * @param <code>ignoreCase</code>, boolean indicating wether code>p</code> is case sensitive
367 * @return the starting index in the text of the pattern , or -1 if not found
369 protected int textPosIn(String text, int start, int end, String p) {
371 int plen= p.length();
375 int i= text.indexOf(p, start);
376 if (i == -1 || i > max)
381 for (int i= start; i <= max; ++i) {
382 if (text.regionMatches(true, i, p, 0, plen))