content: package com.annimon.ownlang.modules.jdbc;
  
  import com.annimon.ownlang.exceptions.ArgumentsMismatchException;
  import com.annimon.ownlang.exceptions.OwnLangRuntimeException;
  import com.annimon.ownlang.lib.*;
  import java.sql.Array;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.util.function.BiFunction;
  import java.util.function.Function;
  import static com.annimon.ownlang.lib.ValueUtils.getNumber;
  
  class JdbcConverters {
  
      static <T> T columnData(Value value, AutogeneratedKeys<T> autogeneratedKeysFunction,
                              ColumnIndices<T> columnIndicesFunction, ColumnNames<T> columnNamesFunction) {
          try {
              if (value.type() != Types.ARRAY) {
                  return autogeneratedKeysFunction.apply(value.asInt());
              }
  
              final ArrayValue array = (ArrayValue) value;
              final int size = array.size();
              final boolean isIntArray = (size > 0) && (array.get(0).type() == Types.NUMBER);
              int index = 0;
  
              if (isIntArray) {
                  final int[] columnIndices = new int[size];
                  for (Value v : array) {
                      columnIndices[index++] = v.asInt();
                  }
                  return columnIndicesFunction.apply(columnIndices);
              }
  
              final String[] columnNames = new String[size];
              for (Value v : array) {
                  columnNames[index++] = v.asString();
              }
              return columnNamesFunction.apply(columnNames);
  
          } catch (SQLException sqlex) {
              throw new OwnLangRuntimeException(sqlex);
          }
      }
  
      interface AutogeneratedKeys<T> {
          T apply(int autogeneratedKeys) throws SQLException;
      }
  
      interface ColumnIndices<T> {
          T apply(int[] columnIndices) throws SQLException;
      }
  
      interface ColumnNames<T> {
          T apply(String[] columnNames) throws SQLException;
      }
  
      static FunctionValue voidFunction(VoidResult result) {
          return new FunctionValue(args -> {
              try {
                  result.execute();
                  return NumberValue.ONE;
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static FunctionValue voidIntFunction(VoidResultInt result) {
          return new FunctionValue(args -> {
              Arguments.check(1, args.length);
              try {
                  result.execute(args[0].asInt());
                  return NumberValue.ONE;
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static FunctionValue booleanFunction(BooleanResult result) {
          return new FunctionValue(args -> {
              try {
                  return NumberValue.fromBoolean(result.get());
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static Value intFunction(IntResult numberResult) {
          return new FunctionValue(args -> {
              try {
                  return NumberValue.of(numberResult.get());
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static Value stringFunction(StringResult stringResult) {
          return new FunctionValue(args -> {
              try {
                  return new StringValue(stringResult.get());
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static <T> Value objectFunction(ObjectResult<T> objectResult, Function<T, Value> converter) {
          return new FunctionValue(args -> {
              try {
                  return converter.apply(objectResult.get());
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
  
      static Value getBooleanResult(BooleanColumnResultInt numberResult,
                                    BooleanColumnResultString stringResult) {
          return new FunctionValue(args -> {
              Arguments.check(1, args.length);
              try {
                  if (args[0].type() == Types.NUMBER) {
                      return NumberValue.fromBoolean(numberResult.get(args[0].asInt()));
                  }
                  return NumberValue.fromBoolean(stringResult.get(args[0].asString()));
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static Value getNumberResult(NumberColumnResultInt numberResult,
                                   NumberColumnResultIntString stringResult) {
          return new FunctionValue(args -> {
              Arguments.check(1, args.length);
              try {
                  if (args[0].type() == Types.NUMBER) {
                      return NumberValue.of(numberResult.get(args[0].asInt()));
                  }
                  return NumberValue.of(stringResult.get(args[0].asString()));
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static Value getStringResult(StringColumnResultInt numberResult,
                                   StringColumnResultIntString stringResult) {
          return new FunctionValue(args -> {
              Arguments.check(1, args.length);
              try {
                  if (args[0].type() == Types.NUMBER) {
                      return new StringValue(numberResult.get(args[0].asInt()));
                  }
                  return new StringValue(stringResult.get(args[0].asString()));
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
  
  
      static <T> Value updateData(VoidResultObject<T> result, Function<Value[], T> converter) {
          return new FunctionValue(args -> {
              Arguments.checkAtLeast(2, args.length);
              try {
                  result.execute(converter.apply(args));
                  return NumberValue.ONE;
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static <T> Value updateData(VoidColumnResultIntObject<T> numberResult, Function<Value[], T> converter) {
          return new FunctionValue(args -> {
              Arguments.checkAtLeast(2, args.length);
              try {
                  numberResult.execute(args[0].asInt(), converter.apply(args));
                  return NumberValue.ONE;
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static <T> Value updateData(VoidColumnResultIntObject<T> numberResult, VoidColumnResultStringObject<T> stringResult,
                                  Function<Value[], T> converter) {
          return new FunctionValue(args -> {
              Arguments.checkAtLeast(2, args.length);
              try {
                  if (args[0].type() == Types.NUMBER) {
                      numberResult.execute(args[0].asInt(), converter.apply(args));
                  } else {
                      stringResult.execute(args[0].asString(), converter.apply(args));
                  }
                  return NumberValue.ONE;
              } catch (SQLException sqlex) {
                  throw new OwnLangRuntimeException(sqlex);
              }
          });
      }
  
      static Value arrayToResultSetValue(Array array, Value[] args) {
          try {
              final ResultSet result = switch (args.length) {
                  // column
                  case 1 -> array.getResultSet();
                  case 3 -> {
                      // column, index, count
                      long index = (args[1].type() == Types.NUMBER) ? ((NumberValue) args[1]).asLong() : args[1].asInt();
                      yield array.getResultSet(index, args[2].asInt());
                  }
                  default -> throw new ArgumentsMismatchException("Wrong number of arguments");
              };
              return new ResultSetValue(result);
          } catch (SQLException sqlex) {
              throw new OwnLangRuntimeException(sqlex);
          }
      }
  
      static byte[] valueToByteArray(Value value) {
          if (value.type() != Types.ARRAY) {
              return new byte[0];
          }
          final ArrayValue array = (ArrayValue) value;
          final int size = array.size();
          final byte[] result = new byte[size];
          for (int i = 0; i < size; i++) {
              result[i] = getNumber(array.get(i)).byteValue();
          }
          return result;
      }
  
      static Value intArrayToValue(int[] array) {
          final ArrayValue result = new ArrayValue(array.length);
          for (int i = 0; i < array.length; i++) {
              result.set(i, NumberValue.of(array[i]));
          }
          return result;
      }
  
      static Value longArrayToValue(long[] array) {
          final ArrayValue result = new ArrayValue(array.length);
          for (int i = 0; i < array.length; i++) {
              result.set(i, NumberValue.of(array[i]));
          }
          return result;
      }
  
      interface VoidResult {
          void execute() throws SQLException;
      }
  
      interface VoidResultInt {
          void execute(int index) throws SQLException;
      }
  
      interface VoidResultObject<T> {
          void execute(T data) throws SQLException;
      }
  
      interface BooleanResult {
          boolean get() throws SQLException;
      }
  
      interface IntResult {
          int get() throws SQLException;
      }
  
      interface StringResult {
          String get() throws SQLException;
      }
  
      interface ObjectResult<T> {
          T get() throws SQLException;
      }
  
      interface BooleanColumnResultInt {
          boolean get(int columnIndex) throws SQLException;
      }
  
      interface BooleanColumnResultString {
          boolean get(String columnName) throws SQLException;
      }
  
      interface NumberColumnResultInt {
          Number get(int columnIndex) throws SQLException;
      }
  
      interface NumberColumnResultIntString {
          Number get(String columnName) throws SQLException;
      }
  
      interface StringColumnResultInt {
          String get(int columnIndex) throws SQLException;
      }
  
      interface StringColumnResultIntString {
          String get(String columnName) throws SQLException;
      }
  
      interface VoidColumnResultIntObject<T> {
          void execute(int columnIndex, T value) throws SQLException;
      }
  
      interface VoidColumnResultStringObject<T> {
          void execute(String columnName, T value) throws SQLException;
      }
      
  }
  