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 /** 029 * Describes errors more precisely. 030 */ 031 public interface Detail { 032 /** 033 * @return the end column on the line that triggered the error 034 */ 035 int end(); 036 037 /** 038 * @return the start column on the line that triggered the error 039 */ 040 int start(); 041 042 /** 043 * @return the actual part of code that triggered the error 044 */ 045 046 @Override 047 String toString(); 048 } 049 050 /** 051 * Gets the info from a script. 052 * @param script the script 053 * @return the info 054 */ 055 public static JexlInfo from(final JexlScript script) { 056 return script instanceof Script? ((Script) script).getInfo() : null; 057 } 058 059 /** Line number. */ 060 private final int line; 061 062 /** Column number. */ 063 private final int column; 064 065 /** Name. */ 066 private final String name; 067 068 /** 069 * Create an information structure for dynamic set/get/invoke/new. 070 * <p>This gathers the class, method and line number of the first calling method 071 * outside of o.a.c.jexl3.</p> 072 */ 073 public JexlInfo() { 074 final StackTraceElement[] stack = new Throwable().getStackTrace(); 075 String cname = getClass().getName(); 076 final String pkgname = getClass().getPackage().getName(); 077 StackTraceElement se = null; 078 for (int s = 1; s < stack.length; ++s) { 079 se = stack[s]; 080 final String className = se.getClassName(); 081 if (!className.equals(cname)) { 082 // go deeper if called from jexl implementation classes 083 if (!className.startsWith(pkgname + ".internal.") && !className.startsWith(pkgname + ".Jexl") 084 && !className.startsWith(pkgname + ".parser")) { 085 break; 086 } 087 cname = className; 088 } 089 } 090 this.name = se != null ? se.getClassName() + "." + se.getMethodName() + ":" + se.getLineNumber() : "?"; 091 this.line = 1; 092 this.column = 1; 093 } 094 095 /** 096 * The copy constructor. 097 * 098 * @param copy the instance to copy 099 */ 100 protected JexlInfo(final JexlInfo copy) { 101 this(copy.getName(), copy.getLine(), copy.getColumn()); 102 } 103 104 /** 105 * Create info. 106 * 107 * @param source source name 108 * @param l line number 109 * @param c column number 110 */ 111 public JexlInfo(final String source, final int l, final int c) { 112 name = source; 113 line = l <= 0? 1: l; 114 column = c <= 0? 1 : c; 115 } 116 117 /** 118 * Creates info reusing the name. 119 * 120 * @param l the line 121 * @param c the column 122 * @return a new info instance 123 */ 124 public JexlInfo at(final int l, final int c) { 125 return new JexlInfo(name, l, c); 126 } 127 128 /** 129 * @return this instance or a copy without any decorations 130 */ 131 public JexlInfo detach() { 132 return this; 133 } 134 135 /** 136 * Gets the column number. 137 * 138 * @return the column. 139 */ 140 public final int getColumn() { 141 return column; 142 } 143 144 /** 145 * @return the detailed information in case of an error 146 */ 147 public Detail getDetail() { 148 return null; 149 } 150 151 /** 152 * Gets the line number. 153 * 154 * @return line number. 155 */ 156 public final int getLine() { 157 return line; 158 } 159 160 /** 161 * Gets the file/script/url name. 162 * 163 * @return template name 164 */ 165 public final String getName() { 166 return name; 167 } 168 169 /** 170 * Formats this info in the form 'name@line:column'. 171 * 172 * @return the formatted info 173 */ 174 @Override 175 public String toString() { 176 final StringBuilder sb = new StringBuilder(name != null ? name : ""); 177 sb.append("@"); 178 sb.append(line); 179 sb.append(":"); 180 sb.append(column); 181 final JexlInfo.Detail dbg = getDetail(); 182 if (dbg!= null) { 183 sb.append("!["); 184 sb.append(dbg.start()); 185 sb.append(","); 186 sb.append(dbg.end()); 187 sb.append("]: '"); 188 sb.append(dbg.toString()); 189 sb.append("'"); 190 } 191 return sb.toString(); 192 } 193} 194