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    }