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