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.calcite.avatica;
  
  import org.apache.calcite.avatica.proto.Common;
  
  import com.fasterxml.jackson.annotation.JsonCreator;
  import com.fasterxml.jackson.annotation.JsonProperty;
  import com.google.protobuf.Descriptors.FieldDescriptor;
  
  import java.sql.Connection;
  import java.sql.SQLException;
  import java.util.Objects;
  
  /** Concrete implementation of {@link Meta.ConnectionProperties}. Provides additional state
   * tracking to enable {@code RemoteMeta} to lazily push changes up to a query server.
   *
   * <p>{@code Meta} instances should probably hold authority on the {@code isDirty}
   * flag because {@code AvaticaConnection} instances have no way of knowing if they're local or
   * remote.
   */
  public class ConnectionPropertiesImpl implements Meta.ConnectionProperties {
    private static final FieldDescriptor CATALOG_DESCRIPTOR = Common.ConnectionProperties
        .getDescriptor().findFieldByNumber(Common.ConnectionProperties.CATALOG_FIELD_NUMBER);
    private static final FieldDescriptor SCHEMA_DESCRIPTOR = Common.ConnectionProperties
        .getDescriptor().findFieldByNumber(Common.ConnectionProperties.SCHEMA_FIELD_NUMBER);
    private static final FieldDescriptor TRANSACTION_ISOLATION_DESCRIPTOR = Common
        .ConnectionProperties.getDescriptor().findFieldByNumber(
            Common.ConnectionProperties.TRANSACTION_ISOLATION_FIELD_NUMBER);
  
    private boolean isDirty = false;
    private Boolean autoCommit;
    private Boolean readOnly;
    private Integer transactionIsolation;
    private String catalog;
    private String schema;
  
    // TODO: replace with Meta.ConnectionProperties$EMPTY instance?
    public ConnectionPropertiesImpl() {}
  
    public ConnectionPropertiesImpl(Connection conn) throws SQLException {
      this(conn.getAutoCommit(), conn.isReadOnly(), conn.getTransactionIsolation(),
          conn.getCatalog(), conn.getSchema());
    }
  
    @JsonCreator
    public ConnectionPropertiesImpl(
        @JsonProperty("autoCommit") Boolean autoCommit,
        @JsonProperty("readOnly") Boolean readOnly,
        @JsonProperty("transactionIsolation") Integer transactionIsolation,
        @JsonProperty("catalog") String catalog,
        @JsonProperty("schema") String schema) {
      this.autoCommit = autoCommit;
      this.readOnly = readOnly;
      this.transactionIsolation = transactionIsolation;
      this.catalog = catalog;
      this.schema = schema;
    }
  
    public ConnectionPropertiesImpl setDirty(boolean val) {
      this.isDirty = val;
      return this;
    }
  
    public boolean isDirty() {
      return this.isDirty;
    }
  
    @Override public boolean isEmpty() {
      return autoCommit == null && readOnly == null && transactionIsolation == null
          && catalog == null && schema == null;
    }
  
    /** Overwrites fields in {@code this} with any non-null fields in {@code that}. Sets
     * {@code isDirty} if any fields are changed.
     *
     * @return {@code this}
     */
    @Override public ConnectionPropertiesImpl merge(Meta.ConnectionProperties that) {
      if (this == that) {
        return this;
      }
      if (that.isAutoCommit() != null && !Objects.equals(this.autoCommit, that.isAutoCommit())) {
        this.autoCommit = that.isAutoCommit();
        this.isDirty = true;
      }
      if (that.isReadOnly() != null && !Objects.equals(this.readOnly, that.isReadOnly())) {
        this.readOnly = that.isReadOnly();
        this.isDirty = true;
      }
      if (that.getTransactionIsolation() != null
          && !Objects.equals(this.transactionIsolation, that.getTransactionIsolation())) {
        this.transactionIsolation = that.getTransactionIsolation();
        this.isDirty = true;
      }
      if (that.getCatalog() != null && !that.getCatalog().equalsIgnoreCase(this.catalog)) {
        this.catalog = that.getCatalog();
        this.isDirty = true;
      }
      if (that.getSchema() != null && !that.getSchema().equalsIgnoreCase(this.schema)) {
        this.schema = that.getSchema();
        this.isDirty = true;
      }
      return this;
    }
  
    /** Sets {@code autoCommit} status and flag as dirty.
     *
     * @return {@code this}
     */
    @Override public Meta.ConnectionProperties setAutoCommit(boolean val) {
      this.autoCommit = val;
      this.isDirty = true;
      return this;
    }
  
    @Override public Boolean isAutoCommit() {
      return this.autoCommit;
    }
  
    /** Sets {@code readOnly} status and flag as dirty.
     *
     * @return {@code this}
     */
    @Override public Meta.ConnectionProperties setReadOnly(boolean val) {
      this.readOnly = val;
      this.isDirty = true;
      return this;
    }
  
    @Override public Boolean isReadOnly() {
      return this.readOnly;
    }
  
    /** Sets {@code transactionIsolation} status and flag as dirty.
     *
     * @return {@code this}
     */
    @Override public Meta.ConnectionProperties setTransactionIsolation(int val) {
      this.transactionIsolation = val;
      this.isDirty = true;
      return this;
    }
  
    public Integer getTransactionIsolation() {
      return this.transactionIsolation;
    }
  
    /** Sets {@code catalog} and flag as dirty.
     *
     * @return {@code this}
     */
    @Override public Meta.ConnectionProperties setCatalog(String val) {
      this.catalog = val;
      this.isDirty = true;
      return this;
    }
  
    @Override public String getCatalog() {
      return this.catalog;
    }
  
    /** Sets {@code schema} and flag as dirty.
     *
     * @return {@code this}
     */
    @Override public Meta.ConnectionProperties setSchema(String val) {
      this.schema = val;
      this.isDirty = true;
      return this;
    }
  
    public String getSchema() {
      return this.schema;
    }
  
    @Override public int hashCode() {
      return Objects.hash(autoCommit, catalog, isDirty, readOnly, schema,
          transactionIsolation);
    }
  
    @Override public boolean equals(Object o) {
      return o == this
          || o instanceof ConnectionPropertiesImpl
          && Objects.equals(autoCommit, ((ConnectionPropertiesImpl) o).autoCommit)
          && Objects.equals(catalog, ((ConnectionPropertiesImpl) o).catalog)
          && isDirty == ((ConnectionPropertiesImpl) o).isDirty
          && Objects.equals(readOnly, ((ConnectionPropertiesImpl) o).readOnly)
          && Objects.equals(schema, ((ConnectionPropertiesImpl) o).schema)
          && Objects.equals(transactionIsolation,
              ((ConnectionPropertiesImpl) o).transactionIsolation);
    }
  
    public Common.ConnectionProperties toProto() {
      Common.ConnectionProperties.Builder builder = Common.ConnectionProperties.newBuilder();
  
      if (null != autoCommit) {
        builder.setHasAutoCommit(true);
        builder.setAutoCommit(autoCommit.booleanValue());
      } else {
        // Be explicit to avoid default value confusion
        builder.setHasAutoCommit(false);
      }
  
      if (null != catalog) {
        builder.setCatalog(catalog);
      }
  
      builder.setIsDirty(isDirty);
  
      if (null != readOnly) {
        builder.setHasReadOnly(true);
        builder.setReadOnly(readOnly.booleanValue());
      } else {
        // Be explicit to avoid default value confusion
        builder.setHasReadOnly(false);
      }
  
      if (null != schema) {
        builder.setSchema(schema);
      }
  
      if (null != transactionIsolation) {
        builder.setTransactionIsolation(transactionIsolation.intValue());
      }
  
      return builder.build();
    }
  
    public static ConnectionPropertiesImpl fromProto(Common.ConnectionProperties proto) {
      String catalog = null;
      if (proto.hasField(CATALOG_DESCRIPTOR)) {
        catalog = proto.getCatalog();
      }
  
      String schema = null;
      if (proto.hasField(SCHEMA_DESCRIPTOR)) {
        schema = proto.getSchema();
      }
  
      Boolean autoCommit = null;
      if (proto.getHasAutoCommit()) {
        autoCommit = Boolean.valueOf(proto.getAutoCommit());
      }
  
      Boolean readOnly = null;
      if (proto.getHasReadOnly()) {
        readOnly = Boolean.valueOf(proto.getReadOnly());
      }
  
      Integer transactionIsolation = null;
      if (proto.hasField(TRANSACTION_ISOLATION_DESCRIPTOR)) {
        transactionIsolation = Integer.valueOf(proto.getTransactionIsolation());
      }
  
      ConnectionPropertiesImpl impl = new ConnectionPropertiesImpl(autoCommit, readOnly,
          transactionIsolation, catalog, schema);
  
      impl.setDirty(proto.getIsDirty());
  
      return impl;
    }
  }
  
  // End ConnectionPropertiesImpl.java
  