// // Copyright (C) 2006 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.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Logger; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPF; /** * utility class to access arbitrary source files by line number * sources can be files inside of root directories, or * can be entries in jars */ public class Source { static Logger logger = JPF.getLogger("gov.nasa.jpf.util.Source"); static List<SourceRoot> sourceRoots; static Hashtable<String,Source> sources = new Hashtable<String,Source>(); static Source noSource = new Source(null, null); static abstract class SourceRoot { // common base abstract InputStream getInputStream (String fname); } static class DirRoot extends SourceRoot { String path; DirRoot (String path){ this.path = path; } InputStream getInputStream (String fname) { if (File.separatorChar != '/'){ fname = fname.replace('/', File.separatorChar); } File f = new File(path, fname); if (f.exists()) { try { return new FileInputStream(f); } catch (FileNotFoundException fnfx) { return null; } } else { return null; } } public boolean equals (Object other){ return (other instanceof DirRoot) && path.equals(((DirRoot)other).path); } public String toString() { return path; } } static class JarRoot extends SourceRoot { JarFile jar; String entryPrefix; JarRoot (String path, String ep) throws IOException { jar = new JarFile(path); if (ep == null) { entryPrefix = null; } else { entryPrefix = ep; if (ep.charAt(ep.length()-1) != '/') { entryPrefix += '/'; } } } InputStream getInputStream (String fname) { String en = (entryPrefix != null) ? entryPrefix + fname : fname; JarEntry entry = jar.getJarEntry(en); if (entry != null) { try { return jar.getInputStream(entry); } catch (IOException e) { return null; } } else { return null; } } public boolean equals (Object other){ if (other instanceof JarRoot){ // just how hard can it be to check if two JarFiles instances refer to // the same file? JarRoot o = (JarRoot)other; File f = new File(jar.getName()); File fOther = new File(o.jar.getName()); if (f.getAbsolutePath().equals(fOther.getAbsolutePath())){ if (entryPrefix == null){ return o.entryPrefix == null; } else { return entryPrefix.equals(o.entryPrefix); } } } return false; } public String toString() { return jar.getName(); } } static void addSourceRoot (Config config, List<SourceRoot> roots, String spec){ SourceRoot sr = null; try { int i = spec.indexOf(".jar"); if (i >= 0) { // jar String pn = FileUtils.asPlatformPath(spec.substring(0, i + 4)); File jar = new File(pn); if (jar.exists()) { int i0 = i + 5; // scrub the leading path separator // JarFile assumes Unix for archive-internal paths (also on Windows) String ep = (spec.length() > i0) ? FileUtils.asCanonicalUnixPath(spec.substring(i0)) : null; // we should probably check here if there is such a dir in the Jar sr = new JarRoot(pn, ep); } } else { // directory String pn = FileUtils.asPlatformPath(spec); File dir = new File(pn); if (dir.exists()) { sr = new DirRoot(pn); } } } catch (IOException iox) { // we report this below } if (sr != null) { if (!roots.contains(sr)){ roots.add(sr); } } else { logger.info("not a valid source root: " + spec); } } static String findSrcRoot (String cpEntry){ if (cpEntry.endsWith(".jar")){ // check if there is a 'src' dir in the jar try (JarFile jf = new JarFile(cpEntry)) { JarEntry srcEntry = jf.getJarEntry("src"); if (srcEntry != null && srcEntry.isDirectory()) { return jf.getName() + "/src"; // jar internal paths use '/' separators } } catch (IOException iox){ return null; } } else { // is it a dir? File cpe = new File(cpEntry); if (cpe.isDirectory()){ // go up until you hit a dir that has a 'src' subdir // remember the traversed path elements LinkedList<String> dirStack = new LinkedList<String>(); dirStack.addFirst(cpe.getName()); for (File pd = cpe.getParentFile(); pd != null; pd = pd.getParentFile()){ File sd = new File(pd,"src"); if (sd.isDirectory()){ String srcRoot = sd.getPath(); for (String e : dirStack) { srcRoot = srcRoot + File.separatorChar + e; } sd = new File(srcRoot); if (sd.isDirectory()){ return srcRoot; } } else { dirStack.addFirst(pd.getName()); } } } } return null; } public static void init (Config config) { ArrayList<SourceRoot> roots = new ArrayList<SourceRoot>(); String[] srcPaths = config.getCompactStringArray("sourcepath"); if (srcPaths != null){ for (String e : srcPaths){ addSourceRoot(config, roots, e); } } sourceRoots = roots; sources.clear(); //printRoots(); } // for debugging purposes static void printRoots() { System.out.println("source roots:"); for (SourceRoot sr : sourceRoots){ System.out.println(" " + sr); } } public static Source getSource (String relPathName) { if (relPathName == null){ return null; } Source s = sources.get(relPathName); if (s == noSource) { return null; } if (s == null) { for (SourceRoot root : sourceRoots) { InputStream is = root.getInputStream(relPathName); if (is != null) { try { s = new Source(root,relPathName); s.loadLines(is); is.close(); sources.put(relPathName, s); return s; } catch (IOException iox) { logger.warning("error reading " + relPathName + " from" + root); return null; } } } } else { return s; } sources.put(relPathName, noSource); return null; } //--- the Source instance data itself protected SourceRoot root; protected String fname; protected String[] lines; protected Source (SourceRoot root, String fname) { this.root = root; this.fname = fname; } protected void loadLines (InputStream is) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(is)); ArrayList<String> l = new ArrayList<String>(); for (String line = in.readLine(); line != null; line = in.readLine()) { l.add(line); } in.close(); if (l.size() > 0) { lines = l.toArray(new String[l.size()]); } } /** * this is our sole purpose in life - answer line strings * line index is 1-based */ public String getLine (int i) { if ((lines == null) || (i <= 0) || (i > lines.length)) { return null; } else { return lines[i-1]; } } public int getLineCount() { return(lines.length); } public String getPath() { return root.toString() + File.separatorChar + fname; } }