001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.jasper.compiler;
018:
019: import java.util.List;
020: import java.util.ArrayList;
021:
022: /**
023: * Represents the line and file mappings associated with a JSR-045
024: * "stratum".
025: *
026: * @author Jayson Falkner
027: * @author Shawn Bayern
028: */
029: public class SmapStratum {
030:
031: //*********************************************************************
032: // Class for storing LineInfo data
033:
034: /**
035: * Represents a single LineSection in an SMAP, associated with
036: * a particular stratum.
037: */
038: public static class LineInfo {
039: private int inputStartLine = -1;
040: private int outputStartLine = -1;
041: private int lineFileID = 0;
042: private int inputLineCount = 1;
043: private int outputLineIncrement = 1;
044: private boolean lineFileIDSet = false;
045:
046: /** Sets InputStartLine. */
047: public void setInputStartLine(int inputStartLine) {
048: if (inputStartLine < 0)
049: throw new IllegalArgumentException("" + inputStartLine);
050: this .inputStartLine = inputStartLine;
051: }
052:
053: /** Sets OutputStartLine. */
054: public void setOutputStartLine(int outputStartLine) {
055: if (outputStartLine < 0)
056: throw new IllegalArgumentException("" + outputStartLine);
057: this .outputStartLine = outputStartLine;
058: }
059:
060: /**
061: * Sets lineFileID. Should be called only when different from
062: * that of prior LineInfo object (in any given context) or 0
063: * if the current LineInfo has no (logical) predecessor.
064: * <tt>LineInfo</tt> will print this file number no matter what.
065: */
066: public void setLineFileID(int lineFileID) {
067: if (lineFileID < 0)
068: throw new IllegalArgumentException("" + lineFileID);
069: this .lineFileID = lineFileID;
070: this .lineFileIDSet = true;
071: }
072:
073: /** Sets InputLineCount. */
074: public void setInputLineCount(int inputLineCount) {
075: if (inputLineCount < 0)
076: throw new IllegalArgumentException("" + inputLineCount);
077: this .inputLineCount = inputLineCount;
078: }
079:
080: /** Sets OutputLineIncrement. */
081: public void setOutputLineIncrement(int outputLineIncrement) {
082: if (outputLineIncrement < 0)
083: throw new IllegalArgumentException(""
084: + outputLineIncrement);
085: this .outputLineIncrement = outputLineIncrement;
086: }
087:
088: /**
089: * Retrieves the current LineInfo as a String, print all values
090: * only when appropriate (but LineInfoID if and only if it's been
091: * specified, as its necessity is sensitive to context).
092: */
093: public String getString() {
094: if (inputStartLine == -1 || outputStartLine == -1)
095: throw new IllegalStateException();
096: StringBuffer out = new StringBuffer();
097: out.append(inputStartLine);
098: if (lineFileIDSet)
099: out.append("#" + lineFileID);
100: if (inputLineCount != 1)
101: out.append("," + inputLineCount);
102: out.append(":" + outputStartLine);
103: if (outputLineIncrement != 1)
104: out.append("," + outputLineIncrement);
105: out.append('\n');
106: return out.toString();
107: }
108:
109: public String toString() {
110: return getString();
111: }
112: }
113:
114: //*********************************************************************
115: // Private state
116:
117: private String stratumName;
118: private List fileNameList;
119: private List filePathList;
120: private List lineData;
121: private int lastFileID;
122:
123: //*********************************************************************
124: // Constructor
125:
126: /**
127: * Constructs a new SmapStratum object for the given stratum name
128: * (e.g., JSP).
129: *
130: * @param stratumName the name of the stratum (e.g., JSP)
131: */
132: public SmapStratum(String stratumName) {
133: this .stratumName = stratumName;
134: fileNameList = new ArrayList();
135: filePathList = new ArrayList();
136: lineData = new ArrayList();
137: lastFileID = 0;
138: }
139:
140: //*********************************************************************
141: // Methods to add mapping information
142:
143: /**
144: * Adds record of a new file, by filename.
145: *
146: * @param filename the filename to add, unqualified by path.
147: */
148: public void addFile(String filename) {
149: addFile(filename, filename);
150: }
151:
152: /**
153: * Adds record of a new file, by filename and path. The path
154: * may be relative to a source compilation path.
155: *
156: * @param filename the filename to add, unqualified by path
157: * @param filePath the path for the filename, potentially relative
158: * to a source compilation path
159: */
160: public void addFile(String filename, String filePath) {
161: int pathIndex = filePathList.indexOf(filePath);
162: if (pathIndex == -1) {
163: fileNameList.add(filename);
164: filePathList.add(filePath);
165: }
166: }
167:
168: /**
169: * Combines consecutive LineInfos wherever possible
170: */
171: public void optimizeLineSection() {
172:
173: /* Some debugging code
174: for (int i = 0; i < lineData.size(); i++) {
175: LineInfo li = (LineInfo)lineData.get(i);
176: System.out.print(li.toString());
177: }
178: */
179: //Incorporate each LineInfo into the previous LineInfo's
180: //outputLineIncrement, if possible
181: int i = 0;
182: while (i < lineData.size() - 1) {
183: LineInfo li = (LineInfo) lineData.get(i);
184: LineInfo liNext = (LineInfo) lineData.get(i + 1);
185: if (!liNext.lineFileIDSet
186: && liNext.inputStartLine == li.inputStartLine
187: && liNext.inputLineCount == 1
188: && li.inputLineCount == 1
189: && liNext.outputStartLine == li.outputStartLine
190: + li.inputLineCount
191: * li.outputLineIncrement) {
192: li.setOutputLineIncrement(liNext.outputStartLine
193: - li.outputStartLine
194: + liNext.outputLineIncrement);
195: lineData.remove(i + 1);
196: } else {
197: i++;
198: }
199: }
200:
201: //Incorporate each LineInfo into the previous LineInfo's
202: //inputLineCount, if possible
203: i = 0;
204: while (i < lineData.size() - 1) {
205: LineInfo li = (LineInfo) lineData.get(i);
206: LineInfo liNext = (LineInfo) lineData.get(i + 1);
207: if (!liNext.lineFileIDSet
208: && liNext.inputStartLine == li.inputStartLine
209: + li.inputLineCount
210: && liNext.outputLineIncrement == li.outputLineIncrement
211: && liNext.outputStartLine == li.outputStartLine
212: + li.inputLineCount
213: * li.outputLineIncrement) {
214: li.setInputLineCount(li.inputLineCount
215: + liNext.inputLineCount);
216: lineData.remove(i + 1);
217: } else {
218: i++;
219: }
220: }
221: }
222:
223: /**
224: * Adds complete information about a simple line mapping. Specify
225: * all the fields in this method; the back-end machinery takes care
226: * of printing only those that are necessary in the final SMAP.
227: * (My view is that fields are optional primarily for spatial efficiency,
228: * not for programmer convenience. Could always add utility methods
229: * later.)
230: *
231: * @param inputStartLine starting line in the source file
232: * (SMAP <tt>InputStartLine</tt>)
233: * @param inputFileName the filepath (or name) from which the input comes
234: * (yields SMAP <tt>LineFileID</tt>) Use unqualified names
235: * carefully, and only when they uniquely identify a file.
236: * @param inputLineCount the number of lines in the input to map
237: * (SMAP <tt>LineFileCount</tt>)
238: * @param outputStartLine starting line in the output file
239: * (SMAP <tt>OutputStartLine</tt>)
240: * @param outputLineIncrement number of output lines to map to each
241: * input line (SMAP <tt>OutputLineIncrement</tt>). <i>Given the
242: * fact that the name starts with "output", I continuously have
243: * the subconscious urge to call this field
244: * <tt>OutputLineExcrement</tt>.</i>
245: */
246: public void addLineData(int inputStartLine, String inputFileName,
247: int inputLineCount, int outputStartLine,
248: int outputLineIncrement) {
249: // check the input - what are you doing here??
250: int fileIndex = filePathList.indexOf(inputFileName);
251: if (fileIndex == -1) // still
252: throw new IllegalArgumentException("inputFileName: "
253: + inputFileName);
254:
255: //Jasper incorrectly SMAPs certain Nodes, giving them an
256: //outputStartLine of 0. This can cause a fatal error in
257: //optimizeLineSection, making it impossible for Jasper to
258: //compile the JSP. Until we can fix the underlying
259: //SMAPping problem, we simply ignore the flawed SMAP entries.
260: if (outputStartLine == 0)
261: return;
262:
263: // build the LineInfo
264: LineInfo li = new LineInfo();
265: li.setInputStartLine(inputStartLine);
266: li.setInputLineCount(inputLineCount);
267: li.setOutputStartLine(outputStartLine);
268: li.setOutputLineIncrement(outputLineIncrement);
269: if (fileIndex != lastFileID)
270: li.setLineFileID(fileIndex);
271: lastFileID = fileIndex;
272:
273: // save it
274: lineData.add(li);
275: }
276:
277: //*********************************************************************
278: // Methods to retrieve information
279:
280: /**
281: * Returns the name of the stratum.
282: */
283: public String getStratumName() {
284: return stratumName;
285: }
286:
287: /**
288: * Returns the given stratum as a String: a StratumSection,
289: * followed by at least one FileSection and at least one LineSection.
290: */
291: public String getString() {
292: // check state and initialize buffer
293: if (fileNameList.size() == 0 || lineData.size() == 0)
294: return null;
295:
296: StringBuffer out = new StringBuffer();
297:
298: // print StratumSection
299: out.append("*S " + stratumName + "\n");
300:
301: // print FileSection
302: out.append("*F\n");
303: int bound = fileNameList.size();
304: for (int i = 0; i < bound; i++) {
305: if (filePathList.get(i) != null) {
306: out.append("+ " + i + " " + fileNameList.get(i) + "\n");
307: // Source paths must be relative, not absolute, so we
308: // remove the leading "/", if one exists.
309: String filePath = (String) filePathList.get(i);
310: if (filePath.startsWith("/")) {
311: filePath = filePath.substring(1);
312: }
313: out.append(filePath + "\n");
314: } else {
315: out.append(i + " " + fileNameList.get(i) + "\n");
316: }
317: }
318:
319: // print LineSection
320: out.append("*L\n");
321: bound = lineData.size();
322: for (int i = 0; i < bound; i++) {
323: LineInfo li = (LineInfo) lineData.get(i);
324: out.append(li.getString());
325: }
326:
327: return out.toString();
328: }
329:
330: public String toString() {
331: return getString();
332: }
333:
334: }
|