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