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