//
// This software is now distributed according to
// the Lesser Gnu Public License. Please see
// http://www.gnu.org/copyleft/lesser.txt for
// the details.
// -- Happy Computing!
//
package com.stevesoft.ewe_pat;
import ewe.io.*;
import ewe.util.*;
/** This class is a different form of Regex designed to work more
like the file matching utility of a Unix shell. It is implemented
by some simple string transformations:
<center>
<table border=1>
<tr> <td> FileRegex </td> <td> Regex </td>
<tr> <td> * </td><td> .* </td>
<tr> <td> . </td><td> \. </td>
<tr> <td> { </td><td> (?: </td>
<tr> <td> {?! </td><td> (?! </td>
<tr> <td> {?= </td><td> (?= </td>
<tr> <td> {?? </td><td> (?? </td>
<tr> <td> } </td><td> ) </td>
<tr> <td> ? </td><td> . </td>
<tr> <td> {,} </td><td> (|) </td>
</table>
</center>
Note that a FileRegex pattern always ends with the Regex
pattern element "$". If you like to experiment, try making
FileRegex's and then printing them out. The toString() method
does a decompile of the pattern to a standard Regex. Here are
some more complete examples:
<center>
<table border=3>
<tr> <td> FileRegex </td><td> Regex </td>
<tr> <td>*.java </td><td> .*\.java$ </td>
<tr> <td>*.{java,html} </td><td> .*\.(java|html)$ </td>
<tr> <td> foo.[chC] </td><td> foo.[chC]$ </td>
</table>
</center>
*/
public class FileRegex extends Regex {
/** Build an unitialized FileRegex. */
public FileRegex() { dirflag=EITHER; }
/** Build a FileRegex form String s. */
public FileRegex(String s) {
super(s);
dirflag = EITHER;
}
/** Compile a new pattern.
Throws @exception com.stevesoft.pat.RegSyntax for
nonsensical patterns like "[9-0]+" just as Regex does.
@see com.stevesoft.pat#compile(java.lang.String)
*/
public void compile(String s) throws RegSyntax {
String npat = toFileRegex(s);
super.compile(npat);
if(File.separatorChar == '\\') // MS-DOS
ignoreCase = true;
}
/** This is the method required by FileNameFilter.
To get a listing of files in the current directory
ending in .java, do this:
<pre>
File dot = new File(".");
FileRegex java_files = new FileRegex("*.java");
String[] file_list = dot.list(java_files);
</pre>
*/
public boolean accept(File dir,String s) {
if(dirflag != EITHER) {
File f = new File(s);
if(f.isDirectory() && dirflag == NONDIR)
return false;
if(!f.isDirectory() && dirflag == DIR)
return false;
}
return matchAt(s,0);
}
int dirflag = 0;
final static int EITHER=0,DIR=1,NONDIR=2;
/** Provides an alternative to File.list -- this
separates its argument according to File.pathSeparator.
To each path, it splits off a directory -- all characters
up to and including the first instance of File.separator --
and a file pattern -- the part that comes after the directory.
It then produces a list of all the pattern matches on all
the paths. Thus "*.java:../*.java" would produce a list of
all the java files in this directory and in the ".." directory
on a Unix machine. "*.java;..\\*.java" would do the same thing
on a Dos machine. */
public static String[] list(String f) {
return list(f,EITHER);
}
static String[] list(String f,int df) {
//return list_(f,new FileRegex());
StringTokenizer st = new StringTokenizer(f,File.pathSeparator);
Vector v = new Vector();
while(st.hasMoreTokens()) {
String path = st.nextToken();
list1(path,v,df,true);
}
String[] sa = new String[v.size()];
v.copyInto(sa);
return sa;
}
final static Regex root=new Regex(File.separatorChar=='/' ?
"/$" : "(?:.:|)\\\\$");
static void list1(String path,Vector v,int df,boolean rec) {
// if path looks like a/b/c/ or d:\ then add .
if(root.matchAt(path,0)) {
v.addElement(path+".");
return;
}
File f = new File(path);
if(f.getParent() != null && rec) {
Vector v2 = new Vector();
list1(f.getParent(),v2,DIR,true);
for(int i=0;i<v2.size();i++) {
String path2 = ((String)v2.elementAt(i))+
File.separator+f.getName();
list1(path2,v,df,false);
}
} else {
File base = new File(path);
String dir_s = base.getParent();
if(dir_s==null) dir_s=".";
File dir = new File(dir_s);
FileRegex fr = new FileRegex(base.getName());
if(fr.isLiteral()) {
v.addElement(dir_s+File.separator+base.getName());
return;
}
fr.dirflag = df;
String[] sa = dir.list(fr);
if(sa == null) return;
for(int i=0;i<sa.length;i++) {
v.addElement(dir_s+File.separator+sa[i]);
}
}
}
/** This method takes a file regular expression, and translates it
into the type of pattern used by a normal Regex. */
public static String toFileRegex(String s) {
StrPos sp = new StrPos(s,0);
StringBuffer sb = new StringBuffer();
if(sp.incMatch("{?e=")) {
char e = sp.thisChar();
sp.inc();
if(sp.incMatch("}")) {
sb.append("(?e="+e+")^");
} else {
sb.append("^(?e=");
}
sp.esc = e;
}
int ParenLvl = 0;
while(!sp.eos()) {
if(File.separatorChar == '\\') {
if(sp.escaped())
sb.append("\\\\");
sp.dontMatch = false;
}
if(sp.incMatch("?"))
sb.append(".");
else if(sp.incMatch(".")) {
sb.append(sp.esc);
sb.append('.');
} else if(sp.incMatch("{??")) {
sb.append("(??");
ParenLvl++;
// allow negative lookahead to work
} else if(sp.incMatch("{?!")) {
sb.append("(?!");
ParenLvl++;
// allow positive lookahead to work
} else if(sp.incMatch("{?=")) {
sb.append("(?=");
ParenLvl++;
} else if(sp.incMatch("{")) {
sb.append("(?:");
ParenLvl++;
} else if(sp.incMatch("}")) {
sb.append(')');
ParenLvl--;
} else if(ParenLvl != 0 && sp.incMatch(","))
sb.append('|');
else if(sp.incMatch("*"))
sb.append(".*");
else {
sb.append(sp.thisChar());
sp.inc();
}
}
sb.append("$");
return sb.toString();
}
public boolean isLiteral() {
Pattern x = thePattern;
while(x != null && !(x instanceof End)) {
if(x instanceof oneChar)
;
else if(x instanceof Skipped)
;
else return false;
x = x.next;
}
return true;
}
}