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 */
017package org.apache.commons.jexl2;
018
019import 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 */
026public 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}