2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
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
10 * http://www.apache.org/licenses/LICENSE-2.0.
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.
18 package gov.nasa.jpf.util;
21 import gov.nasa.jpf.Config;
22 import gov.nasa.jpf.JPF;
24 import java.io.BufferedReader;
26 import java.io.FileInputStream;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.util.ArrayList;
32 import java.util.Hashtable;
33 import java.util.LinkedList;
34 import java.util.List;
35 import java.util.jar.JarEntry;
36 import java.util.jar.JarFile;
37 import java.util.logging.Logger;
41 * utility class to access arbitrary source files by line number
42 * sources can be files inside of root directories, or
43 * can be entries in jars
47 static Logger logger = JPF.getLogger("gov.nasa.jpf.util.Source");
49 static List<SourceRoot> sourceRoots;
50 static Hashtable<String,Source> sources = new Hashtable<String,Source>();
51 static Source noSource = new Source(null, null);
53 static abstract class SourceRoot { // common base
54 abstract InputStream getInputStream (String fname);
57 static class DirRoot extends SourceRoot {
60 DirRoot (String path){
65 InputStream getInputStream (String fname) {
66 if (File.separatorChar != '/'){
67 fname = fname.replace('/', File.separatorChar);
70 File f = new File(path, fname);
73 return new FileInputStream(f);
74 } catch (FileNotFoundException fnfx) {
83 public boolean equals (Object other){
84 return (other != null) && (other instanceof DirRoot) &&
85 path.equals(((DirRoot)other).path);
89 public String toString() {
94 static class JarRoot extends SourceRoot {
98 JarRoot (String path, String ep) throws IOException {
99 jar = new JarFile(path);
105 if (ep.charAt(ep.length()-1) != '/') {
112 InputStream getInputStream (String fname) {
113 String en = (entryPrefix != null) ? entryPrefix + fname : fname;
114 JarEntry entry = jar.getJarEntry(en);
117 return jar.getInputStream(entry);
118 } catch (IOException e) {
127 public boolean equals (Object other){
128 if ( (other != null) && (other instanceof JarRoot)){
130 // just how hard can it be to check if two JarFiles instances refer to
132 JarRoot o = (JarRoot)other;
133 File f = new File(jar.getName());
134 File fOther = new File(o.jar.getName());
135 if (f.getAbsolutePath().equals(fOther.getAbsolutePath())){
136 if (entryPrefix == null){
137 return o.entryPrefix == null;
139 entryPrefix.equals(o.entryPrefix);
148 public String toString() {
149 return jar.getName();
153 static void addSourceRoot (Config config, List<SourceRoot> roots, String spec){
154 SourceRoot sr = null;
157 int i = spec.indexOf(".jar");
159 String pn = FileUtils.asPlatformPath(spec.substring(0, i + 4));
160 File jar = new File(pn);
162 int i0 = i + 5; // scrub the leading path separator
163 // JarFile assumes Unix for archive-internal paths (also on Windows)
164 String ep = (spec.length() > i0) ? FileUtils.asCanonicalUnixPath(spec.substring(i0)) : null;
165 // we should probably check here if there is such a dir in the Jar
166 sr = new JarRoot(pn, ep);
169 } else { // directory
170 String pn = FileUtils.asPlatformPath(spec);
171 File dir = new File(pn);
173 sr = new DirRoot(pn);
176 } catch (IOException iox) {
177 // we report this below
181 if (!roots.contains(sr)){
185 logger.info("not a valid source root: " + spec);
189 static String findSrcRoot (String cpEntry){
190 if (cpEntry.endsWith(".jar")){
191 // check if there is a 'src' dir in the jar
193 JarFile jf = new JarFile(cpEntry);
194 JarEntry srcEntry = jf.getJarEntry("src");
195 if (srcEntry != null && srcEntry.isDirectory()) {
196 return jf.getName() + "/src"; // jar internal paths use '/' separators
198 } catch (IOException iox){
202 } else { // is it a dir?
203 File cpe = new File(cpEntry);
204 if (cpe.isDirectory()){
205 // go up until you hit a dir that has a 'src' subdir
206 // remember the traversed path elements
207 LinkedList<String> dirStack = new LinkedList<String>();
208 dirStack.addFirst(cpe.getName());
209 for (File pd = cpe.getParentFile(); pd != null; pd = pd.getParentFile()){
210 File sd = new File(pd,"src");
211 if (sd.isDirectory()){
212 String srcRoot = sd.getPath();
213 for (String e : dirStack) {
214 srcRoot = srcRoot + File.separatorChar + e;
216 sd = new File(srcRoot);
217 if (sd.isDirectory()){
221 dirStack.addFirst(pd.getName());
230 public static void init (Config config) {
231 ArrayList<SourceRoot> roots = new ArrayList<SourceRoot>();
233 String[] srcPaths = config.getCompactStringArray("sourcepath");
234 if (srcPaths != null){
235 for (String e : srcPaths){
236 addSourceRoot(config, roots, e);
246 // for debugging purposes
247 static void printRoots() {
248 System.out.println("source roots:");
249 for (SourceRoot sr : sourceRoots){
250 System.out.println(" " + sr);
254 public static Source getSource (String relPathName) {
255 if (relPathName == null){
259 Source s = sources.get(relPathName);
265 for (SourceRoot root : sourceRoots) {
266 InputStream is = root.getInputStream(relPathName);
269 s = new Source(root,relPathName);
273 sources.put(relPathName, s);
275 } catch (IOException iox) {
276 logger.warning("error reading " + relPathName + " from" + root);
285 sources.put(relPathName, noSource);
289 //--- the Source instance data itself
290 protected SourceRoot root;
291 protected String fname;
292 protected String[] lines;
295 protected Source (SourceRoot root, String fname) {
300 protected void loadLines (InputStream is) throws IOException {
301 BufferedReader in = new BufferedReader(new InputStreamReader(is));
303 ArrayList<String> l = new ArrayList<String>();
304 for (String line = in.readLine(); line != null; line = in.readLine()) {
310 lines = l.toArray(new String[l.size()]);
316 * this is our sole purpose in life - answer line strings
317 * line index is 1-based
319 public String getLine (int i) {
320 if ((lines == null) || (i <= 0) || (i > lines.length)) {
327 public int getLineCount()
329 return(lines.length);
332 public String getPath() {
333 return root.toString() + File.separatorChar + fname;