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    }