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 * http://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 * Gets the end column on the line that triggered the error
34 * @return the end column on the line that triggered the error
35 */
36 int end();
37
38 /**
39 * Gets the start column on the line that triggered the error
40 * @return the start column on the line that triggered the error
41 */
42 int start();
43
44 /**
45 * Gets the code that triggered the error
46 * @return the actual part of code that triggered the error
47 */
48 @Override
49 String toString();
50 }
51
52 /**
53 * Gets the info from a script.
54 * @param script the script
55 * @return the info
56 */
57 public static JexlInfo from(final JexlScript script) {
58 return script instanceof Script? ((Script) script).getInfo() : null;
59 }
60
61 /** Line number. */
62 private final int line;
63
64 /** Column number. */
65 private final int column;
66
67 /** Name. */
68 private final String name;
69
70 /**
71 * Create an information structure for dynamic set/get/invoke/new.
72 * <p>This gathers the class, method and line number of the first calling method
73 * outside of o.a.c.jexl3.</p>
74 */
75 public JexlInfo() {
76 final StackTraceElement[] stack = new Throwable().getStackTrace();
77 String cname = getClass().getName();
78 final String pkgname = getClass().getPackage().getName();
79 StackTraceElement se = null;
80 for (int s = 1; s < stack.length; ++s) {
81 se = stack[s];
82 final String className = se.getClassName();
83 if (!className.equals(cname)) {
84 // go deeper if called from jexl implementation classes
85 if (!className.startsWith(pkgname + ".internal.") && !className.startsWith(pkgname + ".Jexl")
86 && !className.startsWith(pkgname + ".parser")) {
87 break;
88 }
89 cname = className;
90 }
91 }
92 this.name = se != null ? se.getClassName() + "." + se.getMethodName() + ":" + se.getLineNumber() : "?";
93 this.line = 1;
94 this.column = 1;
95 }
96
97 /**
98 * The copy constructor.
99 *
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