KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > aspectj > util > LineNumberTableMapper


1 /* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * This file is part of the debugger and core tools for the AspectJ(tm)
4  * programming language; see http://aspectj.org
5  *
6  * The contents of this file are subject to the Mozilla Public License
7  * Version 1.1 (the "License"); you may not use this file except in
8  * compliance with the License. You may obtain a copy of the License at
9  * either http://www.mozilla.org/MPL/ or http://aspectj.org/MPL/.
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is AspectJ.
17  *
18  * The Initial Developer of the Original Code is Xerox Corporation. Portions
19  * created by Xerox Corporation are Copyright (C) 1999-2002 Xerox Corporation.
20  * All Rights Reserved.
21  */

22 package org.aspectj.util;
23
24 import java.io.File JavaDoc;
25 import java.util.*;
26
27 /** This class is responsible for parsing the information stored in
28     the SourceFile and LineNumberTable attributes of classfiles
29     produced by AspectJ. Said names/numbers are currently produced by
30     {@link org.aspectj.compiler.base.bcg.ClassfileBuilder#resolve()}.
31     AspectJ produces classfiles that have multiple source files. The
32     source file attribute of such files looks like:
33
34 <pre>
35 Attribute := Filename SecondaryFiles
36 SecondaryFiles := Empty | ';' Pathname '(' Integer 'k' ')' SecondaryFiles
37 Filename := any sequence of characters not including ';' or '/'
38 Pathname := Filename | /-separated-package-name '/' Filename
39 </pre>
40
41     <p> The <em>meaning</em> of such a string is an ordered set of
42     triples of packages, filenames, and integer multiples of a
43     thousand. The first filename listed implicitly is associated with
44     '0k'. So, the <em>meaning</em> of the string </p>
45
46 <pre>
47 Foo.java;org/aspectj/Goo Bear.java(3k);strange\filename:d(x(7k)
48 </pre>
49
50     <p> is the sorted association </p>
51
52 <pre>
53 0k Foo's package File Foo.java
54 3k pkg org.aspectj File Goo Bear.java
55 7k default pkg File strange\filename:d(x
56 </pre>
57
58     <p> which means that line numbers in the interval (0k, 3k) are
59     from Foo.java, those in the interval (3k, 7k) are from the next
60     file, and those in the interval (7k, +inf) are from the third. </p>
61
62     <p> In addition, the line number in the association forms an
63     <em>offset</em>. So the line number 3137 is in the second file,
64     and in fact is line 137 of the second file. </p>
65
66     <p> A mapper is created by passing in a string (the source file
67     attribute), a string (the package name of the classfile), and a
68     File object (the root from which to find filenames) to the
69     constructor. Then the mapper can be passed line numbers
70     to determine what file the line number is in, and what is the
71     actual line number in that file. </p>
72
73 */

74
75 public class LineNumberTableMapper {
76
77     private static final char PACKAGE_SEPARATOR = '/';
78     private static final char FILE_SEPARATOR = ';';
79
80     /** Create a new LineNumberTableMapper given the string of a
81         SourceFile attribute.
82
83         @param attrib The SourceFile attribute string
84         @param pkg This class's package, with '/' separators (NOT
85         '.'). For the default package, pass in null.
86         @param root The package root for this class */

87
88     public LineNumberTableMapper(String JavaDoc attrib, String JavaDoc pkg, File JavaDoc root) {
89         int len = attrib.length();
90         int i = attrib.indexOf(FILE_SEPARATOR);
91         if (i == -1) {
92             files = new File JavaDoc[] { buildFile(root,
93                                            (pkg == null)
94                                            ? attrib
95                                            : pkg + PACKAGE_SEPARATOR + attrib) };
96             offsets = new int[] { 0 };
97         } else {
98             List strings = new Vector();
99             List ints = new Vector();
100             strings.add(attrib.substring(0, i));
101             ints.add(new Integer JavaDoc(0));
102             while (i != -1) {
103                 i++;
104                 int start = i;
105                 i = attrib.indexOf(FILE_SEPARATOR, start);
106                 int end = (i == -1) ? len : i;
107                 end--; // now pointing at ')'
108
end--; // now pointing at 'k'
109
end--; // now pointing at digit
110
int accumulator = 0;
111                 int power = 1;
112                 for (char c = attrib.charAt(end); c != '('; end--, c = attrib.charAt(end)) {
113                     accumulator += Character.digit(c, 10) * power;
114                     power *= 10;
115                 }
116                 strings.add(attrib.substring(start, end));
117                 ints.add(new Integer JavaDoc(accumulator * 1000));
118             }
119             int size = strings.size();
120             files = new File JavaDoc[size];
121             offsets = new int[size];
122             for (int j = 0; j < size; j++) {
123                 files[j] = buildFile(root, (String JavaDoc) strings.get(j));
124                 offsets[j] = ((Integer JavaDoc) ints.get(j)).intValue();
125             }
126         }
127     }
128
129     /** Builds a new file by adding a string relative path to a File
130         root. This should be the same thing as {@link File(File,
131         String)}, but there the concatenation is based on the system
132         path separator. Here, we know exactly that path is '/'
133         separated.
134
135         @param root the root file to build upon
136         @param path the '/'-separated child path to add
137         @return a new file
138     */

139     private static File JavaDoc buildFile(File JavaDoc root, String JavaDoc path) {
140         for (int i = 0, j = path.indexOf(PACKAGE_SEPARATOR);
141              true;
142              i = j + 1, j = path.indexOf(PACKAGE_SEPARATOR, i)) {
143             if (j == -1) {
144                 return new File JavaDoc(root, path.substring(i, path.length()));
145             } else {
146                 root = new File JavaDoc(root, path.substring(i, j));
147             }
148         }
149     }
150
151     /** Returns the File corresponding to the region of a source
152         line.
153     
154         @param n the source line in question, from the LineNumberTable
155         attribute
156
157         @return a file representing the source file for that line */

158
159     public File JavaDoc getCorrespondingFile(int n) {
160         int end = files.length - 1;
161         for (int i = 1; i <= end; i++) {
162             if (n < offsets[i]) return files[i - 1];
163         }
164         return files[end];
165     }
166     
167     /** Returns the actual line number corresponding to a line number
168         in the line number table attribute.
169     
170         @param n the source line in question, from the LineNumberTable
171         attribute.
172
173         @return the line number in its true source file.
174     */

175     public int getCorrespondingLineNumber(int n) {
176         int end = offsets.length - 1;
177         for (int i = 1; i <= end; i++) {
178             if (n < offsets[i]) return n - offsets[i - 1];
179         }
180         return n - offsets[end];
181     }
182
183     /** Returns the fictitious line number corresponding to a line
184         number of a particular file. Users of this method should
185         first check that this classfile actually has code
186         corresponding to the source file + line number pair. This
187         procedure will signal an exception if it has no code from the
188         source file, but will return an erronious result if it has
189         code from the source file, but no code from the passed in line
190         number.
191
192         @param f the source file containing the line number
193         @param n the line number in question
194         
195         @return a line number suitable for setting a breakpoint
196         @throws IllegalArgumentException if this classfile contains no
197         code from the passed in source file.
198
199     */

200     public int getCorrespondingLineNumber(File JavaDoc f, int n) {
201         for (int i = 0, len = files.length; i < len; i++) {
202             if (f.equals(files[i])) {
203                 return offsets[i] + n;
204             }
205         }
206         throw new IllegalArgumentException JavaDoc("No code from "
207                                            + f
208                                            + " in this classfile");
209     }
210
211     // ------------------------------
212
// fields
213

214     private File JavaDoc[] files;
215     private int[] offsets;
216
217     // ------------------------------
218
// debugging
219

220     public String JavaDoc toString() {
221         String JavaDoc s = "";
222         for (int i = 0; i < files.length - 1; i++) {
223             s += offsets[i] + " " + files[i] + ":";
224         }
225         s += offsets[files.length - 1] + " "
226             + files[files.length - 1];
227         return s;
228     }
229
230     public static void main(String JavaDoc[] args) {
231         System.err.println(new LineNumberTableMapper(args[0], "", new File JavaDoc(".")));
232     }
233 }
234
Popular Tags