//
// Copyright (C) 2010 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.util;
import java.io.File;
/**
* support for specification of source locations
*
* This maps sourcefile:line1-range strings into instructions that can be efficiently
* checked for by listeners. Example formats are:
*
* X.java (the whole of file X.java)
* MyClass.java:42 (file MyClass.java, line1 42)
* FooBar.java:42-48 (file FooBar.java, lines 42 to 48)
* FooBar.java:42+3 (range of lines from 42 to 45)
* x/y/z/whatever:42+ (file with pathname x/y/z/whatever, lines 42 to end)
*
*
* NOTE path names are given in Unix notation, to avoid the usual Java string
* quoting problem with backslashes
*/
public class LocationSpec {
public static final int ANYLINE = -1;
protected StringMatcher pathSpec;
protected StringMatcher fileSpec;
protected int fromLine = ANYLINE; // inclusive
protected int toLine = ANYLINE; // inclusive
/**
* factory method that includes the parser
*/
public static LocationSpec createLocationSpec (String s){
s = s.replace('\\', '/');
String pspec = null, fspec;
int line1 = -1, line2 = -1;
int idx = s.lastIndexOf(':');
// check if it is just a dreaded "drive letter"
if (idx == s.length()-1 || s.charAt(idx + 1) == '/') {
idx = -1;
}
if (idx < 0){ // no line1 spec
fspec = s.trim();
} else if (idx > 0){ // line1 spec present
fspec = s.substring(0, idx).trim();
// now get the line1 (range)
s = s.substring(idx+1).trim();
int len = s.length();
if (len > 0){
int i = 0;
for (; i < len; i++) {
char c = s.charAt(i);
if (c == '-' || c == '+') {
line1 = Integer.parseInt(s.substring(0, i));
if (i == len - 1) { // open interval
line2 = Integer.MAX_VALUE;
} else {
line2 = Integer.parseInt(s.substring(i + 1));
if (c == '+') {
line2 = line1 + line2;
}
}
break;
}
}
if (i == len) { // single line
line1 = Integer.parseInt(s);
}
}
} else {
throw new RuntimeException("no filename in LocationSpec: " + s);
}
idx = fspec.lastIndexOf('/');
if (idx > 0){
pspec = fspec.substring(0, idx);
fspec = fspec.substring(idx+1);
} else if (idx == 0){
pspec = "/";
fspec = fspec.substring(1);
}
return new LocationSpec(pspec, fspec, line1, line2);
}
public LocationSpec (String pspec, String fspec, int line1, int line2){
if (pspec != null){
pathSpec = new StringMatcher(pspec);
}
fileSpec = new StringMatcher(fspec);
fromLine = line1;
toLine = line2;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
if (pathSpec != null){
sb.append(pathSpec);
sb.append('/');
}
sb.append(fileSpec);
if (fromLine != ANYLINE){
sb.append(':');
sb.append(fromLine);
}
if (toLine != ANYLINE){
sb.append('-');
if (toLine != Integer.MAX_VALUE){
sb.append(toLine);
}
}
return sb.toString();
}
public boolean matchesFile (File f){
if (fileSpec.matches(f.getName())){
if (pathSpec != null){
String pspec = f.getParent();
pspec = pspec.replace('\\', '/'); // use Unix '/' to avoid quoting issue
return pathSpec.matches(pspec);
} else { // no path
return true;
}
}
return false;
}
public boolean isAnyLine(){
return fromLine == ANYLINE;
}
public boolean isLineInterval(){
return toLine != ANYLINE;
}
public int getLine(){ // for specs that are single locations
return fromLine;
}
public int getFromLine() {
return fromLine;
}
// note - this is < 0 if there was only a
public int getToLine() {
if (toLine < 0){
if (fromLine >= 0){
return fromLine;
}
}
return toLine;
}
/**
* 'pathName' has to use '/' as separators
*/
public boolean matchesFile (String pathName){
if (pathName != null){
pathName = pathName.replace('\\', '/');
int idx = pathName.lastIndexOf('/');
if (idx >= 0) {
String fname = pathName.substring(idx + 1);
if (fileSpec.matches(fname)) {
if (pathSpec != null) {
String pname = idx > 0 ? pathName.substring(0, idx) : "/";
return pathSpec.matches(pname);
} else {
return true;
}
}
} else { // no path
return fileSpec.matches(pathName);
}
}
return false;
}
public boolean includesLine (int line){
if (fromLine == ANYLINE){
return true;
} else {
if (fromLine == line){
return true;
} else if (fromLine < line){
if (toLine == ANYLINE){ // single location
return false;
} else {
return (line <= toLine);
}
}
}
return false;
}
}