content: /*
   * Copyright The OpenTelemetry Authors
   * SPDX-License-Identifier: Apache-2.0
   */
  
  package io.opentelemetry.instrumentation.api.annotation.support;
  
  import io.opentelemetry.api.common.AttributeKey;
  import java.lang.reflect.Type;
  import java.util.AbstractList;
  import java.util.Arrays;
  import java.util.List;
  import java.util.Optional;
  import java.util.function.Function;
  
  /**
   * Helper class for creating {@link AttributeBinding} instances based on the {@link Type} of the
   * parameter for a traced method.
   */
  class AttributeBindingFactory {
    private AttributeBindingFactory() {}
  
    static AttributeBinding createBinding(String name, Type type) {
  
      // Simple scalar parameter types
      if (type == String.class) {
        AttributeKey<String> key = AttributeKey.stringKey(name);
        return (setter, arg) -> setter.put(key, (String) arg);
      }
      if (type == long.class || type == Long.class) {
        AttributeKey<Long> key = AttributeKey.longKey(name);
        return (setter, arg) -> setter.put(key, (Long) arg);
      }
      if (type == double.class || type == Double.class) {
        AttributeKey<Double> key = AttributeKey.doubleKey(name);
        return (setter, arg) -> setter.put(key, (Double) arg);
      }
      if (type == boolean.class || type == Boolean.class) {
        AttributeKey<Boolean> key = AttributeKey.booleanKey(name);
        return (setter, arg) -> setter.put(key, (Boolean) arg);
      }
      if (type == int.class || type == Integer.class) {
        AttributeKey<Long> key = AttributeKey.longKey(name);
        return (setter, arg) -> setter.put(key, ((Integer) arg).longValue());
      }
      if (type == float.class || type == Float.class) {
        AttributeKey<Double> key = AttributeKey.doubleKey(name);
        return (setter, arg) -> setter.put(key, ((Float) arg).doubleValue());
      }
  
      if (isArrayType(type)) {
        return arrayBinding(name, type);
      }
  
      return resolveListComponentType(type)
          .map(componentType -> listBinding(name, componentType))
          .orElseGet(() -> defaultBinding(name));
    }
  
    private static boolean isArrayType(Type type) {
      if (type instanceof Class) {
        return ((Class<?>) type).isArray();
      }
      return false;
    }
  
    private static Optional<Type> resolveListComponentType(Type type) {
      return ParameterizedClass.of(type)
          .findParameterizedSuperclass(List.class)
          .map(pc -> pc.getActualTypeArguments()[0]);
    }
  
    private static AttributeBinding arrayBinding(String name, Type type) {
      // Simple array attribute types without conversion
      if (type == String[].class) {
        AttributeKey<List<String>> key = AttributeKey.stringArrayKey(name);
        return (setter, arg) -> setter.put(key, Arrays.asList((String[]) arg));
      }
      if (type == Long[].class) {
        AttributeKey<List<Long>> key = AttributeKey.longArrayKey(name);
        return (setter, arg) -> setter.put(key, Arrays.asList((Long[]) arg));
      }
      if (type == Double[].class) {
        AttributeKey<List<Double>> key = AttributeKey.doubleArrayKey(name);
        return (setter, arg) -> setter.put(key, Arrays.asList((Double[]) arg));
      }
      if (type == Boolean[].class) {
        AttributeKey<List<Boolean>> key = AttributeKey.booleanArrayKey(name);
        return (setter, arg) -> setter.put(key, Arrays.asList((Boolean[]) arg));
      }
  
      if (type == long[].class) {
        return longArrayBinding(name);
      }
      if (type == int[].class) {
        return intArrayBinding(name);
      }
      if (type == float[].class) {
        return floatArrayBinding(name);
      }
      if (type == double[].class) {
        return doubleArrayBinding(name);
      }
      if (type == boolean[].class) {
        return booleanArrayBinding(name);
      }
      if (type == Integer[].class) {
        return boxedIntegerArrayBinding(name);
      }
      if (type == Float[].class) {
        return boxedFloatArrayBinding(name);
      }
  
      return defaultArrayBinding(name);
    }
  
    @SuppressWarnings("unchecked")
    private static AttributeBinding listBinding(String name, Type componentType) {
      if (componentType == String.class) {
        AttributeKey<List<String>> key = AttributeKey.stringArrayKey(name);
        return (setter, arg) -> setter.put(key, (List<String>) arg);
      }
      if (componentType == Long.class) {
        AttributeKey<List<Long>> key = AttributeKey.longArrayKey(name);
        return (setter, arg) -> setter.put(key, (List<Long>) arg);
      }
      if (componentType == Double.class) {
        AttributeKey<List<Double>> key = AttributeKey.doubleArrayKey(name);
        return (setter, arg) -> setter.put(key, (List<Double>) arg);
      }
      if (componentType == Boolean.class) {
        AttributeKey<List<Boolean>> key = AttributeKey.booleanArrayKey(name);
        return (setter, arg) -> setter.put(key, (List<Boolean>) arg);
      }
      if (componentType == Integer.class) {
        AttributeKey<List<Long>> key = AttributeKey.longArrayKey(name);
        return mappedListBinding(key, Integer::longValue);
      }
      if (componentType == Float.class) {
        AttributeKey<List<Double>> key = AttributeKey.doubleArrayKey(name);
        return mappedListBinding(key, Float::doubleValue);
      }
  
      return defaultListBinding(name);
    }
  
    private static AttributeBinding intArrayBinding(String name) {
      AttributeKey<List<Long>> key = AttributeKey.longArrayKey(name);
      return (setter, arg) -> {
        int[] array = (int[]) arg;
        List<Long> wrapper =
            new AbstractList<Long>() {
              @Override
              public Long get(int index) {
                return (long) array[index];
              }
  
              @Override
              public int size() {
                return array.length;
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static AttributeBinding boxedIntegerArrayBinding(String name) {
      AttributeKey<List<Long>> key = AttributeKey.longArrayKey(name);
      return (setter, arg) -> {
        Integer[] array = (Integer[]) arg;
        List<Long> wrapper =
            new AbstractList<Long>() {
              @Override
              public Long get(int index) {
                Integer value = array[index];
                return value != null ? value.longValue() : null;
              }
  
              @Override
              public int size() {
                return array.length;
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static AttributeBinding longArrayBinding(String name) {
      AttributeKey<List<Long>> key = AttributeKey.longArrayKey(name);
      return (setter, arg) -> {
        long[] array = (long[]) arg;
        List<Long> wrapper =
            new AbstractList<Long>() {
              @Override
              public Long get(int index) {
                return array[index];
              }
  
              @Override
              public int size() {
                return array.length;
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static AttributeBinding floatArrayBinding(String name) {
      AttributeKey<List<Double>> key = AttributeKey.doubleArrayKey(name);
      return (setter, arg) -> {
        float[] array = (float[]) arg;
        List<Double> wrapper =
            new AbstractList<Double>() {
              @Override
              public Double get(int index) {
                return (double) array[index];
              }
  
              @Override
              public int size() {
                return array.length;
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static AttributeBinding boxedFloatArrayBinding(String name) {
      AttributeKey<List<Double>> key = AttributeKey.doubleArrayKey(name);
      return (setter, arg) -> {
        Float[] array = (Float[]) arg;
        List<Double> wrapper =
            new AbstractList<Double>() {
              @Override
              public Double get(int index) {
                Float value = array[index];
                return value != null ? value.doubleValue() : null;
              }
  
              @Override
              public int size() {
                return array.length;
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static AttributeBinding doubleArrayBinding(String name) {
      AttributeKey<List<Double>> key = AttributeKey.doubleArrayKey(name);
      return (setter, arg) -> {
        double[] array = (double[]) arg;
        List<Double> wrapper =
            new AbstractList<Double>() {
              @Override
              public Double get(int index) {
                return array[index];
              }
  
              @Override
              public int size() {
                return array.length;
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static AttributeBinding booleanArrayBinding(String name) {
      AttributeKey<List<Boolean>> key = AttributeKey.booleanArrayKey(name);
      return (setter, arg) -> {
        boolean[] array = (boolean[]) arg;
        List<Boolean> wrapper =
            new AbstractList<Boolean>() {
              @Override
              public Boolean get(int index) {
                return array[index];
              }
  
              @Override
              public int size() {
                return array.length;
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static AttributeBinding defaultArrayBinding(String name) {
      AttributeKey<List<String>> key = AttributeKey.stringArrayKey(name);
      return (setter, arg) -> {
        Object[] array = (Object[]) arg;
        List<String> wrapper =
            new AbstractList<String>() {
              @Override
              public String get(int index) {
                Object value = array[index];
                return value != null ? value.toString() : null;
              }
  
              @Override
              public int size() {
                return array.length;
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static <T, U> AttributeBinding mappedListBinding(
        AttributeKey<List<U>> key, Function<T, U> mapping) {
      return (setter, arg) -> {
        @SuppressWarnings("unchecked")
        List<T> list = (List<T>) arg;
        List<U> wrapper =
            new AbstractList<U>() {
              @Override
              public U get(int index) {
                T value = list.get(index);
                return value != null ? mapping.apply(value) : null;
              }
  
              @Override
              public int size() {
                return list.size();
              }
            };
        setter.put(key, wrapper);
      };
    }
  
    private static AttributeBinding defaultListBinding(String name) {
      AttributeKey<List<String>> key = AttributeKey.stringArrayKey(name);
      return mappedListBinding(key, Object::toString);
    }
  
    private static AttributeBinding defaultBinding(String name) {
      AttributeKey<String> key = AttributeKey.stringKey(name);
      return (setter, arg) -> setter.put(key, arg.toString());
    }
  }
  