001: /*
002: * Glob.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 1999 Sun Microsystems, Inc.
007: *
008: * Sun Public License Notice
009: *
010: * The contents of this file are subject to the Sun Public License Version
011: * 1.0 (the "License"). You may not use this file except in compliance with
012: * the License. A copy of the License is included as the file "license.terms",
013: * and also available at http://www.sun.com/
014: *
015: * The Original Code is from:
016: * Brazil project web application Framework release 1.1.
017: * The Initial Developer of the Original Code is: suhler.
018: * Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
019: * All Rights Reserved.
020: *
021: * Contributor(s): cstevens, suhler.
022: *
023: * Version: 1.6
024: * Created by suhler on 99/08/05
025: * Last modified by cstevens on 99/11/09 20:24:39
026: */
027:
028: package sunlabs.brazil.util;
029:
030: /**
031: * Glob-style string matching and substring extraction. Glob was
032: * implemented by translating the glob package for
033: * <a href="http://www.scriptics.com">tcl8.0</a>.
034: * <ul>
035: * <li> "*" matches 0 or more characters
036: * <li> "?" matches a single character
037: * <li> "[...]" matches a set and/or range of characters
038: * <li> "\" following character is not special
039: * </ul>
040: *
041: * Each of the substrings matching (?, *, or [..]) are returned.
042: *
043: * @author Colin Stevens (colin.stevens@sun.com)
044: * @version 1.6, 99/11/09
045: */
046: public class Glob {
047: private Glob() {
048: }
049:
050: /**
051: * Match a string against a pattern.
052: *
053: * @param pattern
054: * Glob pattern.
055: *
056: * @param string
057: * String to match against pattern.
058: *
059: * @return <code>true</code> if the string matched the pattern,
060: * <code>false</code> otherwise.
061: */
062: public static boolean match(String pattern, String string) {
063: return match(pattern, string, null);
064: }
065:
066: /**
067: * Match a string against a pattern, and return sub-matches.
068: * <p>
069: * The caller can provide an array of strings that will be filled in with
070: * the substrings of <code>string</code> that matched the glob
071: * meta-characters in <code>pattern</code>. The array of strings may be
072: * partially modified even if the string did not match the glob pattern.
073: * The array may contain more elements than glob meta-characters, in
074: * which case those extra elements will not be modified; the array may
075: * also contain fewer elements or even be <code>null</code>, to ignore
076: * some or all of the glob meta-characters. In other words, the user can
077: * pass pretty much anything and this method defines errors out of
078: * existence.
079: *
080: * @param pattern
081: * Glob pattern.
082: *
083: * @param string
084: * String to match against pattern.
085: *
086: * @param substr
087: * Array of strings provided by the caller, to be filled in
088: * with the substrings that matched the glob meta-characters.
089: * May be <code>null</code>.
090: *
091: * @return <code>true</code> if the string matched the pattern,
092: * <code>false</code> otherwise.
093: */
094: public static boolean match(String pattern, String string,
095: String[] substr) {
096: return match(pattern, 0, string, 0, substr, 0);
097: }
098:
099: private static boolean match(String pat, int pIndex, String str,
100: int sIndex, String[] substrs, int subIndex) {
101: int pLen = pat.length();
102: int sLen = str.length();
103:
104: while (true) {
105: if (pIndex == pLen) {
106: if (sIndex == sLen) {
107: return true;
108: } else {
109: return false;
110: }
111: } else if ((sIndex == sLen) && (pat.charAt(pIndex) != '*')) {
112: return false;
113: }
114:
115: switch (pat.charAt(pIndex)) {
116: case '*': {
117: int start = sIndex;
118: pIndex++;
119: if (pIndex >= pLen) {
120: addMatch(str, start, sLen, substrs, subIndex);
121: return true;
122: }
123: while (true) {
124: if (match(pat, pIndex, str, sIndex, substrs,
125: subIndex + 1)) {
126: addMatch(str, start, sIndex, substrs, subIndex);
127: return true;
128: }
129: if (sIndex == sLen) {
130: return false;
131: }
132: sIndex++;
133: }
134: }
135: case '?': {
136: pIndex++;
137: addMatch(str, sIndex, sIndex + 1, substrs, subIndex++);
138: sIndex++;
139: break;
140: }
141: case '[': {
142: try {
143: pIndex++;
144: char s = str.charAt(sIndex);
145: char p = pat.charAt(pIndex);
146:
147: while (true) {
148: if (p == ']') {
149: return false;
150: }
151: if (p == s) {
152: break;
153: }
154: pIndex++;
155: char next = pat.charAt(pIndex);
156: if (next == '-') {
157: pIndex++;
158: char p2 = pat.charAt(pIndex);
159: if ((p <= s) && (s <= p2)) {
160: break;
161: }
162: pIndex++;
163: next = pat.charAt(pIndex);
164: }
165: p = next;
166: }
167: pIndex = pat.indexOf(']', pIndex) + 1;
168: if (pIndex <= 0) {
169: return false;
170: }
171: addMatch(str, sIndex, sIndex + 1, substrs,
172: subIndex++);
173: sIndex++;
174: } catch (StringIndexOutOfBoundsException e) {
175: /*
176: * Easier just to catch malformed [] sequences than
177: * to check bounds all the time.
178: */
179:
180: return false;
181: }
182: break;
183: }
184: case '\\': {
185: pIndex++;
186: if (pIndex >= pLen) {
187: return false;
188: }
189: // fall through
190: }
191: default: {
192: if (pat.charAt(pIndex) != str.charAt(sIndex)) {
193: return false;
194: }
195: pIndex++;
196: sIndex++;
197: }
198: }
199: }
200: }
201:
202: private static void addMatch(String str, int start, int end,
203: String[] substrs, int subIndex) {
204: if ((substrs == null) || (subIndex >= substrs.length)) {
205: return;
206: }
207:
208: substrs[subIndex] = str.substring(start, end);
209: }
210: }
|