//
// 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.pat;
import java.io.*;
import java.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:
FileRegex | Regex |
* | .* |
. | \. |
{ | (?: |
{?! | (?! |
{?= | (?= |
{?? | (?? |
} | ) |
? | . |
{,} | (|) |
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:
FileRegex | Regex |
*.java | .*\.java$ |
*.{java,html} | .*\.(java|html)$ |
foo.[chC] | foo.[chC]$ |
*/
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:
File dot = new File(".");
FileRegex java_files = new FileRegex("*.java");
String[] file_list = dot.list(java_files);
*/
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;
}
}