Fixes null captured parameters
[jpf-core.git] / src / main / gov / nasa / jpf / util / PathnameExpander.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.util;
19
20 import java.io.File;
21 import java.util.ArrayList;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
25 /**
26  * utility to perform pathname expansion
27  * the following patterns are supported so far:
28  *
29  * (1) brace expansion ala bash: foo{Boo,Shoo} => fooBoo, fooShoo
30  *     (this doesn't check for existence, its simply lexical)
31  *
32  * (2) '*' wildcard pathname expansion ala bash: "*.java" | "*\Main*.java"
33  *     (supports wildcards in mutiple path elements and within file/dir name)
34  *
35  * (3) recursive dir expansion ala Ant: "**\*.jar"
36  *
37  */
38 public class PathnameExpander {
39
40   public String[] expandPath (String s) {
41     if (s == null || s.length() == 0) {
42       return null;
43     }
44
45     boolean hasWildcards = (s.indexOf('*') >= 0);
46
47     int i = s.indexOf('{');
48     if (i >= 0){
49       ArrayList<String> list = new ArrayList<String>();
50
51       int j=0, jLast = s.length();
52       for (; (i = s.indexOf('{', j)) >= 0;) {
53         if ((j = s.indexOf('}', i)) > 0) {
54           String[] choices = s.substring(i + 1, j).split(",");
55
56           if (list.isEmpty()) {
57             String prefix = s.substring(0, i);
58             for (String c : choices) {
59               list.add(prefix + c);
60             }
61           } else {
62             String prefix = s.substring(jLast, i);
63             ArrayList<String> newList = new ArrayList<String>();
64             for (String e : list) {
65               for (String c : choices) {
66                 newList.add(e + prefix + c);
67               }
68             }
69             list = newList;
70           }
71           jLast = j+1;
72         } else {
73           throw new IllegalArgumentException("illegal path spec (missing '}'): " + s);
74         }
75       }
76
77       if (jLast < s.length()) {
78         String postfix = s.substring(jLast);
79         ArrayList<String> newList = new ArrayList<String>();
80         for (String e : list) {
81           newList.add(e + postfix);
82         }
83         list = newList;
84       }
85
86       if (hasWildcards){
87         ArrayList<String> newList = new ArrayList<String>();
88         for (String p : list) {
89           for (String c : expandWildcards(p)) {
90             newList.add(c);
91           }
92         }
93         list = newList;
94       }
95
96       return list.toArray(new String[list.size()]);
97
98     } else {  // no bracket expansion required
99
100       if (hasWildcards){
101         return expandWildcards(s);
102
103       } else { // nothing to expand at all
104         return (new String[] {s});
105       }
106     }
107   }
108
109   protected String[] expandWildcards (String s){
110     int i = s.indexOf('*');
111
112     if (i >= 0){ // Ok, we have at least one wildcard
113       String[] a = s.split("\\/");
114       ArrayList<File> list = new ArrayList<File>();
115
116       int j= initializeMatchList(list, a[0]);
117       for (; j<a.length; j++){
118         ArrayList<File> newList = new ArrayList<File>();
119
120         String e = a[j];
121         if (e.indexOf('*') >= 0){
122
123           if (e.equals("**")){ // matches all subdirs recursively
124             collectDirs(list, newList);
125
126           } else { // file/dir name match
127             collectMatchingNames(list, newList, getPattern(e));
128           }
129
130         } else { // no wildcard
131           collectExistingFile(list, newList, e);
132         }
133
134         if (newList.isEmpty()){  // shortcut, nothing more to match
135           return new String[0];
136         }
137         list = newList;
138       }
139
140       return getPaths(list);
141
142     } else { // no wildcards, nothing to expand
143       return new String[] {s};
144     }
145   }
146
147   private int initializeMatchList (ArrayList<File> list, String path){
148     if (path.isEmpty()){ // absolute pathname (ignoring drive letters for now)
149       list.add(new File(File.separator));
150       return 1;
151     } else if (path.equals("..") || path.equals(".")){
152       list.add(new File(path));
153       return 1;
154     } else {
155       list.add(new File("."));
156       return 0;
157     }
158   }
159
160   private void collectMatchingNames(ArrayList<File> list, ArrayList<File> newList, Pattern pattern){
161     for (File dir : list) {
162       if (dir.isDirectory()){
163         for (String c : dir.list()){
164           Matcher m = pattern.matcher(c);
165           if (m.matches()){
166             newList.add(new File(dir,c));
167           }
168         }
169       }
170     }
171   }
172
173   private void collectExistingFile(ArrayList<File> list, ArrayList<File> newList, String fname) {
174     for (File dir : list) {
175       if (dir.isDirectory()){
176         File nf = new File(dir, fname);
177         if (nf.exists()) {
178           newList.add(nf);
179         }
180       }
181     }
182   }
183
184   private void collectDirs(ArrayList<File> list, ArrayList<File> newList){
185     for (File dir : list) {
186       if (dir.isDirectory()){
187         newList.add(dir); // this includes the dir itself!
188         collectSubdirs(newList,dir);
189       }
190     }
191   }
192   private void collectSubdirs(ArrayList<File> newList, File dir) {
193     for (File f : dir.listFiles()){
194       if (f.isDirectory()){
195         newList.add(f);
196         collectSubdirs(newList, f);
197       }
198     }
199   }
200
201   protected String[] getPaths(ArrayList<File> list) {
202     String[] result = new String[list.size()];
203     int k=0;
204     for (File f : list){
205       String p = f.getPath();
206       if ((p.length() > 1) && (p.charAt(0) == '.')){ // remove leading "./"
207         char c = p.charAt(1);
208         if (c == '\\' || c == '/'){
209           p = p.substring(2);
210         }
211       }
212       result[k++] = p;
213     }
214     return result;
215   }
216
217   protected Pattern getPattern(String s){
218     Pattern p;
219
220     StringBuilder sb = new StringBuilder();
221
222     int len = s.length();
223     for (int j=0; j<len; j++){
224       char c = s.charAt(j);
225       switch (c){
226       case '.' : sb.append("\\."); break;
227       case '$' : sb.append("\\$"); break;
228       case '[' : sb.append("\\["); break;
229       case ']' : sb.append("\\]"); break;
230       case '*' : sb.append(".*"); break;
231       // <2do> and probably more..
232       default:   sb.append(c);
233       }
234     }
235
236     p = Pattern.compile(sb.toString());
237     return p;
238   }
239 }