    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 java.math.MathContext;
    020
    021    /**
    022     * A derived arithmetic that allows different threads to operate with
    023     * different strict/lenient/math modes using the same JexlEngine.
    024     * @since 2.1
    025     */
    026    public class JexlThreadedArithmetic extends JexlArithmetic {
    027
    028        /** Holds the threaded version of some arithmetic features. */
    029        static class Features {
    030            /** Default ctor. */
    031            Features() {}
    032            /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */
    033            private Boolean lenient = null;
    034            /** The big decimal math context. */
    035            private MathContext mathContext = null;
    036            /** The big decimal scale. */
    037            private Integer mathScale = null;
    038        }
    039
    040        /**
    041         * Standard ctor.
    042         * @param lenient lenient versus strict evaluation flag
    043         */
    044        public JexlThreadedArithmetic(boolean lenient) {
    045            super(lenient);
    046        }
    047
    048        /**
    049         * Creates a JexlThreadedArithmetic instance.
    050         * @param lenient whether this arithmetic is lenient or strict
    051         * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
    052         * @param bigdScale the scale used for big decimals.
    053         */
    054        public JexlThreadedArithmetic(boolean lenient, MathContext bigdContext, int bigdScale) {
    055            super(lenient, bigdContext, bigdScale);
    056        }
    057
    058        /** Whether this JexlArithmetic instance behaves in strict or lenient mode for this thread. */
    059        static final ThreadLocal<Features> FEATURES = new ThreadLocal<Features>() {
    060            @Override
    061            protected synchronized Features initialValue() {
    062                return new Features();
    063            }
    064        };
    065
    066
    067        /**
    068         * Overrides the default behavior and sets whether this JexlArithmetic instance triggers errors
    069         * during evaluation when null is used as an operand for the current thread.
    070         * <p>It is advised to protect calls by either calling JexlThreadedArithmetic.setLenient explicitly
    071         * before evaluation or add a try/finally clause resetting the flag to avoid unexpected reuse of the lenient
    072         * flag value through thread pools side-effects.</p>
    073         * @see JexlEngine#setSilent
    074         * @see JexlEngine#setDebug
    075         * @param flag true means no JexlException will occur, false allows them, null reverts to default behavior
    076         */
    077        public static void setLenient(Boolean flag) {
    078            FEATURES.get().lenient = flag;
    079        }
    080
    081        /**
    082         * Sets the math scale.
    083         * <p>The goal and constraints are the same than for setLenient.</p>
    084         * @param scale the scale
    085         */
    086        public static void setMathScale(Integer scale) {
    087            FEATURES.get().mathScale = scale;
    088        }
    089
    090        /**
    091         * Sets the math context.
    092         * <p>The goal and constraints are the same than for setLenient.</p>
    093         * @param mc the math context
    094         */
    095        public static void setMathContext(MathContext mc) {
    096            FEATURES.get().mathContext = mc;
    097        }
    098
    099        /** {@inheritDoc} */
    100        @Override
    101        public boolean isLenient() {
    102            Boolean lenient = FEATURES.get().lenient;
    103            return lenient == null ? super.isLenient() : lenient.booleanValue();
    104        }
    105
    106        @Override
    107        public int getMathScale() {
    108            Integer scale = FEATURES.get().mathScale;
    109            return scale == null ? super.getMathScale() : scale.intValue();
    110        }
    111
    112        @Override
    113        public MathContext getMathContext() {
    114            MathContext mc = FEATURES.get().mathContext;
    115            return mc == null? super.getMathContext() : mc;
    116        }
    117    }