content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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.apache.arrow.driver.jdbc.utils;
  
  import java.util.List;
  import java.util.Map;
  import java.util.stream.Collectors;
  import java.util.stream.Stream;
  import org.apache.arrow.driver.jdbc.converter.impl.BinaryAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.BinaryViewAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.BoolAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.DateAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.DecimalAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.DurationAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.FixedSizeBinaryAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.FixedSizeListAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.FloatingPointAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.IntAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.IntervalAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.LargeBinaryAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.LargeListAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.LargeUtf8AvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.ListAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.MapAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.NullAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.StructAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.TimeAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.TimestampAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.UnionAvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.Utf8AvaticaParameterConverter;
  import org.apache.arrow.driver.jdbc.converter.impl.Utf8ViewAvaticaParameterConverter;
  import org.apache.arrow.flight.sql.FlightSqlColumnMetadata;
  import org.apache.arrow.vector.types.pojo.ArrowType;
  import org.apache.arrow.vector.types.pojo.Field;
  import org.apache.calcite.avatica.AvaticaParameter;
  import org.apache.calcite.avatica.ColumnMetaData;
  import org.apache.calcite.avatica.proto.Common;
  
  /** Convert objects between Arrow and Avatica. */
  public final class ConvertUtils {
  
    private ConvertUtils() {}
  
    /**
     * Convert Fields To Column MetaData List functions.
     *
     * @param fields list of {@link Field}.
     * @return list of {@link ColumnMetaData}.
     */
    public static List<ColumnMetaData> convertArrowFieldsToColumnMetaDataList(
        final List<Field> fields) {
      return Stream.iterate(0, Math::incrementExact)
          .limit(fields.size())
          .map(
              index -> {
                final Field field = fields.get(index);
                final ArrowType fieldType = field.getType();
  
                final Common.ColumnMetaData.Builder builder =
                    Common.ColumnMetaData.newBuilder()
                        .setOrdinal(index)
                        .setColumnName(field.getName())
                        .setLabel(field.getName());
  
                setOnColumnMetaDataBuilder(builder, field.getMetadata());
  
                builder.setType(
                    Common.AvaticaType.newBuilder()
                        .setId(SqlTypes.getSqlTypeIdFromArrowType(fieldType))
                        .setName(SqlTypes.getSqlTypeNameFromArrowType(fieldType))
                        .build());
  
                return ColumnMetaData.fromProto(builder.build());
              })
          .collect(Collectors.toList());
    }
  
    /**
     * Set on Column MetaData Builder.
     *
     * @param builder {@link Common.ColumnMetaData.Builder}
     * @param metadataMap {@link Map}
     */
    public static void setOnColumnMetaDataBuilder(
        final Common.ColumnMetaData.Builder builder, final Map<String, String> metadataMap) {
      final FlightSqlColumnMetadata columnMetadata = new FlightSqlColumnMetadata(metadataMap);
      final String catalogName = columnMetadata.getCatalogName();
      if (catalogName != null) {
        builder.setCatalogName(catalogName);
      }
      final String schemaName = columnMetadata.getSchemaName();
      if (schemaName != null) {
        builder.setSchemaName(schemaName);
      }
      final String tableName = columnMetadata.getTableName();
      if (tableName != null) {
        builder.setTableName(tableName);
      }
  
      final Integer precision = columnMetadata.getPrecision();
      if (precision != null) {
        builder.setPrecision(precision);
      }
      final Integer scale = columnMetadata.getScale();
      if (scale != null) {
        builder.setScale(scale);
      }
  
      final Boolean isAutoIncrement = columnMetadata.isAutoIncrement();
      if (isAutoIncrement != null) {
        builder.setAutoIncrement(isAutoIncrement);
      }
      final Boolean caseSensitive = columnMetadata.isCaseSensitive();
      if (caseSensitive != null) {
        builder.setCaseSensitive(caseSensitive);
      }
      final Boolean readOnly = columnMetadata.isReadOnly();
      if (readOnly != null) {
        builder.setReadOnly(readOnly);
      }
      final Boolean searchable = columnMetadata.isSearchable();
      if (searchable != null) {
        builder.setSearchable(searchable);
      }
      final String remarks = columnMetadata.getRemarks();
      if (remarks != null) {
        builder.setLabel(remarks);
      }
    }
  
    /**
     * Convert Fields To Avatica Parameters.
     *
     * @param fields list of {@link Field}.
     * @return list of {@link AvaticaParameter}.
     */
    public static List<AvaticaParameter> convertArrowFieldsToAvaticaParameters(
        final List<Field> fields) {
      return fields.stream()
          .map(field -> field.getType().accept(new ConverterVisitor(field)))
          .collect(Collectors.toList());
    }
  
    private static class ConverterVisitor implements ArrowType.ArrowTypeVisitor<AvaticaParameter> {
      private final Field field;
  
      private ConverterVisitor(Field field) {
        this.field = field;
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Null type) {
        return new NullAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Struct type) {
        return new StructAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.List type) {
        return new ListAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.LargeList type) {
        return new LargeListAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.FixedSizeList type) {
        return new FixedSizeListAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Union type) {
        return new UnionAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Map type) {
        return new MapAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Int type) {
        return new IntAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.FloatingPoint type) {
        return new FloatingPointAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Utf8 type) {
        return new Utf8AvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Utf8View type) {
        return new Utf8ViewAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.LargeUtf8 type) {
        return new LargeUtf8AvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Binary type) {
        return new BinaryAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.BinaryView type) {
        return new BinaryViewAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.LargeBinary type) {
        return new LargeBinaryAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.FixedSizeBinary type) {
        return new FixedSizeBinaryAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Bool type) {
        return new BoolAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Decimal type) {
        return new DecimalAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Date type) {
        return new DateAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Time type) {
        return new TimeAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Timestamp type) {
        return new TimestampAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Interval type) {
        return new IntervalAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.Duration type) {
        return new DurationAvaticaParameterConverter(type).createParameter(field);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.ListView type) {
        throw new UnsupportedOperationException(
            "AvaticaParameter not yet supported for type " + type);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.LargeListView type) {
        throw new UnsupportedOperationException(
            "AvaticaParameter not yet supported for type " + type);
      }
  
      @Override
      public AvaticaParameter visit(ArrowType.RunEndEncoded type) {
        throw new UnsupportedOperationException(
            "No Avatica parameter binder implemented for type " + type);
      }
    }
  }
  