001: /**
002: * Copyright (c) 2001, Sergey A. Samokhodkin
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without modification,
006: * are permitted provided that the following conditions are met:
007: *
008: * - Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * - Redistributions in binary form
011: * must reproduce the above copyright notice, this list of conditions and the following
012: * disclaimer in the documentation and/or other materials provided with the distribution.
013: * - Neither the name of jregex nor the names of its contributors may be used
014: * to endorse or promote products derived from this software without specific prior
015: * written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
018: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
019: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
020: * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
021: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
022: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
023: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
025: * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
026: *
027: * @version 1.2_01
028: */package jregex.util.io;
029:
030: import jregex.*;
031: import java.io.FilenameFilter;
032: import java.io.File;
033: import java.util.Enumeration;
034: import java.util.Vector;
035:
036: /**
037: * A special-purpose subclass of the Pattern class.
038: * Has two different applications:
039: * <li> to search files by their paths using special patterns;
040: * <li> to match path strings
041: * Syntax:
042: * <li><code><b>?</b></code> - any character but path separator
043: * <li><code><b>*</b></code> - any string no including path separators
044: * <li><code><b>**</b></code> - any path<br>
045: * <br>
046: * Usage:<pre>
047: * PathPattern pp=new PathPattern("jregex/**"); //all files and directories
048: * //under the jregex directory
049: * Enumeration files=pp.enumerateFiles();
050: * Matcher m=pp.matcher();
051: * while(files.hasMoreElements()){
052: * File f=(File)files.nextElement();
053: * m.setTarget(f.getPath());
054: * if(!m.matches()) System.out.println("Error in jregex.io.PathPattern");
055: * }
056: * </pre>
057: * @see jregex.WildcardPattern
058: */
059:
060: public class PathPattern extends Pattern {
061: private static final int RESERVED = 1;
062: private static int GRP_NO = RESERVED + 1;
063: private static final int ANY_G = GRP_NO++;
064: private static final int FS_G = GRP_NO++;
065: private static final int STAR_G = GRP_NO++;
066: private static final int QMARK_G = GRP_NO++;
067: private static final int SPCHAR_G = GRP_NO++;
068: private static final int NONROOT_G = GRP_NO++;
069:
070: private static final String grp(int gno, String s) {
071: return "({" + gno + "}" + s + ")";
072: }
073:
074: private static final String fsChars = "/\\" + File.separator;
075: private static final String fsClass = "[" + fsChars + "]";
076: private static final String nfsClass = "[^" + fsChars + "]";
077: private static final String fName = nfsClass + "+";
078: private static final Pattern fs = new Pattern(fsClass);
079: private static final Pattern spCharPattern = new Pattern(grp(
080: NONROOT_G, "^(?!" + fsClass + ")")
081: + "|"
082: + grp(ANY_G, fsClass + "?\\*\\*" + fsClass + "?")
083: + "|"
084: + grp(FS_G, fsClass)
085: + "|"
086: + grp(STAR_G, "\\*")
087: + "|"
088: + grp(QMARK_G, "\\?")
089: + "|"
090: + grp(SPCHAR_G, "[.()\\{\\}+|^$\\[\\]\\\\]"));
091:
092: private static final Replacer spCharProcessor = new Replacer(
093: spCharPattern, new Substitution() {
094: public void appendSubstitution(MatchResult mr,
095: TextBuffer dest) {
096: //System.out.println("spCharProcessor.appendSubstitution(): "+((Matcher)mr).groupv());
097: if (mr.isCaptured(FS_G)) {
098: dest.append(fsClass);
099: } else if (mr.isCaptured(ANY_G)) {
100: dest.append("(?:(?:");
101: dest.append(fsClass);
102: dest.append("|^)((?:");
103: dest.append(fName);
104: dest.append("(?:");
105: dest.append(fsClass);
106: dest.append(fName);
107: dest.append(")*)?))?");
108: dest.append("(?:");
109: dest.append(fsClass);
110: dest.append("|$)");
111: } else if (mr.isCaptured(STAR_G)) {
112: dest.append("(");
113: dest.append(nfsClass);
114: dest.append("*)");
115: } else if (mr.isCaptured(QMARK_G)) {
116: dest.append("(");
117: dest.append(nfsClass);
118: dest.append(")");
119: } else if (mr.isCaptured(SPCHAR_G)) {
120: dest.append("\\");
121: mr.getGroup(SPCHAR_G, dest);
122: } else if (mr.isCaptured(NONROOT_G)) {
123: dest.append("(?:\\.");
124: dest.append(fsClass);
125: dest.append(")?");
126: }
127: }
128: });
129:
130: private String str;
131: private String root;
132: private File rootf;
133: private PathElementMask queue, last;
134:
135: public PathPattern(String ptn) {
136: this (ptn, DEFAULT);
137: }
138:
139: public PathPattern(String ptn, boolean icase) {
140: this (ptn, icase ? DEFAULT | IGNORE_CASE : DEFAULT);
141: }
142:
143: public PathPattern(String path, int flags) {
144: this (null, path, flags);
145: }
146:
147: public PathPattern(File dir, String path, boolean icase) {
148: this (null, path, icase ? DEFAULT | IGNORE_CASE : DEFAULT);
149: }
150:
151: public PathPattern(File dir, String path, int flags) {
152: if (path == null || path.length() == 0)
153: throw new IllegalArgumentException("empty path not allowed");
154:
155: str = path;
156: RETokenizer tok = new RETokenizer(fs.matcher(path), true);
157: String s = tok.nextToken();
158: if (s.equals("")) {
159: if (dir != null)
160: rootf = dir;
161: else
162: root = "/";
163: } else {
164: if (dir != null)
165: rootf = dir;
166: else
167: root = ".";
168: addElement(newMask(s, flags, tok.hasMore()));
169: }
170: while (tok.hasMore()) {
171: s = tok.nextToken();
172: boolean hasMore = tok.hasMore();
173: if (s.equals("")) {
174: if (hasMore)
175: throw new IllegalArgumentException(
176: "\"//\" not allowed");
177: else
178: break;
179: }
180: addElement(newMask(s, flags, hasMore));
181: }
182: compile(spCharProcessor.replace(path), flags);
183: //System.out.println(spCharProcessor.replace(path));
184: }
185:
186: private void addElement(PathElementMask mask) {
187: if (queue == null) {
188: queue = last = mask;
189: } else {
190: last = (last.next = mask);
191: }
192: }
193:
194: public Enumeration enumerateFiles() {
195: PathElementEnumerator fe = queue.newEnumerator();
196: fe.setDir(rootf != null ? rootf : new File(root));
197: return fe;
198: }
199:
200: public File[] files() {
201: Enumeration e = enumerateFiles();
202: Vector v = new Vector();
203: while (e.hasMoreElements())
204: v.addElement(e.nextElement());
205: File[] files = new File[v.size()];
206: v.copyInto(files);
207: return files;
208: }
209:
210: /**
211: * @deprecated Is meaningless with regard to variable paths (since v.1.2)
212: */
213: public String[] names() {
214: return null;
215: }
216:
217: /**
218: * @deprecated Is meaningless with regard to variable paths (since v.1.2)
219: */
220: public File directory() {
221: return null;
222: }
223:
224: private static PathElementMask newMask(String s, int flags,
225: boolean dirsOnly) {
226: if (s == null || s.length() == 0)
227: throw new IllegalArgumentException(
228: "Error: empty path element not allowed");
229: if (s.indexOf('*') < 0 && s.indexOf('?') < 0) {
230: //if((flags&IGNORE_CASE)==0) return PathElementMask.fixedMask(s,dirsOnly);
231: //just a dirty trick,
232: //on windows this could be a disk name ("D:"),
233: //and so won't be listed, so the RegularMask won't help
234: if ((flags & IGNORE_CASE) == 0 || s.indexOf(':') >= 0)
235: return PathElementMask.fixedMask(s, dirsOnly);
236: else
237: return PathElementMask.regularMask(s, flags, dirsOnly);
238: } else if (s.equals("*"))
239: return PathElementMask.anyFile(dirsOnly);
240: else if (s.equals("**"))
241: return PathElementMask.anyPath(dirsOnly);
242: else
243: return PathElementMask.regularMask(s, flags, dirsOnly);
244: }
245:
246: public String toString() {
247: return str;
248: }
249:
250: //public static void main(String[] args)throws Exception{
251: // PathPattern path=new PathPattern(args.length>0? args[0]: "/**/*tmp*/**",true);
252: // //PathPattern path=new PathPattern(args.length>0? args[0]: "*/*",true);
253: // //PathPattern path=new PathPattern(args.length>0? args[0]: "/**/*abc*",true);
254: // Enumeration e=path.enumerateFiles();
255: // int c=0;
256: // int err=0;
257: // Matcher m=path.matcher();
258: // long t0=System.currentTimeMillis();
259: // //while(e.hasMoreElements()){
260: // //while(e.hasMoreElements() && c<30){
261: // while(e.hasMoreElements() && err<10){
262: // File f=(File)e.nextElement();
263: // if(!m.matches(f.getPath())){
264: // System.out.println("error with file: "+f);
265: // err++;
266: // }
267: // else{
268: // //System.out.println("file matches: "+m.groupv());
269: // }
270: // c++;
271: // }
272: // long t1=System.currentTimeMillis();
273: // System.out.println("found "+err+" errors in "+c+" files, time="+(t1-t0));
274: //}
275: }
|