    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(super.getMessage());
    129            Throwable cause = getCause();
    130            if (cause != null && NULL_OPERAND == cause.getMessage()) {
    131                msg.append(" caused by null operand");
    132            }
    133            return msg.toString();
    134        }
    135    }