1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.jasper.compiler;
19
20 import java.util.ArrayList;
21 import java.util.List;
22
23 /***
24 * Represents the line and file mappings associated with a JSR-045
25 * "stratum".
26 *
27 * @author Jayson Falkner
28 * @author Shawn Bayern
29 */
30 public class SmapStratum {
31
32 //**********************************************************************
33 // Class for storing LineInfo data
34
35 /**
36 * Represents a single LineSection in an SMAP, associated with
37 * a particular stratum.
38 */
39 public static class LineInfo {
40 private int inputStartLine = -1;
41 private int outputStartLine = -1;
42 private int lineFileID = 0;
43 private int inputLineCount = 1;
44 private int outputLineIncrement = 1;
45 private boolean lineFileIDSet = false;
46
47 /***
48 * Sets InputStartLine.
49 */
50 public void setInputStartLine(int inputStartLine) {
51 if (inputStartLine < 0)
52 throw new IllegalArgumentException("" + inputStartLine);
53 this.inputStartLine = inputStartLine;
54 }
55
56 /***
57 * Sets OutputStartLine.
58 */
59 public void setOutputStartLine(int outputStartLine) {
60 if (outputStartLine < 0)
61 throw new IllegalArgumentException("" + outputStartLine);
62 this.outputStartLine = outputStartLine;
63 }
64
65 /***
66 * Sets lineFileID. Should be called only when different from
67 * that of prior LineInfo object (in any given context) or 0
68 * if the current LineInfo has no (logical) predecessor.
69 * <tt>LineInfo</tt> will print this file number no matter what.
70 */
71 public void setLineFileID(int lineFileID) {
72 if (lineFileID < 0)
73 throw new IllegalArgumentException("" + lineFileID);
74 this.lineFileID = lineFileID;
75 this.lineFileIDSet = true;
76 }
77
78 /***
79 * Sets InputLineCount.
80 */
81 public void setInputLineCount(int inputLineCount) {
82 if (inputLineCount < 0)
83 throw new IllegalArgumentException("" + inputLineCount);
84 this.inputLineCount = inputLineCount;
85 }
86
87 /***
88 * Sets OutputLineIncrement.
89 */
90 public void setOutputLineIncrement(int outputLineIncrement) {
91 if (outputLineIncrement < 0)
92 throw new IllegalArgumentException("" + outputLineIncrement);
93 this.outputLineIncrement = outputLineIncrement;
94 }
95
96 /***
97 * Retrieves the current LineInfo as a String, print all values
98 * only when appropriate (but LineInfoID if and only if it's been
99 * specified, as its necessity is sensitive to context).
100 */
101 public String getString() {
102 if (inputStartLine == -1 || outputStartLine == -1)
103 throw new IllegalStateException();
104 StringBuffer out = new StringBuffer();
105 out.append(inputStartLine);
106 if (lineFileIDSet)
107 out.append("#" + lineFileID);
108 if (inputLineCount != 1)
109 out.append("," + inputLineCount);
110 out.append(":" + outputStartLine);
111 if (outputLineIncrement != 1)
112 out.append("," + outputLineIncrement);
113 out.append('\n');
114 return out.toString();
115 }
116
117 public String toString() {
118 return getString();
119 }
120 }
121
122 //**********************************************************************
123 // Private state
124
125 private String stratumName;
126 private List fileNameList;
127 private List filePathList;
128 private List lineData;
129 private int lastFileID;
130
131 //*********************************************************************
132 // Constructor
133
134 /**
135 * Constructs a new SmapStratum object for the given stratum name
136 * (e.g., JSP).
137 *
138 * @param stratumName the name of the stratum (e.g., JSP)
139 */
140 public SmapStratum(String stratumName) {
141 this.stratumName = stratumName;
142 fileNameList = new ArrayList();
143 filePathList = new ArrayList();
144 lineData = new ArrayList();
145 lastFileID = 0;
146 }
147
148 //**********************************************************************
149 // Methods to add mapping information
150
151 /**
152 * Adds record of a new file, by filename.
153 *
154 * @param filename the filename to add, unqualified by path.
155 */
156 public void addFile(String filename) {
157 addFile(filename, filename);
158 }
159
160 /***
161 * Adds record of a new file, by filename and path. The path
162 * may be relative to a source compilation path.
163 *
164 * @param filename the filename to add, unqualified by path
165 * @param filePath the path for the filename, potentially relative
166 * to a source compilation path
167 */
168 public void addFile(String filename, String filePath) {
169 int pathIndex = filePathList.indexOf(filePath);
170 if (pathIndex == -1) {
171 fileNameList.add(filename);
172 filePathList.add(filePath);
173 }
174 }
175
176 /***
177 * Combines consecutive LineInfos wherever possible
178 */
179 public void optimizeLineSection() {
180
181
182
183
184
185
186
187
188
189 int i = 0;
190 while (i < lineData.size() - 1) {
191 LineInfo li = (LineInfo) lineData.get(i);
192 LineInfo liNext = (LineInfo) lineData.get(i + 1);
193 if (!liNext.lineFileIDSet
194 && liNext.inputStartLine == li.inputStartLine
195 && liNext.inputLineCount == 1
196 && li.inputLineCount == 1
197 && liNext.outputStartLine
198 == li.outputStartLine
199 + li.inputLineCount * li.outputLineIncrement) {
200 li.setOutputLineIncrement(
201 liNext.outputStartLine
202 - li.outputStartLine
203 + liNext.outputLineIncrement);
204 lineData.remove(i + 1);
205 } else {
206 i++;
207 }
208 }
209
210
211
212 i = 0;
213 while (i < lineData.size() - 1) {
214 LineInfo li = (LineInfo) lineData.get(i);
215 LineInfo liNext = (LineInfo) lineData.get(i + 1);
216 if (!liNext.lineFileIDSet
217 && liNext.inputStartLine == li.inputStartLine + li.inputLineCount
218 && liNext.outputLineIncrement == li.outputLineIncrement
219 && liNext.outputStartLine
220 == li.outputStartLine
221 + li.inputLineCount * li.outputLineIncrement) {
222 li.setInputLineCount(li.inputLineCount + liNext.inputLineCount);
223 lineData.remove(i + 1);
224 } else {
225 i++;
226 }
227 }
228 }
229
230 /***
231 * Adds complete information about a simple line mapping. Specify
232 * all the fields in this method; the back-end machinery takes care
233 * of printing only those that are necessary in the final SMAP.
234 * (My view is that fields are optional primarily for spatial efficiency,
235 * not for programmer convenience. Could always add utility methods
236 * later.)
237 *
238 * @param inputStartLine starting line in the source file
239 * (SMAP <tt>InputStartLine</tt>)
240 * @param inputFileName the filepath (or name) from which the input comes
241 * (yields SMAP <tt>LineFileID</tt>) Use unqualified names
242 * carefully, and only when they uniquely identify a file.
243 * @param inputLineCount the number of lines in the input to map
244 * (SMAP <tt>LineFileCount</tt>)
245 * @param outputStartLine starting line in the output file
246 * (SMAP <tt>OutputStartLine</tt>)
247 * @param outputLineIncrement number of output lines to map to each
248 * input line (SMAP <tt>OutputLineIncrement</tt>). <i>Given the
249 * fact that the name starts with "output", I continuously have
250 * the subconscious urge to call this field
251 * <tt>OutputLineExcrement</tt>.</i>
252 */
253 public void addLineData(
254 int inputStartLine,
255 String inputFileName,
256 int inputLineCount,
257 int outputStartLine,
258 int outputLineIncrement) {
259
260 int fileIndex = filePathList.indexOf(inputFileName);
261 if (fileIndex == -1)
262 throw new IllegalArgumentException(
263 "inputFileName: " + inputFileName);
264
265
266
267
268
269
270 if (outputStartLine == 0)
271 return;
272
273
274 LineInfo li = new LineInfo();
275 li.setInputStartLine(inputStartLine);
276 li.setInputLineCount(inputLineCount);
277 li.setOutputStartLine(outputStartLine);
278 li.setOutputLineIncrement(outputLineIncrement);
279 if (fileIndex != lastFileID)
280 li.setLineFileID(fileIndex);
281 lastFileID = fileIndex;
282
283
284 lineData.add(li);
285 }
286
287 //**********************************************************************
288 // Methods to retrieve information
289
290 /**
291 * Returns the name of the stratum.
292 */
293 public String getStratumName() {
294 return stratumName;
295 }
296
297 /***
298 * Returns the given stratum as a String: a StratumSection,
299 * followed by at least one FileSection and at least one LineSection.
300 */
301 public String getString() {
302
303 if (fileNameList.size() == 0 || lineData.size() == 0)
304 return null;
305
306 StringBuffer out = new StringBuffer();
307
308
309 out.append("*S " + stratumName + "\n");
310
311
312 out.append("*F\n");
313 int bound = fileNameList.size();
314 for (int i = 0; i < bound; i++) {
315 if (filePathList.get(i) != null) {
316 out.append("+ " + i + " " + fileNameList.get(i) + "\n");
317
318
319 String filePath = (String) filePathList.get(i);
320 if (filePath.startsWith("/")) {
321 filePath = filePath.substring(1);
322 }
323 out.append(filePath + "\n");
324 } else {
325 out.append(i + " " + fileNameList.get(i) + "\n");
326 }
327 }
328
329
330 out.append("*L\n");
331 bound = lineData.size();
332 for (int i = 0; i < bound; i++) {
333 LineInfo li = (LineInfo) lineData.get(i);
334 out.append(li.getString());
335 }
336
337 return out.toString();
338 }
339
340 public String toString() {
341 return getString();
342 }
343
344 }