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    }