View Javadoc

1   package net.sourceforge.pmd.lang.vm.ast;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.    
20   */
21  
22  import net.sourceforge.pmd.lang.vm.util.LogUtil;
23  
24  /**
25   * This is an extension of the ParseException, which also takes a template name.
26   * 
27   * @see org.apache.velocity.runtime.parser.ParseException
28   * 
29   * @author <a href="hps@intermeta.de">Henning P. Schmiedehausen</a>
30   * @version $Id: TemplateParseException.java 703544 2008-10-10 18:15:53Z nbubna $
31   * @since 1.5
32   */
33  public class TemplateParseException extends ParseException {
34      private static final long serialVersionUID = -3146323135623083918L;
35  
36      /**
37       * This is the name of the template which contains the parsing error, or null if not defined.
38       */
39      private final String templateName;
40      
41      private boolean specialConstructor = false;
42  
43      /**
44       * This constructor is used to add a template name to info cribbed from a ParseException generated in the parser.
45       * 
46       * @param currentTokenVal
47       * @param expectedTokenSequencesVal
48       * @param tokenImageVal
49       * @param templateNameVal
50       */
51      public TemplateParseException(final Token currentTokenVal, final int[][] expectedTokenSequencesVal,
52              final String[] tokenImageVal, final String templateNameVal) {
53          super(currentTokenVal, expectedTokenSequencesVal, tokenImageVal);
54          this.templateName = templateNameVal;
55          this.specialConstructor = true;
56      }
57  
58      /**
59       * This constructor is used by the method "generateParseException" in the generated parser. Calling this constructor
60       * generates a new object of this type with the fields "currentToken", "expectedTokenSequences", and "tokenImage"
61       * set. The boolean flag "specialConstructor" is also set to true to indicate that this constructor was used to
62       * create this object. This constructor calls its super class with the empty string to force the "toString" method
63       * of parent class "Throwable" to print the error message in the form: ParseException: <result of getMessage>
64       * 
65       * @param currentTokenVal
66       * @param expectedTokenSequencesVal
67       * @param tokenImageVal
68       */
69      public TemplateParseException(final Token currentTokenVal, final int[][] expectedTokenSequencesVal,
70              final String[] tokenImageVal) {
71          super(currentTokenVal, expectedTokenSequencesVal, tokenImageVal);
72          templateName = "*unset*";
73          this.specialConstructor = true;
74      }
75  
76      /**
77       * The following constructors are for use by you for whatever purpose you can think of. Constructing the exception
78       * in this manner makes the exception behave in the normal way - i.e., as documented in the class "Throwable". The
79       * fields "errorToken", "expectedTokenSequences", and "tokenImage" do not contain relevant information. The JavaCC
80       * generated code does not use these constructors.
81       */
82      public TemplateParseException() {
83          super();
84          templateName = "*unset*";
85      }
86  
87      /**
88       * Creates a new TemplateParseException object.
89       * 
90       * @param message TODO: DOCUMENT ME!
91       */
92      public TemplateParseException(final String message) {
93          super(message);
94          templateName = "*unset*";
95      }
96  
97      /**
98       * returns the Template name where this exception occured.
99       * 
100      * @return The Template name where this exception occured.
101      */
102     public String getTemplateName() {
103         return templateName;
104     }
105 
106     /**
107      * returns the line number where this exception occured.
108      * 
109      * @return The line number where this exception occured.
110      */
111     public int getLineNumber() {
112         if ((currentToken != null) && (currentToken.next != null)) {
113             return currentToken.next.beginLine;
114         }
115         else {
116             return -1;
117         }
118     }
119 
120     /**
121      * returns the column number where this exception occured.
122      * 
123      * @return The column number where this exception occured.
124      */
125     public int getColumnNumber() {
126         if ((currentToken != null) && (currentToken.next != null)) {
127             return currentToken.next.beginColumn;
128         }
129         else {
130             return -1;
131         }
132     }
133 
134     /**
135      * This method has the standard behavior when this object has been created using the standard constructors.
136      * Otherwise, it uses "currentToken" and "expectedTokenSequences" to generate a parse error message and returns it.
137      * If this object has been created due to a parse error, and you do not catch it (it gets thrown from the parser),
138      * then this method is called during the printing of the final stack trace, and hence the correct error message gets
139      * displayed.
140      * 
141      * @return The error message.
142      */
143     @Override
144     public String getMessage() {
145         if (!specialConstructor) {
146             final StringBuffer sb = new StringBuffer(super.getMessage());
147             appendTemplateInfo(sb);
148             return sb.toString();
149         }
150 
151         int maxSize = 0;
152 
153         final StringBuffer expected = new StringBuffer();
154 
155         for (int i = 0; i < expectedTokenSequences.length; i++) {
156             if (maxSize < expectedTokenSequences[i].length) {
157                 maxSize = expectedTokenSequences[i].length;
158             }
159 
160             for (int j = 0; j < expectedTokenSequences[i].length; j++) {
161                 expected.append(tokenImage[expectedTokenSequences[i][j]]).append(" ");
162             }
163 
164             if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
165                 expected.append("...");
166             }
167 
168             expected.append(eol).append("    ");
169         }
170 
171         final StringBuffer retval = new StringBuffer("Encountered \"");
172         Token tok = currentToken.next;
173 
174         for (int i = 0; i < maxSize; i++) {
175             if (i != 0) {
176                 retval.append(" ");
177             }
178 
179             if (tok.kind == 0) {
180                 retval.append(tokenImage[0]);
181                 break;
182             }
183 
184             retval.append(add_escapes(tok.image));
185             tok = tok.next;
186         }
187 
188         retval.append("\" at ");
189         appendTemplateInfo(retval);
190 
191         if (expectedTokenSequences.length == 1) {
192             retval.append("Was expecting:").append(eol).append("    ");
193         }
194         else {
195             retval.append("Was expecting one of:").append(eol).append("    ");
196         }
197 
198         // avoid JDK 1.3 StringBuffer.append(Object o) vs 1.4 StringBuffer.append(StringBuffer sb) gotcha.
199         retval.append(expected.toString());
200         return retval.toString();
201     }
202 
203     /**
204      * @param sb
205      */
206     protected void appendTemplateInfo(final StringBuffer sb) {
207         sb.append(LogUtil.formatFileString(getTemplateName(), getLineNumber(), getColumnNumber()));
208         sb.append(eol);
209     }
210 }