content: /*
   * Copyright 2010-2025 Hyland Software, Inc. and its affiliates.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.activiti.core.el.juel.misc;
  
  import jakarta.el.ELException;
  import java.math.BigDecimal;
  import java.math.BigInteger;
  
  /**
   * Arithmetic Operations as specified in chapter 1.7.
   *
   * @author Christoph Beck
   */
  public class NumberOperations {
  
      private static final Long LONG_ZERO = Long.valueOf(0L);
  
      private static final boolean isDotEe(String value) {
          int length = value.length();
          for (int i = 0; i < length; i++) {
              switch (value.charAt(i)) {
                  case '.':
                  case 'E':
                  case 'e':
                      return true;
              }
          }
          return false;
      }
  
      private static final boolean isDotEe(Object value) {
          return value instanceof String && isDotEe((String) value);
      }
  
      private static final boolean isFloatOrDouble(Object value) {
          return value instanceof Float || value instanceof Double;
      }
  
      private static final boolean isFloatOrDoubleOrDotEe(Object value) {
          return isFloatOrDouble(value) || isDotEe(value);
      }
  
      private static final boolean isBigDecimalOrBigInteger(Object value) {
          return value instanceof BigDecimal || value instanceof BigInteger;
      }
  
      private static final boolean isBigDecimalOrFloatOrDoubleOrDotEe(Object value) {
          return value instanceof BigDecimal || isFloatOrDoubleOrDotEe(value);
      }
  
      public static final Number add(TypeConverter converter, Object o1, Object o2) {
          if (o1 == null && o2 == null) {
              return LONG_ZERO;
          }
          if (o1 instanceof BigDecimal || o2 instanceof BigDecimal) {
              return converter.convert(o1, BigDecimal.class).add(converter.convert(o2, BigDecimal.class));
          }
          if (isFloatOrDoubleOrDotEe(o1) || isFloatOrDoubleOrDotEe(o2)) {
              if (o1 instanceof BigInteger || o2 instanceof BigInteger) {
                  return converter.convert(o1, BigDecimal.class).add(converter.convert(o2, BigDecimal.class));
              }
              return (converter.convert(o1, Double.class) + converter.convert(o2, Double.class));
          }
          if (o1 instanceof BigInteger || o2 instanceof BigInteger) {
              return converter.convert(o1, BigInteger.class).add(converter.convert(o2, BigInteger.class));
          }
          return (converter.convert(o1, Long.class) + converter.convert(o2, Long.class));
      }
  
      public static final Number sub(TypeConverter converter, Object o1, Object o2) {
          if (o1 == null && o2 == null) {
              return LONG_ZERO;
          }
          if (o1 instanceof BigDecimal || o2 instanceof BigDecimal) {
              return converter.convert(o1, BigDecimal.class).subtract(converter.convert(o2, BigDecimal.class));
          }
          if (isFloatOrDoubleOrDotEe(o1) || isFloatOrDoubleOrDotEe(o2)) {
              if (o1 instanceof BigInteger || o2 instanceof BigInteger) {
                  return converter.convert(o1, BigDecimal.class).subtract(converter.convert(o2, BigDecimal.class));
              }
              return (converter.convert(o1, Double.class) - converter.convert(o2, Double.class));
          }
          if (o1 instanceof BigInteger || o2 instanceof BigInteger) {
              return converter.convert(o1, BigInteger.class).subtract(converter.convert(o2, BigInteger.class));
          }
          return (converter.convert(o1, Long.class) - converter.convert(o2, Long.class));
      }
  
      public static final Number mul(TypeConverter converter, Object o1, Object o2) {
          if (o1 == null && o2 == null) {
              return LONG_ZERO;
          }
          if (o1 instanceof BigDecimal || o2 instanceof BigDecimal) {
              return converter.convert(o1, BigDecimal.class).multiply(converter.convert(o2, BigDecimal.class));
          }
          if (isFloatOrDoubleOrDotEe(o1) || isFloatOrDoubleOrDotEe(o2)) {
              if (o1 instanceof BigInteger || o2 instanceof BigInteger) {
                  return converter.convert(o1, BigDecimal.class).multiply(converter.convert(o2, BigDecimal.class));
              }
              return (converter.convert(o1, Double.class) * converter.convert(o2, Double.class));
          }
          if (o1 instanceof BigInteger || o2 instanceof BigInteger) {
              return converter.convert(o1, BigInteger.class).multiply(converter.convert(o2, BigInteger.class));
          }
          return (converter.convert(o1, Long.class) * converter.convert(o2, Long.class));
      }
  
      public static final Number div(TypeConverter converter, Object o1, Object o2) {
          if (o1 == null && o2 == null) {
              return LONG_ZERO;
          }
          if (isBigDecimalOrBigInteger(o1) || isBigDecimalOrBigInteger(o2)) {
              return converter
                  .convert(o1, BigDecimal.class)
                  .divide(converter.convert(o2, BigDecimal.class), BigDecimal.ROUND_HALF_UP);
          }
          return (converter.convert(o1, Double.class) / converter.convert(o2, Double.class));
      }
  
      public static final Number mod(TypeConverter converter, Object o1, Object o2) {
          if (o1 == null && o2 == null) {
              return LONG_ZERO;
          }
          if (isBigDecimalOrFloatOrDoubleOrDotEe(o1) || isBigDecimalOrFloatOrDoubleOrDotEe(o2)) {
              return (converter.convert(o1, Double.class) % converter.convert(o2, Double.class));
          }
          if (o1 instanceof BigInteger || o2 instanceof BigInteger) {
              return converter.convert(o1, BigInteger.class).remainder(converter.convert(o2, BigInteger.class));
          }
          return (converter.convert(o1, Long.class) % converter.convert(o2, Long.class));
      }
  
      public static final Number neg(TypeConverter converter, Object value) {
          if (value == null) {
              return LONG_ZERO;
          }
          if (value instanceof BigDecimal) {
              return ((BigDecimal) value).negate();
          }
          if (value instanceof BigInteger) {
              return ((BigInteger) value).negate();
          }
          if (value instanceof Double) {
              return Double.valueOf(-((Double) value).doubleValue());
          }
          if (value instanceof Float) {
              return Float.valueOf(-((Float) value).floatValue());
          }
          if (value instanceof String) {
              if (isDotEe((String) value)) {
                  return Double.valueOf(-converter.convert(value, Double.class).doubleValue());
              }
              return Long.valueOf(-converter.convert(value, Long.class).longValue());
          }
          if (value instanceof Long) {
              return Long.valueOf(-((Long) value).longValue());
          }
          if (value instanceof Integer) {
              return Integer.valueOf(-((Integer) value).intValue());
          }
          if (value instanceof Short) {
              return Short.valueOf((short) -((Short) value).shortValue());
          }
          if (value instanceof Byte) {
              return Byte.valueOf((byte) -((Byte) value).byteValue());
          }
          throw new ELException(LocalMessages.get("error.negate", value.getClass()));
      }
  }
  