View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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 /* Some debugging code
182         for (int i = 0; i < lineData.size(); i++) {
183             LineInfo li = (LineInfo)lineData.get(i);
184             System.out.print(li.toString());
185         }
186 */
187         //Incorporate each LineInfo into the previous LineInfo's 
188         //outputLineIncrement, if possible
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         //Incorporate each LineInfo into the previous LineInfo's
211         //inputLineCount, if possible
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         // check the input - what are you doing here??
260         int fileIndex = filePathList.indexOf(inputFileName);
261         if (fileIndex == -1) // still
262             throw new IllegalArgumentException(
263                     "inputFileName: " + inputFileName);
264 
265         //Jasper incorrectly SMAPs certain Nodes, giving them an 
266         //outputStartLine of 0.  This can cause a fatal error in
267         //optimizeLineSection, making it impossible for Jasper to
268         //compile the JSP.  Until we can fix the underlying
269         //SMAPping problem, we simply ignore the flawed SMAP entries.
270         if (outputStartLine == 0)
271             return;
272 
273         // build the LineInfo
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         // save it
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         // check state and initialize buffer
303         if (fileNameList.size() == 0 || lineData.size() == 0)
304             return null;
305 
306         StringBuffer out = new StringBuffer();
307 
308         // print StratumSection
309         out.append("*S " + stratumName + "\n");
310 
311         // print FileSection
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                 // Source paths must be relative, not absolute, so we
318                 // remove the leading "/", if one exists.
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         // print LineSection
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 }