Initial import
[jpf-core.git] / src / main / gov / nasa / jpf / util / LocationSpec.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
19 package gov.nasa.jpf.util;
20
21 import java.io.File;
22
23 /**
24  * support for specification of source locations
25  *
26  * This maps sourcefile:line1-range strings into instructions that can be efficiently
27  * checked for by listeners. Example formats are:
28  *
29  *  X.java                (the whole of file X.java)
30  *  MyClass.java:42       (file MyClass.java, line1 42)
31  *  FooBar.java:42-48     (file FooBar.java, lines 42 to 48)
32  *  FooBar.java:42+3      (range of lines from 42 to 45)
33  *  x/y/z/whatever:42+    (file with pathname x/y/z/whatever, lines 42 to end)
34  * 
35  *
36  * NOTE path names are given in Unix notation, to avoid the usual Java string
37  * quoting problem with backslashes
38  */
39 public class LocationSpec {
40
41   public static final int ANYLINE = -1;
42
43   protected StringMatcher pathSpec;
44   protected StringMatcher fileSpec;
45   protected int fromLine = ANYLINE;   // inclusive
46   protected int toLine = ANYLINE;     // inclusive
47
48   /**
49    * factory method that includes the parser
50    */
51   public static LocationSpec createLocationSpec (String s){
52     s = s.replace('\\', '/');
53
54     String pspec = null, fspec;
55     int line1 = -1, line2 = -1;
56
57     int idx = s.lastIndexOf(':');
58     // check if it is just a dreaded "drive letter"
59     if (idx == s.length()-1 || s.charAt(idx + 1) == '/') {
60       idx = -1;
61     }
62
63     if (idx < 0){ // no line1 spec
64       fspec = s.trim();
65
66     } else if (idx > 0){ // line1 spec present
67       fspec = s.substring(0, idx).trim();
68
69       // now get the line1 (range)
70       s = s.substring(idx+1).trim();
71       int len = s.length();
72
73       if (len > 0){
74         int i = 0;
75         for (; i < len; i++) {
76           char c = s.charAt(i);
77           if (c == '-' || c == '+') {
78             line1 = Integer.parseInt(s.substring(0, i));
79
80             if (i == len - 1) { // open interval
81               line2 = Integer.MAX_VALUE;
82             } else {
83               line2 = Integer.parseInt(s.substring(i + 1));
84               if (c == '+') {
85                 line2 = line1 + line2;
86               }
87             }
88             break;
89           }
90         }
91
92         if (i == len) { // single line
93           line1 = Integer.parseInt(s);
94         }
95       }
96
97     } else {
98       throw new RuntimeException("no filename in LocationSpec: " + s);
99     }
100
101     idx = fspec.lastIndexOf('/');
102     if (idx > 0){
103       pspec = fspec.substring(0, idx);
104       fspec = fspec.substring(idx+1);
105     } else if (idx == 0){
106       pspec = "/";
107       fspec = fspec.substring(1);
108     } else {
109       pspec = null;
110     }
111
112     return new LocationSpec(pspec, fspec, line1, line2);
113   }
114
115   public LocationSpec (String pspec, String fspec, int line1, int line2){
116     if (pspec != null){
117       pathSpec = new StringMatcher(pspec);
118     }
119     fileSpec = new StringMatcher(fspec);
120
121     fromLine = line1;
122     toLine = line2;
123   }
124
125   @Override
126   public String toString(){
127     StringBuilder sb = new StringBuilder();
128
129     if (pathSpec != null){
130       sb.append(pathSpec);
131       sb.append('/');
132     }
133
134     sb.append(fileSpec);
135
136     if (fromLine != ANYLINE){
137       sb.append(':');
138       sb.append(fromLine);
139     }
140     if (toLine != ANYLINE){
141       sb.append('-');
142       if (toLine != Integer.MAX_VALUE){
143         sb.append(toLine);
144       }
145     }
146
147     return sb.toString();
148   }
149
150   public boolean matchesFile (File f){
151     if (fileSpec.matches(f.getName())){
152       if (pathSpec != null){
153         String pspec = f.getParent();
154         pspec = pspec.replace('\\', '/'); // use Unix '/' to avoid quoting issue
155
156         return pathSpec.matches(pspec);
157
158       } else { // no path
159         return true;
160       }
161     }
162
163     return false;
164   }
165
166   public boolean isAnyLine(){
167     return fromLine == ANYLINE;
168   }
169
170   public boolean isLineInterval(){
171     return toLine != ANYLINE;
172   }
173
174   public int getLine(){ // for specs that are single locations
175     return fromLine;
176   }
177
178   public int getFromLine() {
179     return fromLine;
180   }
181
182   // note - this is < 0 if there was only a
183   public int getToLine() {
184     if (toLine < 0){
185       if (fromLine >= 0){
186         return fromLine;
187       }
188     }
189
190     return toLine;
191   }
192
193   /**
194    * 'pathName' has to use '/' as separators
195    */
196   public boolean matchesFile (String pathName){
197     if (pathName != null){
198       pathName = pathName.replace('\\', '/');
199       int idx = pathName.lastIndexOf('/');
200
201       if (idx >= 0) {
202         String fname = pathName.substring(idx + 1);
203
204         if (fileSpec.matches(fname)) {
205           if (pathSpec != null) {
206             String pname = idx > 0 ? pathName.substring(0, idx) : "/";
207             return pathSpec.matches(pname);
208           } else {
209             return true;
210           }
211         }
212
213       } else { // no path
214         return fileSpec.matches(pathName);
215       }
216     }
217
218     return false;
219   }
220   
221   public boolean includesLine (int line){
222     if (fromLine == ANYLINE){
223       return true;
224
225     } else {
226       if (fromLine == line){
227         return true;
228       } else if (fromLine < line){
229         if (toLine == ANYLINE){ // single location
230           return false;
231         } else {
232           return (line <= toLine);
233         }
234       }
235     }
236
237     return false;
238   }
239 }