001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.jexl3; 019 020import org.apache.commons.jexl3.internal.Script; 021 022/** 023 * Helper class to carry information such as a url/file name, line and column for 024 * debugging information reporting. 025 */ 026public class JexlInfo { 027 028 /** line number. */ 029 private final int line; 030 031 /** column number. */ 032 private final int column; 033 034 /** name. */ 035 private final String name; 036 037 /** 038 * @return the detailed information in case of an error 039 */ 040 public Detail getDetail() { 041 return null; 042 } 043 044 /** 045 * Describes errors more precisely. 046 */ 047 public interface Detail { 048 /** 049 * @return the start column on the line that triggered the error 050 */ 051 int start(); 052 053 /** 054 * @return the end column on the line that triggered the error 055 */ 056 int end(); 057 058 /** 059 * @return the actual part of code that triggered the error 060 */ 061 062 @Override 063 String toString(); 064 } 065 066 /** 067 * Create info. 068 * 069 * @param source source name 070 * @param l line number 071 * @param c column number 072 */ 073 public JexlInfo(final String source, final int l, final int c) { 074 name = source; 075 line = l; 076 column = c; 077 } 078 079 /** 080 * Create an information structure for dynamic set/get/invoke/new. 081 * <p>This gathers the class, method and line number of the first calling method 082 * outside of o.a.c.jexl3.</p> 083 */ 084 public JexlInfo() { 085 final StackTraceElement[] stack = new Throwable().getStackTrace(); 086 String cname = getClass().getName(); 087 final String pkgname = getClass().getPackage().getName(); 088 StackTraceElement se = null; 089 for (int s = 1; s < stack.length; ++s) { 090 se = stack[s]; 091 final String className = se.getClassName(); 092 if (!className.equals(cname)) { 093 // go deeper if called from jexl implementation classes 094 if (!className.startsWith(pkgname + ".internal.") && !className.startsWith(pkgname + ".Jexl") 095 && !className.startsWith(pkgname + ".parser")) { 096 break; 097 } 098 cname = className; 099 } 100 } 101 this.name = se != null ? se.getClassName() + "." + se.getMethodName() + ":" + se.getLineNumber() : "?"; 102 this.line = 0; 103 this.column = 0; 104 } 105 106 /** 107 * Creates info reusing the name. 108 * 109 * @param l the line 110 * @param c the column 111 * @return a new info instance 112 */ 113 public JexlInfo at(final int l, final int c) { 114 return new JexlInfo(name, l, c); 115 } 116 117 /** 118 * The copy constructor. 119 * 120 * @param copy the instance to copy 121 */ 122 protected JexlInfo(final JexlInfo copy) { 123 name = copy.getName(); 124 line = copy.getLine(); 125 column = copy.getColumn(); 126 } 127 128 /** 129 * Formats this info in the form 'name@line:column'. 130 * 131 * @return the formatted info 132 */ 133 @Override 134 public String toString() { 135 final StringBuilder sb = new StringBuilder(name != null? name : ""); 136 if (line > 0) { 137 sb.append("@"); 138 sb.append(line); 139 if (column > 0) { 140 sb.append(":"); 141 sb.append(column); 142 } 143 } 144 final JexlInfo.Detail dbg = getDetail(); 145 if (dbg!= null) { 146 sb.append("!["); 147 sb.append(dbg.start()); 148 sb.append(","); 149 sb.append(dbg.end()); 150 sb.append("]: '"); 151 sb.append(dbg.toString()); 152 sb.append("'"); 153 } 154 return sb.toString(); 155 } 156 157 /** 158 * Gets the file/script/url name. 159 * 160 * @return template name 161 */ 162 public final String getName() { 163 return name; 164 } 165 166 /** 167 * Gets the line number. 168 * 169 * @return line number. 170 */ 171 public final int getLine() { 172 return line; 173 } 174 175 /** 176 * Gets the column number. 177 * 178 * @return the column. 179 */ 180 public final int getColumn() { 181 return column; 182 } 183 184 /** 185 * @return this instance or a copy without any decorations 186 */ 187 public JexlInfo detach() { 188 return this; 189 } 190 191 /** 192 * Gets the info from a script. 193 * @param script the script 194 * @return the info 195 */ 196 public static JexlInfo from(final JexlScript script) { 197 return script instanceof Script? ((Script) script).getInfo() : null; 198 } 199} 200