Initial import
[jpf-core.git] / src / main / gov / nasa / jpf / util / Source.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
21 import gov.nasa.jpf.Config;
22 import gov.nasa.jpf.JPF;
23
24 import java.io.BufferedReader;
25 import java.io.File;
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;
38
39
40 /**
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
44  */
45 public class Source {
46
47   static Logger logger = JPF.getLogger("gov.nasa.jpf.util.Source");
48
49   static List<SourceRoot> sourceRoots;
50   static Hashtable<String,Source> sources = new Hashtable<String,Source>();
51   static Source noSource = new Source(null, null);
52
53   static abstract class SourceRoot { // common base
54     abstract InputStream getInputStream (String fname);
55   }
56
57   static class DirRoot extends SourceRoot {
58     String path;
59
60     DirRoot (String path){
61       this.path = path;
62     }
63
64     @Override
65         InputStream getInputStream (String fname) {
66       if (File.separatorChar != '/'){
67         fname = fname.replace('/', File.separatorChar);
68       }
69
70       File f = new File(path, fname);
71       if (f.exists()) {
72         try {
73           return new FileInputStream(f);
74         } catch (FileNotFoundException fnfx) {
75           return null;
76         }
77       } else {
78         return null;
79       }
80     }
81
82     @Override
83         public boolean equals (Object other){
84       return (other != null) && (other instanceof DirRoot) &&
85               path.equals(((DirRoot)other).path);
86     }
87
88     @Override
89         public String toString() {
90       return path;
91     }
92   }
93
94   static class JarRoot extends SourceRoot {
95     JarFile jar;
96     String  entryPrefix;
97
98     JarRoot (String path, String ep) throws IOException {
99       jar = new JarFile(path);
100
101       if (ep == null) {
102         entryPrefix = null;
103       } else {
104         entryPrefix = ep;
105         if (ep.charAt(ep.length()-1) != '/') {
106           entryPrefix += '/';
107         }
108       }
109     }
110
111     @Override
112         InputStream getInputStream (String fname) {
113       String en = (entryPrefix != null) ? entryPrefix + fname : fname;
114       JarEntry entry = jar.getJarEntry(en);
115       if (entry != null) {
116         try {
117           return jar.getInputStream(entry);
118         } catch (IOException e) {
119           return null;
120         }
121       } else {
122         return null;
123       }
124     }
125
126     @Override
127         public boolean equals (Object other){
128       if ( (other != null) && (other instanceof JarRoot)){
129
130         // just how hard can it be to check if two JarFiles instances refer to
131         // the same file?
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;
138           } else {
139             entryPrefix.equals(o.entryPrefix);
140           }
141         }
142       }
143
144       return false;
145     }
146
147     @Override
148         public String toString() {
149       return jar.getName();
150     }
151   }
152
153   static void addSourceRoot (Config config, List<SourceRoot> roots, String spec){
154     SourceRoot sr = null;
155
156     try {
157       int i = spec.indexOf(".jar");
158       if (i >= 0) {  // jar
159         String pn = FileUtils.asPlatformPath(spec.substring(0, i + 4));
160         File jar = new File(pn);
161         if (jar.exists()) {
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);
167         }
168
169       } else {       // directory
170         String pn = FileUtils.asPlatformPath(spec);
171         File dir = new File(pn);
172         if (dir.exists()) {
173           sr = new DirRoot(pn);
174         }
175       }
176     } catch (IOException iox) {
177       // we report this below
178       }
179
180     if (sr != null) {
181       if (!roots.contains(sr)){
182         roots.add(sr);
183       }
184     } else {
185       logger.info("not a valid source root: " + spec);
186     }
187   }
188
189   static String findSrcRoot (String cpEntry){
190     if (cpEntry.endsWith(".jar")){
191       // check if there is a 'src' dir in the jar
192       try {
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
197         }
198       } catch (IOException iox){
199         return null;
200       }
201
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;
215             }
216             sd = new File(srcRoot);
217             if (sd.isDirectory()){
218               return srcRoot;
219             }
220           } else {
221             dirStack.addFirst(pd.getName());
222           }
223         }
224       }
225     }
226
227     return null;
228   }
229
230   public static void init (Config config) {
231     ArrayList<SourceRoot> roots = new ArrayList<SourceRoot>();
232
233     String[] srcPaths = config.getCompactStringArray("sourcepath");
234     if (srcPaths != null){
235       for (String e : srcPaths){
236         addSourceRoot(config, roots, e);
237       }
238     }
239
240     sourceRoots = roots;
241     sources.clear();
242     
243     //printRoots();
244   }
245
246   // for debugging purposes
247   static void printRoots() {
248     System.out.println("source roots:");
249     for (SourceRoot sr : sourceRoots){
250       System.out.println("  " + sr);
251     }
252   }
253
254   public static Source getSource (String relPathName) {
255     if (relPathName == null){
256       return null;
257     }
258     
259     Source s = sources.get(relPathName);
260     if (s == noSource) {
261        return null;
262     }
263
264     if (s == null) {
265       for (SourceRoot root : sourceRoots) {
266         InputStream is = root.getInputStream(relPathName);
267         if (is != null) {
268           try {
269           s = new Source(root,relPathName);
270           s.loadLines(is);
271           is.close();
272
273           sources.put(relPathName, s);
274           return s;
275           } catch (IOException iox) {
276             logger.warning("error reading " + relPathName + " from" + root);
277             return null;
278           }
279         }
280       }
281     } else {
282       return s;
283     }
284
285     sources.put(relPathName, noSource);
286     return null;
287   }
288
289   //--- the Source instance data itself
290   protected SourceRoot root;
291   protected String     fname;
292   protected String[]   lines;
293
294
295   protected Source (SourceRoot root, String fname) {
296     this.root = root;
297     this.fname = fname;
298   }
299
300   protected void loadLines (InputStream is) throws IOException {
301     BufferedReader in = new BufferedReader(new InputStreamReader(is));
302
303     ArrayList<String> l = new ArrayList<String>();
304     for (String line = in.readLine(); line != null; line = in.readLine()) {
305       l.add(line);
306     }
307     in.close();
308
309     if (l.size() > 0) {
310       lines = l.toArray(new String[l.size()]);
311     }
312   }
313
314
315   /**
316    * this is our sole purpose in life - answer line strings
317    * line index is 1-based
318    */
319   public String getLine (int i) {
320     if ((lines == null) || (i <= 0) || (i > lines.length)) {
321       return null;
322     } else {
323       return lines[i-1];
324     }
325   }
326
327   public int getLineCount()
328   {
329      return(lines.length);
330   }
331
332   public String getPath() {
333     return root.toString() + File.separatorChar + fname;
334   }
335 }