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.") && !className.startsWith(pkgname + ".Jexl")
91 && !className.startsWith(pkgname + ".parser")) {
92 break;
93 }
94 cname = className;
95 }
96 }
97 this.name = se != null ? se.getClassName() + "." + se.getMethodName() + ":" + se.getLineNumber() : "?";
98 this.line = 1;
99 this.column = 1;
100 }
101
102 /**
103 * The copy constructor.
104 *
105 * @param copy the instance to copy
106 */
107 protected JexlInfo(final JexlInfo copy) {
108 this(copy.getName(), copy.getLine(), copy.getColumn());
109 }
110
111 /**
112 * Create info.
113 *
114 * @param source source name
115 * @param l line number
116 * @param c column number
117 */
118 public JexlInfo(final String source, final int l, final int c) {
119 name = source;
120 line = l <= 0? 1: l;
121 column = c <= 0? 1 : c;
122 }
123
124 /**
125 * Creates info reusing the name.
126 *
127 * @param l the line
128 * @param c the column
129 * @return a new info instance
130 */
131 public JexlInfo at(final int l, final int c) {
132 return new JexlInfo(name, l, c);
133 }
134
135 /**
136 * Gets this instance or a copy without any decorations
137 *
138 * @return {@code this} instance or a copy without any decorations
139 */
140 public JexlInfo detach() {
141 return this;
142 }
143
144 /**
145 * Gets the column number.
146 *
147 * @return the column.
148 */
149 public final int getColumn() {
150 return column;
151 }
152
153 /**
154 * Gets error detail
155 *
156 * @return the detailed information in case of an error
157 */
158 public Detail getDetail() {
159 return null;
160 }
161
162 /**
163 * Gets the line number.
164 *
165 * @return line number.
166 */
167 public final int getLine() {
168 return line;
169 }
170
171 /**
172 * Gets the file/script/url name.
173 *
174 * @return template name
175 */
176 public final String getName() {
177 return name;
178 }
179
180 /**
181 * Formats this info in the form 'name@line:column'.
182 *
183 * @return the formatted info
184 */
185 @Override
186 public String toString() {
187 final StringBuilder sb = new StringBuilder(name != null ? name : "");
188 sb.append("@");
189 sb.append(line);
190 sb.append(":");
191 sb.append(column);
192 final JexlInfo.Detail dbg = getDetail();
193 if (dbg!= null) {
194 sb.append("![");
195 sb.append(dbg.start());
196 sb.append(",");
197 sb.append(dbg.end());
198 sb.append("]: '");
199 sb.append(dbg.toString());
200 sb.append("'");
201 }
202 return sb.toString();
203 }
204 }
205