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 }