fa57795cdf887258b1a2dbdfe24d9863c6fc83bf
[jalview.git] / src / com / stevesoft / pat / FileRegex.java
1 //
2 // This software is now distributed according to
3 // the Lesser Gnu Public License.  Please see
4 // http://www.gnu.org/copyleft/lesser.txt for
5 // the details.
6 //    -- Happy Computing!
7 //
8 package com.stevesoft.pat;
9
10 import java.io.File;
11 import java.util.StringTokenizer;
12 import java.util.Vector;
13
14 /**
15  * This class is a different form of Regex designed to work more like the file
16  * matching utility of a Unix shell. It is implemented by some simple string
17  * transformations: <center>
18  * <table border=1>
19  * <tr>
20  * <td>FileRegex</td>
21  * <td>Regex</td>
22  * <tr>
23  * <td>*</td>
24  * <td>.*</td>
25  * <tr>
26  * <td>.</td>
27  * <td>\.</td>
28  * <tr>
29  * <td>{</td>
30  * <td>(?:</td>
31  * <tr>
32  * <td>{?!</td>
33  * <td>(?!</td>
34  * <tr>
35  * <td>{?=</td>
36  * <td>(?=</td>
37  * <tr>
38  * <td>{??</td>
39  * <td>(??</td>
40  * <tr>
41  * <td></td>
42  * <td>)</td>
43  * <tr>
44  * <td>?</td>
45  * <td>.</td>
46  * <tr>
47  * <td>{,}</td>
48  * <td>(|)</td>
49  * </table>
50  * </center> Note that a FileRegex pattern always ends with the Regex pattern
51  * element "$". If you like to experiment, try making FileRegex's and then
52  * printing them out. The toString() method does a decompile of the pattern to a
53  * standard Regex. Here are some more complete examples: <center>
54  * <table * border=3>
55  * <tr>
56  * <td>FileRegex</td>
57  * <td>Regex</td>
58  * <tr>
59  * <td>*.java</td>
60  * <td>.*\.java$</td>
61  * <tr>
62  * <td>*.{java,html}</td>
63  * <td>.*\.(java|html)$</td>
64  * <tr>
65  * <td>foo.[chC]</td>
66  * <td>foo.[chC]$</td>
67  * </table>
68  * </center>
69  */
70 public class FileRegex extends Regex
71 {
72   /** Build an unitialized FileRegex. */
73   public FileRegex()
74   {
75     dirflag = EITHER;
76   }
77
78   /** Build a FileRegex form String s. */
79   public FileRegex(String s)
80   {
81     super(s);
82     dirflag = EITHER;
83   }
84
85   /**
86    * Compile a new pattern. Throws
87    * 
88    * @exception com.stevesoft.pat.RegSyntax
89    *              for nonsensical patterns like "[9-0]+" just as Regex does.
90    * @see com.stevesoft.pat#compile(java.lang.String)
91    */
92   public void compile(String s) throws RegSyntax
93   {
94     String npat = toFileRegex(s);
95     super.compile(npat);
96     if (File.separatorChar == '\\') // MS-DOS
97     {
98       ignoreCase = true;
99     }
100   }
101
102   /**
103    * This is the method required by FileNameFilter. To get a listing of files in
104    * the current directory ending in .java, do this:
105    * 
106    * <pre>
107    * File dot = new File(&quot;.&quot;);
108    * 
109    * FileRegex java_files = new FileRegex(&quot;*.java&quot;);
110    * 
111    * String[] file_list = dot.list(java_files);
112    * </pre>
113    */
114   public boolean accept(File dir, String s)
115   {
116     if (dirflag != EITHER)
117     {
118       File f = new File(s);
119       if (f.isDirectory() && dirflag == NONDIR)
120       {
121         return false;
122       }
123       if (!f.isDirectory() && dirflag == DIR)
124       {
125         return false;
126       }
127     }
128     return matchAt(s, 0);
129   }
130
131   int dirflag = 0;
132
133   final static int EITHER = 0, DIR = 1, NONDIR = 2;
134
135   /**
136    * Provides an alternative to File.list -- this separates its argument
137    * according to File.pathSeparator. To each path, it splits off a directory --
138    * all characters up to and including the first instance of File.separator --
139    * and a file pattern -- the part that comes after the directory. It then
140    * produces a list of all the pattern matches on all the paths. Thus
141    * "*.java:../*.java" would produce a list of all the java files in this
142    * directory and in the ".." directory on a Unix machine. "*.java;..\\*.java"
143    * would do the same thing on a Dos machine.
144    */
145   public static String[] list(String f)
146   {
147     return list(f, EITHER);
148   }
149
150   static String[] list(String f, int df)
151   {
152     // return list_(f,new FileRegex());
153     StringTokenizer st = new StringTokenizer(f, File.pathSeparator);
154     Vector v = new Vector();
155     while (st.hasMoreTokens())
156     {
157       String path = st.nextToken();
158       list1(path, v, df, true);
159     }
160     String[] sa = new String[v.size()];
161     v.copyInto(sa);
162     return sa;
163   }
164
165   final static Regex root = new Regex(File.separatorChar == '/' ? "/$"
166           : "(?:.:|)\\\\$");
167
168   static void list1(String path, Vector v, int df, boolean rec)
169   {
170     // if path looks like a/b/c/ or d:\ then add .
171     if (root.matchAt(path, 0))
172     {
173       v.addElement(path + ".");
174       return;
175     }
176     File f = new File(path);
177     if (f.getParent() != null && rec)
178     {
179       Vector v2 = new Vector();
180       list1(f.getParent(), v2, DIR, true);
181       for (int i = 0; i < v2.size(); i++)
182       {
183         String path2 = ((String) v2.elementAt(i)) + File.separator
184                 + f.getName();
185         list1(path2, v, df, false);
186       }
187     }
188     else
189     {
190       File base = new File(path);
191
192       String dir_s = base.getParent();
193       if (dir_s == null)
194       {
195         dir_s = ".";
196       }
197       File dir = new File(dir_s);
198
199       FileRegex fr = new FileRegex(base.getName());
200       if (fr.isLiteral())
201       {
202         v.addElement(dir_s + File.separator + base.getName());
203         return;
204       }
205       fr.dirflag = df;
206       String[] sa = dir.list(fr);
207       if (sa == null)
208       {
209         return;
210       }
211       for (int i = 0; i < sa.length; i++)
212       {
213         v.addElement(dir_s + File.separator + sa[i]);
214       }
215     }
216   }
217
218   /**
219    * This method takes a file regular expression, and translates it into the
220    * type of pattern used by a normal Regex.
221    */
222   public static String toFileRegex(String s)
223   {
224     StrPos sp = new StrPos(s, 0);
225     StringBuffer sb = new StringBuffer();
226     if (sp.incMatch("{?e="))
227     {
228       char e = sp.thisChar();
229       sp.inc();
230       if (sp.incMatch("}"))
231       {
232         sb.append("(?e=" + e + ")^");
233       }
234       else
235       {
236         sb.append("^(?e=");
237       }
238       sp.esc = e;
239     }
240     int ParenLvl = 0;
241     while (!sp.eos())
242     {
243       if (File.separatorChar == '\\')
244       {
245         if (sp.escaped())
246         {
247           sb.append("\\\\");
248         }
249         sp.dontMatch = false;
250       }
251       if (sp.incMatch("?"))
252       {
253         sb.append(".");
254       }
255       else if (sp.incMatch("."))
256       {
257         sb.append(sp.esc);
258         sb.append('.');
259       }
260       else if (sp.incMatch("{??"))
261       {
262         sb.append("(??");
263         ParenLvl++;
264         // allow negative lookahead to work
265       }
266       else if (sp.incMatch("{?!"))
267       {
268         sb.append("(?!");
269         ParenLvl++;
270         // allow positive lookahead to work
271       }
272       else if (sp.incMatch("{?="))
273       {
274         sb.append("(?=");
275         ParenLvl++;
276       }
277       else if (sp.incMatch("{"))
278       {
279         sb.append("(?:");
280         ParenLvl++;
281       }
282       else if (sp.incMatch("}"))
283       {
284         sb.append(')');
285         ParenLvl--;
286       }
287       else if (ParenLvl != 0 && sp.incMatch(","))
288       {
289         sb.append('|');
290       }
291       else if (sp.incMatch("*"))
292       {
293         sb.append(".*");
294       }
295       else
296       {
297         sb.append(sp.thisChar());
298         sp.inc();
299       }
300     }
301     sb.append("$");
302     return sb.toString();
303   }
304
305   public boolean isLiteral()
306   {
307     Pattern x = thePattern;
308     while (x != null && !(x instanceof End))
309     {
310       if (x instanceof oneChar)
311       {
312         ;
313       }
314       else if (x instanceof Skipped)
315       {
316         ;
317       }
318       else
319       {
320         return false;
321       }
322       x = x.next;
323     }
324     return true;
325   }
326 }