KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > ba > SourceFile


1 /*
2  * Bytecode Analysis Framework
3  * Copyright (C) 2003,2004 University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs.ba;
21
22 import java.io.ByteArrayInputStream JavaDoc;
23 import java.io.ByteArrayOutputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26
27 /**
28  * Cached data for a source file.
29  * Contains a map of line numbers to byte offsets, for quick
30  * searching of source lines.
31  *
32  * @author David Hovemeyer
33  * @see SourceFinder
34  */

35 public class SourceFile {
36     private static int intValueOf(byte b) {
37         return b & 0xff;
38     }
39
40     /**
41      * Helper object to build map of line number to byte offset
42      * for a source file.
43      */

44     private static class LineNumberMapBuilder {
45         private SourceFile sourceFile;
46         private int offset;
47         private int lastSeen;
48
49         public LineNumberMapBuilder(SourceFile sourceFile) {
50             this.sourceFile = sourceFile;
51             this.offset = 0;
52             this.lastSeen = -1;
53         }
54
55         public void addData(byte[] data, int len) {
56             for (int i = 0; i < len; ++i) {
57                 int ch = intValueOf(data[i]);
58                 //if (ch < 0) throw new IllegalStateException();
59
add(ch);
60             }
61         }
62
63         public void eof() {
64             add(-1);
65         }
66
67         private void add(int ch) {
68             switch (ch) {
69             case '\n':
70                 sourceFile.addLineOffset(offset + 1);
71                 break;
72             case '\r':
73                 // Need to see next character to know if it's a
74
// line terminator.
75
break;
76             default:
77                 if (lastSeen == '\r') {
78                     // We consider a bare CR to be an end of line
79
// if it is not followed by a new line.
80
// Mac OS has historically used a bare CR as
81
// its line terminator.
82
sourceFile.addLineOffset(offset);
83                 }
84             }
85
86             lastSeen = ch;
87             ++offset;
88         }
89     }
90
91     private static final int DEFAULT_SIZE = 100;
92
93     private SourceFileDataSource dataSource;
94     private byte[] data;
95     private int[] lineNumberMap;
96     private int numLines;
97
98     /**
99      * Constructor.
100      *
101      * @param dataSource the SourceFileDataSource object which will
102      * provide the data of the source file
103      */

104     public SourceFile(SourceFileDataSource dataSource) {
105         this.dataSource = dataSource;
106         this.lineNumberMap = new int[DEFAULT_SIZE];
107         this.numLines = 0;
108     }
109
110     /**
111      * Get the full path name of the source file (with directory).
112      */

113     public String JavaDoc getFullFileName() {
114         return dataSource.getFullFileName();
115     }
116
117     /**
118      * Get an InputStream on data.
119      *
120      * @return an InputStream on the data in the source file,
121      * starting from given offset
122      */

123     public InputStream JavaDoc getInputStream() throws IOException JavaDoc {
124         loadFileData();
125         return new ByteArrayInputStream JavaDoc(data);
126     }
127
128     /**
129      * Get an InputStream on data starting at given offset.
130      *
131      * @param offset the start offset
132      * @return an InputStream on the data in the source file,
133      * starting at the given offset
134      */

135     public InputStream JavaDoc getInputStreamFromOffset(int offset) throws IOException JavaDoc {
136         loadFileData();
137         return new ByteArrayInputStream JavaDoc(data, offset, data.length - offset);
138     }
139
140     /**
141      * Add a source line byte offset.
142      * This method should be called for each line in the source file,
143      * in order.
144      *
145      * @param offset the byte offset of the next source line
146      */

147     public void addLineOffset(int offset) {
148         if (numLines >= lineNumberMap.length) {
149             // Grow the line number map.
150
int capacity = lineNumberMap.length * 2;
151             int[] newLineNumberMap = new int[capacity];
152             System.arraycopy(lineNumberMap, 0, newLineNumberMap, 0, lineNumberMap.length);
153             lineNumberMap = newLineNumberMap;
154         }
155
156         lineNumberMap[numLines++] = offset;
157     }
158
159     /**
160      * Get the byte offset in the data for a source line.
161      * Note that lines are considered to be zero-index, so the first
162      * line in the file is numbered zero.
163      *
164      * @param line the line number
165      * @return the byte offset in the file's data for the line,
166      * or -1 if the line is not valid
167      */

168     public int getLineOffset(int line) {
169         if (line < 0 || line >= numLines)
170             return -1;
171         return lineNumberMap[line];
172     }
173
174     private synchronized void loadFileData() throws IOException JavaDoc {
175         if (data != null)
176             return;
177
178         InputStream JavaDoc in = null;
179
180         try {
181             in = dataSource.open();
182             ByteArrayOutputStream JavaDoc out = new ByteArrayOutputStream JavaDoc();
183
184             addLineOffset(0); // Line 0 starts at offset 0
185
LineNumberMapBuilder mapBuilder = new LineNumberMapBuilder(this);
186
187             // Copy all of the data from the file into the byte array output stream
188
byte[] buf = new byte[1024];
189             int n;
190             while ((n = in.read(buf)) >= 0) {
191                 mapBuilder.addData(buf, n);
192                 out.write(buf, 0, n);
193             }
194             mapBuilder.eof();
195
196             setData(out.toByteArray());
197         } finally {
198             if (in != null)
199                 in.close();
200         }
201
202     }
203
204     /**
205      * Set the source file data.
206      *
207      * @param data the data
208      */

209     private void setData(byte[] data) {
210         this.data = data;
211     }
212 }
213
214 // vim:ts=4
215
Popular Tags