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