1 package org.apache.velocity.runtime.parser;
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 org.apache.velocity.exception.ExtendedParseException;
23
24
25 /**
26 * This is an extension of the ParseException, which also takes a
27 * template name.
28 *
29 * @see org.apache.velocity.runtime.parser.ParseException
30 *
31 * @author <a href="hps@intermeta.de">Henning P. Schmiedehausen</a>
32 * @version $Id: TemplateParseException.java 463298 2006-10-12 16:10:32Z henning $
33 */
34 public class TemplateParseException
35 extends ParseException
36 implements ExtendedParseException
37 {
38 private static final long serialVersionUID = -3146323135623083918L;
39
40 /**
41 * This is the name of the template which contains the parsing error, or
42 * null if not defined.
43 */
44 private final String templateName;
45
46 /**
47 * This constructor is used to add a template name
48 * to info cribbed from a ParseException generated in the parser.
49 * @param currentTokenVal
50 * @param expectedTokenSequencesVal
51 * @param tokenImageVal
52 * @param templateNameVal
53 */
54 public TemplateParseException(Token currentTokenVal, int [][] expectedTokenSequencesVal, String [] tokenImageVal,
55 String templateNameVal)
56 {
57 super(currentTokenVal, expectedTokenSequencesVal, tokenImageVal);
58 this.templateName = templateNameVal;
59 }
60
61 /**
62 * This constructor is used by the method "generateParseException"
63 * in the generated parser. Calling this constructor generates
64 * a new object of this type with the fields "currentToken",
65 * "expectedTokenSequences", and "tokenImage" set. The boolean
66 * flag "specialConstructor" is also set to true to indicate that
67 * this constructor was used to create this object.
68 * This constructor calls its super class with the empty string
69 * to force the "toString" method of parent class "Throwable" to
70 * print the error message in the form:
71 * ParseException: <result of getMessage>
72 * @param currentTokenVal
73 * @param expectedTokenSequencesVal
74 * @param tokenImageVal
75 */
76 public TemplateParseException(Token currentTokenVal, int [][] expectedTokenSequencesVal, String [] tokenImageVal)
77 {
78 super(currentTokenVal, expectedTokenSequencesVal, tokenImageVal);
79 templateName = "*unset*";
80 }
81
82 /**
83 * The following constructors are for use by you for whatever
84 * purpose you can think of. Constructing the exception in this
85 * manner makes the exception behave in the normal way - i.e., as
86 * documented in the class "Throwable". The fields "errorToken",
87 * "expectedTokenSequences", and "tokenImage" do not contain
88 * relevant information. The JavaCC generated code does not use
89 * these constructors.
90 */
91 public TemplateParseException()
92 {
93 super();
94 templateName = "*unset*";
95 }
96
97 /**
98 * Creates a new TemplateParseException object.
99 *
100 * @param message TODO: DOCUMENT ME!
101 */
102 public TemplateParseException(String message)
103 {
104 super(message);
105 templateName = "*unset*";
106 }
107
108 /**
109 * returns the Template name where this exception occured.
110 * @return The Template name where this exception occured.
111 */
112 public String getTemplateName()
113 {
114 return templateName;
115 }
116
117 /**
118 * returns the line number where this exception occured.
119 * @return The line number where this exception occured.
120 */
121 public int getLineNumber()
122 {
123 if ((currentToken != null) && (currentToken.next != null))
124 {
125 return currentToken.next.beginLine;
126 }
127 else
128 {
129 return -1;
130 }
131 }
132
133 /**
134 * returns the column number where this exception occured.
135 * @return The column number where this exception occured.
136 */
137 public int getColumnNumber()
138 {
139 if ((currentToken != null) && (currentToken.next != null))
140 {
141 return currentToken.next.beginColumn;
142 }
143 else
144 {
145 return -1;
146 }
147 }
148
149 /**
150 * This method has the standard behavior when this object has been
151 * created using the standard constructors. Otherwise, it uses
152 * "currentToken" and "expectedTokenSequences" to generate a parse
153 * error message and returns it. If this object has been created
154 * due to a parse error, and you do not catch it (it gets thrown
155 * from the parser), then this method is called during the printing
156 * of the final stack trace, and hence the correct error message
157 * gets displayed.
158 * @return The error message.
159 */
160 public String getMessage()
161 {
162 if (!specialConstructor)
163 {
164 StringBuffer sb = new StringBuffer(super.getMessage());
165 appendTemplateInfo(sb);
166 return sb.toString();
167 }
168
169 int maxSize = 0;
170
171 StringBuffer expected = new StringBuffer();
172
173 for (int i = 0; i < expectedTokenSequences.length; i++)
174 {
175 if (maxSize < expectedTokenSequences[i].length)
176 {
177 maxSize = expectedTokenSequences[i].length;
178 }
179
180 for (int j = 0; j < expectedTokenSequences[i].length; j++)
181 {
182 expected.append(tokenImage[expectedTokenSequences[i][j]]).append(" ");
183 }
184
185 if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0)
186 {
187 expected.append("...");
188 }
189
190 expected.append(eol).append(" ");
191 }
192
193 StringBuffer retval = new StringBuffer("Encountered \"");
194 Token tok = currentToken.next;
195
196 for (int i = 0; i < maxSize; i++)
197 {
198 if (i != 0)
199 {
200 retval.append(" ");
201 }
202
203 if (tok.kind == 0)
204 {
205 retval.append(tokenImage[0]);
206 break;
207 }
208
209 retval.append(add_escapes(tok.image));
210 tok = tok.next;
211 }
212
213 retval.append("\"");
214 appendTemplateInfo(retval);
215
216 if (expectedTokenSequences.length == 1)
217 {
218 retval.append("Was expecting:").append(eol).append(" ");
219 }
220 else
221 {
222 retval.append("Was expecting one of:").append(eol).append(" ");
223 }
224
225 // avoid JDK 1.3 StringBuffer.append(Object o) vs 1.4 StringBuffer.append(StringBuffer sb) gotcha.
226 retval.append(expected.toString());
227 return retval.toString();
228 }
229
230 /**
231 * @param sb
232 */
233 protected void appendTemplateInfo(final StringBuffer sb)
234 {
235 sb.append(" at line ").append(getLineNumber())
236 .append(", column ").append(getColumnNumber());
237
238 if (getTemplateName() != null)
239 {
240 sb.append(" of ").append(getTemplateName());
241 }
242 else
243 {
244 sb.append(".");
245 }
246 sb.append(eol);
247 }
248 }