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 }