1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.jexl3;
19
20 import org.apache.commons.jexl3.internal.Script;
21
22 /**
23 * Helper class to carry information such as a url/file name, line and column for
24 * debugging information reporting.
25 */
26 public class JexlInfo {
27
28 /**
29 * Describes errors more precisely.
30 */
31 public interface Detail {
32
33 /**
34 * Gets the end column on the line that triggered the error
35 *
36 * @return the end column on the line that triggered the error
37 */
38 int end();
39
40 /**
41 * Gets the start column on the line that triggered the error
42 *
43 * @return the start column on the line that triggered the error
44 */
45 int start();
46
47 /**
48 * Gets the code that triggered the error
49 *
50 * @return the actual part of code that triggered the error
51 */
52 @Override
53 String toString();
54 }
55
56 /**
57 * Gets the info from a script.
58 *
59 * @param script the script
60 * @return the info
61 */
62 public static JexlInfo from(final JexlScript script) {
63 return script instanceof Script? ((Script) script).getInfo() : null;
64 }
65
66 /** Line number. */
67 private final int line;
68
69 /** Column number. */
70 private final int column;
71
72 /** Name. */
73 private final String name;
74
75 /**
76 * Create an information structure for dynamic set/get/invoke/new.
77 * <p>This gathers the class, method and line number of the first calling method
78 * outside of o.a.c.jexl3.</p>
79 */
80 public JexlInfo() {
81 final StackTraceElement[] stack = new Throwable().getStackTrace();
82 String cname = getClass().getName();
83 final String pkgname = getClass().getPackage().getName();
84 StackTraceElement se = null;
85 for (int s = 1; s < stack.length; ++s) {
86 se = stack[s];
87 final String className = se.getClassName();
88 if (!className.equals(cname)) {
89 // go deeper if called from jexl implementation classes
90 if (!className.startsWith(pkgname + ".internal.")
91 && !className.startsWith(pkgname + ".Jexl")
92 && !className.startsWith(pkgname + ".Jxlt")
93 && !className.startsWith(pkgname + ".parser")) {
94 break;
95 }
96 cname = className;
97 }
98 }
99 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