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