001 package org.shiftone.jrat.util.regex;
002
003
004 import org.shiftone.jrat.util.Assert;
005 import org.shiftone.jrat.util.StringUtil;
006 import org.shiftone.jrat.util.log.Logger;
007
008 import java.io.File;
009
010
011 /**
012 * Class matches simple regular expressions of the form:
013 * <li>this*
014 * <li>* is so *
015 * <li>input_file_*.dat
016 * <li>com.ml.gdfs.common.util.text.*
017 * <li>?he q???k br?wn fox
018 * <li>java 1.4.?
019 *
020 * @author jeff@shiftone.org (Jeff Drost)
021 */
022 public class GlobMatcher implements Matcher {
023
024 private static final Logger LOG = Logger.getLogger(GlobMatcher.class);
025 public static final Matcher INCLUDE_ALL = new GlobMatcher("*");
026 private char[][] patternParts = null;
027 private String pattenString;
028
029 public static Matcher create(String pattenString) {
030 return (pattenString == null) ? Matcher.ALL : new GlobMatcher(pattenString);
031 }
032
033 /**
034 * @param pattenString initializes the matcher with the glob patterm.
035 */
036 public GlobMatcher(String pattenString) {
037
038 Assert.assertNotNull("pattenString", pattenString);
039
040 this.pattenString = pattenString;
041 this.patternParts = getPatternParts(pattenString);
042 }
043
044
045 public boolean isMatch(String inputString) {
046
047 Assert.assertNotNull("inputString", inputString);
048
049 return isMatch(inputString, patternParts);
050 }
051
052
053 /**
054 * simple method to allow glob matcher to implement FilenameFilter. Matches
055 * name only.
056 */
057 public boolean accept(File dir, String name) {
058 return isMatch(name);
059 }
060
061
062 private static char[][] getPatternParts(String pattenString) {
063
064 String[] sections = StringUtil.tokenize(pattenString, "*", true);
065 char[][] patternParts = new char[sections.length][];
066
067 for (int i = 0; i < sections.length; i++) {
068 patternParts[i] = sections[i].toCharArray();
069 }
070
071 return patternParts;
072 }
073
074
075 /**
076 * <b>used by unit tests only</b>
077 */
078 public static boolean isMatch(String inputString, String pattenString) {
079 return isMatch(inputString, getPatternParts(pattenString));
080 }
081
082
083 /**
084 * Method isMatch
085 */
086 public static boolean isMatch(String input, char[][] patternParts) {
087
088 char[] in = input.toCharArray();
089 boolean canSkip = false;
090 int currentIndex = 0;
091
092 for (int i = 0; i < patternParts.length; i++) {
093 if (patternParts[i][0] == '*') {
094 canSkip = true;
095 } else {
096 if (canSkip == false) {
097 if (!matchesFixed(in, currentIndex, patternParts[i])) {
098 return false;
099 }
100 } else {
101 int m = nextFixedMatch(in, currentIndex, patternParts[i]);
102
103 if (m == -1) {
104 return false;
105 } else {
106 currentIndex = m + patternParts[i].length;
107 }
108 }
109
110 canSkip = false;
111 }
112 }
113
114 return true;
115 }
116
117
118 /**
119 * <b>used by unit tests only</b>
120 */
121 public static int nextFixedMatch(String cs, int offSet, String ps) {
122 return nextFixedMatch(cs.toCharArray(), offSet, ps.toCharArray());
123 }
124
125
126 /**
127 * Method nextFixedMatch
128 */
129 public static int nextFixedMatch(char[] cs, int offSet, char[] ps) {
130
131 int endIndex = cs.length - ps.length + 1;
132
133 for (int i = offSet; i < endIndex; i++) {
134 if (matchesFixed(cs, i, ps)) {
135 return i;
136 }
137 }
138
139 return -1;
140 }
141
142
143 /**
144 * <b>used by unit tests only</b>
145 */
146 public static boolean matchesFixed(String cs, int offSet, String ps) {
147 return matchesFixed(cs.toCharArray(), offSet, ps.toCharArray());
148 }
149
150
151 /**
152 * Method matchesFixed
153 */
154 public static boolean matchesFixed(char[] cs, int offSet, char[] ps) {
155
156 for (int i = 0; i < ps.length; i++) {
157 int c = i + offSet;
158
159 if (c >= cs.length) {
160 return false;
161 }
162
163 if ((cs[c] != ps[i]) && (ps[i] != '?')) {
164 return false;
165 }
166 }
167
168 return true;
169 }
170
171
172 public String toString() {
173 return "<glob pattern=\"" + pattenString + "\"/>";
174 }
175 }