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