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.flight.sql;
  
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * Metadata for a column in a Flight SQL query.
   *
   * <p>This can be used with FlightSqlClient to access column's metadata contained in schemas
   * returned by GetTables and query execution as follows:
   *
   * <pre>
   *   FlightSqlColumnMetadata metadata = new FlightSqlColumnMetadata(field.getMetadata());
   *   Integer precision = metadata.getPrecision();
   * </pre>
   *
   * FlightSqlProducer can use this to set metadata on a column in a schema as follows:
   *
   * <pre>
   *   FlightSqlColumnMetadata metadata = new FlightSqlColumnMetadata.Builder()
   *         .precision(10)
   *         .scale(5)
   *         .build();
   *   Field field = new Field("column", new FieldType(..., metadata.getMetadataMap()), null);
   * </pre>
   */
  public class FlightSqlColumnMetadata {
  
    private static final String CATALOG_NAME = "ARROW:FLIGHT:SQL:CATALOG_NAME";
    private static final String SCHEMA_NAME = "ARROW:FLIGHT:SQL:SCHEMA_NAME";
    private static final String TABLE_NAME = "ARROW:FLIGHT:SQL:TABLE_NAME";
    private static final String TYPE_NAME = "ARROW:FLIGHT:SQL:TYPE_NAME";
    private static final String PRECISION = "ARROW:FLIGHT:SQL:PRECISION";
    private static final String SCALE = "ARROW:FLIGHT:SQL:SCALE";
    private static final String IS_AUTO_INCREMENT = "ARROW:FLIGHT:SQL:IS_AUTO_INCREMENT";
    private static final String IS_CASE_SENSITIVE = "ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE";
    private static final String IS_READ_ONLY = "ARROW:FLIGHT:SQL:IS_READ_ONLY";
    private static final String IS_SEARCHABLE = "ARROW:FLIGHT:SQL:IS_SEARCHABLE";
    private static final String REMARKS = "ARROW:FLIGHT:SQL:REMARKS";
  
    private static final String BOOLEAN_TRUE_STR = "1";
    private static final String BOOLEAN_FALSE_STR = "0";
  
    private final Map<String, String> metadataMap;
  
    /** Creates a new instance of FlightSqlColumnMetadata. */
    public FlightSqlColumnMetadata(Map<String, String> metadataMap) {
      this.metadataMap = new HashMap<>(metadataMap);
    }
  
    /**
     * Returns the metadata map.
     *
     * @return The metadata map.
     */
    public Map<String, String> getMetadataMap() {
      return Collections.unmodifiableMap(metadataMap);
    }
  
    /**
     * Returns the catalog name.
     *
     * @return The catalog name.
     */
    public String getCatalogName() {
      return metadataMap.get(CATALOG_NAME);
    }
  
    /**
     * Returns the schema name.
     *
     * @return The schema name.
     */
    public String getSchemaName() {
      return metadataMap.get(SCHEMA_NAME);
    }
  
    /**
     * Returns the table name.
     *
     * @return The table name.
     */
    public String getTableName() {
      return metadataMap.get(TABLE_NAME);
    }
  
    /**
     * Returns the type name.
     *
     * @return The type name.
     */
    public String getTypeName() {
      return metadataMap.get(TYPE_NAME);
    }
  
    /**
     * Returns the precision / column size.
     *
     * @return The precision / column size.
     */
    public Integer getPrecision() {
      String value = metadataMap.get(PRECISION);
      if (value == null) {
        return null;
      }
  
      return Integer.valueOf(value);
    }
  
    /**
     * Returns the scale / decimal digits.
     *
     * @return The scale / decimal digits.
     */
    public Integer getScale() {
      String value = metadataMap.get(SCALE);
      if (value == null) {
        return null;
      }
  
      return Integer.valueOf(value);
    }
  
    /**
     * Returns if the column is auto incremented.
     *
     * @return True if the column is auto incremented, false otherwise.
     */
    public Boolean isAutoIncrement() {
      String value = metadataMap.get(IS_AUTO_INCREMENT);
      if (value == null) {
        return null;
      }
  
      return stringToBoolean(value);
    }
  
    /**
     * Returns if the column is case-sensitive.
     *
     * @return True if the column is case-sensitive, false otherwise.
     */
    public Boolean isCaseSensitive() {
      String value = metadataMap.get(IS_CASE_SENSITIVE);
      if (value == null) {
        return null;
      }
  
      return stringToBoolean(value);
    }
  
    /**
     * Returns if the column is read only.
     *
     * @return True if the column is read only, false otherwise.
     */
    public Boolean isReadOnly() {
      String value = metadataMap.get(IS_READ_ONLY);
      if (value == null) {
        return null;
      }
  
      return stringToBoolean(value);
    }
  
    /**
     * Returns if the column is searchable.
     *
     * @return True if the column is searchable, false otherwise.
     */
    public Boolean isSearchable() {
      String value = metadataMap.get(IS_SEARCHABLE);
      if (value == null) {
        return null;
      }
  
      return stringToBoolean(value);
    }
  
    /**
     * Returns the comment describing the column.
     *
     * @return The comment describing the column.
     */
    public String getRemarks() {
      return metadataMap.get(REMARKS);
    }
  
    /** Builder of FlightSqlColumnMetadata, used on FlightSqlProducer implementations. */
    public static class Builder {
      private final Map<String, String> metadataMap;
  
      /** Creates a new instance of FlightSqlColumnMetadata.Builder. */
      public Builder() {
        this.metadataMap = new HashMap<>();
      }
  
      /**
       * Sets the catalog name.
       *
       * @param catalogName the catalog name.
       * @return This builder.
       */
      public Builder catalogName(String catalogName) {
        metadataMap.put(CATALOG_NAME, catalogName);
        return this;
      }
  
      /**
       * Sets the schema name.
       *
       * @param schemaName The schema name.
       * @return This builder.
       */
      public Builder schemaName(String schemaName) {
        metadataMap.put(SCHEMA_NAME, schemaName);
        return this;
      }
  
      /**
       * Sets the table name.
       *
       * @param tableName The table name.
       * @return This builder.
       */
      public Builder tableName(String tableName) {
        metadataMap.put(TABLE_NAME, tableName);
        return this;
      }
  
      /**
       * Sets the type name.
       *
       * @param typeName The type name.
       * @return This builder.
       */
      public Builder typeName(String typeName) {
        metadataMap.put(TYPE_NAME, typeName);
        return this;
      }
  
      /**
       * Sets the precision / column size.
       *
       * @param precision The precision / column size.
       * @return This builder.
       */
      public Builder precision(int precision) {
        metadataMap.put(PRECISION, Integer.toString(precision));
        return this;
      }
  
      /**
       * Sets the scale / decimal digits.
       *
       * @param scale The scale / decimal digits.
       * @return This builder.
       */
      public Builder scale(int scale) {
        metadataMap.put(SCALE, Integer.toString(scale));
        return this;
      }
  
      /**
       * Sets if the column is auto incremented.
       *
       * @param isAutoIncrement True if the column is auto incremented.
       * @return This builder.
       */
      public Builder isAutoIncrement(boolean isAutoIncrement) {
        metadataMap.put(IS_AUTO_INCREMENT, booleanToString(isAutoIncrement));
        return this;
      }
  
      /**
       * Sets if the column is case-sensitive.
       *
       * @param isCaseSensitive If the column is case-sensitive.
       * @return This builder.
       */
      public Builder isCaseSensitive(boolean isCaseSensitive) {
        metadataMap.put(IS_CASE_SENSITIVE, booleanToString(isCaseSensitive));
        return this;
      }
  
      /**
       * Sets if the column is read only.
       *
       * @param isReadOnly If the column is read only.
       * @return This builder.
       */
      public Builder isReadOnly(boolean isReadOnly) {
        metadataMap.put(IS_READ_ONLY, booleanToString(isReadOnly));
        return this;
      }
  
      /**
       * Sets if the column is searchable.
       *
       * @param isSearchable If the column is searchable.
       * @return This builder.
       */
      public Builder isSearchable(boolean isSearchable) {
        metadataMap.put(IS_SEARCHABLE, booleanToString(isSearchable));
        return this;
      }
  
      /**
       * Sets the comment describing the column.
       *
       * @param remarks The comment describing the column.
       * @return This builder.
       */
      public Builder remarks(String remarks) {
        metadataMap.put(REMARKS, remarks);
        return this;
      }
  
      /**
       * Builds a new instance of FlightSqlColumnMetadata.
       *
       * @return A new instance of FlightSqlColumnMetadata.
       */
      public FlightSqlColumnMetadata build() {
        return new FlightSqlColumnMetadata(metadataMap);
      }
    }
  
    private static String booleanToString(boolean boolValue) {
      return boolValue ? BOOLEAN_TRUE_STR : BOOLEAN_FALSE_STR;
    }
  
    private static boolean stringToBoolean(String value) {
      return value.equals(BOOLEAN_TRUE_STR);
    }
  }
  