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