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 package org.apache.commons.jexl2;
018
019 import org.apache.commons.jexl2.parser.JexlNode;
020
021 /**
022 * Wraps any error that might occur during interpretation of a script or expression.
023 * @since 2.0
024 */
025 public class JexlException extends RuntimeException {
026 /** Serial version UID. */
027 private static final long serialVersionUID = 2690666400232612395L;
028 /** The point of origin for this exception. */
029 protected final JexlNode mark;
030 /** The debug info. */
031 protected final JexlInfo info;
032 /** A marker to use in NPEs stating a null operand error. */
033 public static final String NULL_OPERAND = "jexl.null";
034 /**
035 * Creates a new JexlException.
036 * @param node the node causing the error
037 * @param msg the error message
038 */
039 public JexlException(JexlNode node, String msg) {
040 super(msg);
041 mark = node;
042 info = node != null? node.getInfo() : null;
043
044 }
045
046 /**
047 * Creates a new JexlException.
048 * @param node the node causing the error
049 * @param msg the error message
050 * @param cause the exception causing the error
051 */
052 public JexlException(JexlNode node, String msg, Throwable cause) {
053 super(msg, cause);
054 mark = node;
055 info = node != null? node.getInfo() : null;
056 }
057
058 /**
059 * Creates a new JexlException.
060 * @param dbg the debugging information associated
061 * @param msg the error message
062 */
063 public JexlException(JexlInfo dbg, String msg) {
064 super(msg);
065 mark = null;
066 info = dbg;
067 }
068
069 /**
070 * Creates a new JexlException.
071 * @param dbg the debugging information associated
072 * @param msg the error message
073 * @param cause the exception causing the error
074 */
075 public JexlException(JexlInfo dbg, String msg, Throwable cause) {
076 super(msg, cause);
077 mark = null;
078 info = dbg;
079 }
080
081 /**
082 * Gets information about the cause of this error.
083 * <p>
084 * The returned string represents the outermost expression in error.
085 * The info parameter, an int[2] optionally provided by the caller, will be filled with the begin/end offset
086 * characters of the precise error's trigger.
087 * </p>
088 * @param offsets character offset interval of the precise node triggering the error
089 * @return a string representation of the offending expression, the empty string if it could not be determined
090 */
091 public String getInfo(int[] offsets) {
092 Debugger dbg = new Debugger();
093 if (dbg.debug(mark)) {
094 if (offsets != null && offsets.length >= 2) {
095 offsets[0] = dbg.start();
096 offsets[1] = dbg.end();
097 }
098 return dbg.data();
099 }
100 return "";
101 }
102
103 /**
104 * Detailed info message about this error.
105 * Format is "debug![begin,end]: string \n msg" where:
106 * - debug is the debugging information if it exists (@link JexlEngine.setDebug)
107 * - begin, end are character offsets in the string for the precise location of the error
108 * - string is the string representation of the offending expression
109 * - msg is the actual explanation message for this error
110 * @return this error as a string
111 */
112 @Override
113 public String getMessage() {
114 Debugger dbg = new Debugger();
115 StringBuilder msg = new StringBuilder();
116 if (info != null) {
117 msg.append(info.debugString());
118 }
119 if (dbg.debug(mark)) {
120 msg.append("![");
121 msg.append(dbg.start());
122 msg.append(",");
123 msg.append(dbg.end());
124 msg.append("]: '");
125 msg.append(dbg.data());
126 msg.append("'");
127 }
128 msg.append(' ');
129 msg.append(super.getMessage());
130 Throwable cause = getCause();
131 if (cause != null && NULL_OPERAND == cause.getMessage()) {
132 msg.append(" caused by null operand");
133 }
134 return msg.toString();
135 }
136 }