# DbColumn.java

    /*
     * Copyright (C) 2000 - 2024 Silverpeas
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU Affero General Public License as
     * published by the Free Software Foundation, either version 3 of the
     * License, or (at your option) any later version.
     *
     * As a special exception to the terms and conditions of version 3.0 of
     * the GPL, you may redistribute this Program in connection with Free/Libre
     * Open Source Software ("FLOSS") applications as described in Silverpeas's
     * FLOSS exception.  You should have received a copy of the text describing
     * the FLOSS exception, and it is also available here:
     * "https://www.silverpeas.org/legal/floss_exception.html"
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU Affero General Public License for more details.
     *
     * You should have received a copy of the GNU Affero General Public License
     * along with this program.  If not, see <https://www.gnu.org/licenses/>.
     */

    package org.silverpeas.components.mydb.model;

    import org.silverpeas.core.util.logging.SilverLogger;

    import java.sql.JDBCType;
    import java.sql.Types;
    import java.util.Objects;
    import java.util.stream.Stream;

    import static org.silverpeas.core.util.StringUtil.isDefined;

    /**
     * A column in a database table. The column doesn't contain any values, it defines the column in
     * a {@link DbTable} instance by a name and a type.
     * @author mmoquillon
     */
    public class DbColumn {

      private JdbcRequester.ColumnDescriptor descriptor;

      /**
       * Constructs a new {@link DbColumn} instance of the specified SQL type and with the specified
       * name.
       * @param descriptor a descriptor of the column.
       */
      public DbColumn(final JdbcRequester.ColumnDescriptor descriptor) {
        this.descriptor = descriptor;
      }

      /**
       * Gets the SQL type of the values this column can contain. The values are define by the
       * {@link java.sql.Types} class.
       * @return an integer identifying a generic SQL type.
       */
      public int getType() {
        return descriptor.getType();
      }

      /**
       * Gets the name of this column.
       * @return the column's name.
       */
      public String getName() {
        return descriptor.getName();
      }

      /**
       * Gets the name of the SQL type of the values this column can contain. The name is determined
       * from the SQL type code by using the {@link JDBCType} class.
       * @return the name of the column's SQL type (VARCHAR, TIMESTAMP, ...)
       */
      public String getTypeName() {
        return JDBCType.valueOf(getType()).getName();
      }

      /**
       * Gets the size of this column. It depends on its type; for example for VARCHAR(256), its
       * size is 256.
       * @return the size of this column according to its type.
       */
      public int getSize() {
        int size = descriptor.getSize();
        if (getType() == Types.BIT && descriptor.getSize() == 1) {
          size = Math.max("true".length(), "false".length());
        }
        return size;
      }

      /**
       * Is this column represents a primary key?
       * @return true if the values in this column are or are part of a primary key. False otherwise.
       */
      public boolean isPrimaryKey() {
        return descriptor.isPrimaryKey();
      }

      /**
       * Is this column references a column of another table. If true, then the column takes part of
       * a foreign key whose name is given by the {@link DbColumn#getName()} method.
       * @return true if the values in this column are the primary keys of another table. False
       * otherwise.
       */
      public boolean isForeignKey() {
        return descriptor.getForeignKey() != null;
      }

      /**
       * Gets the name of the foreign key to which this column is a component. Several columns can
       * be part of the same foreign key; in this case, the foreign key name is a way to figure out
       * them.
       * If this column isn't a foreign key, then {@link NullPointerException} is thrown.
       * @return the unique foreign key name.
       */
      public String getForeignKeyName() {
        Objects.requireNonNull(descriptor.getForeignKey());
        return descriptor.getForeignKey().getName();
      }

      /**
       * Gets the column that is referenced by this column if this column is a foreign key.
       * If this column isn't a foreign key, then {@link NullPointerException} is thrown.
       * @return the name of the column referenced by this foreign key.
       */
      public String getReferencedColumn() {
        Objects.requireNonNull(descriptor.getForeignKey());
        return descriptor.getForeignKey().getTargetColumnName();
      }

      /**
       * Gets the table that is referenced by this column if this column is a foreign key.
       * If this column isn't a foreign key, then {@link NullPointerException} is thrown.
       * @return the name of the table referenced by this foreign key.
       */
      public String getReferencedTable() {
        Objects.requireNonNull(descriptor.getForeignKey());
        return descriptor.getForeignKey().getTargetTableName();
      }

      /**
       * Is this column accepts null values?
       * @return true if this column is nullable. False otherwise.
       */
      public boolean isNullable() {
        return descriptor.isNullable();
      }

      /**
       * Is the valuation of this column autogenerated when a new tuple is inserted into the table?
       * @return true if this column is auto-valuated by the database when a row is newly inserted into
       * the table this column belongs to.
       */
      public boolean isAutoValued() {
        return descriptor.isAutoIncrementable();
      }

      /**
       * Is there a default value defined for this column?
       * @return true if a default value is defined for this column, false otherwise.
       */
      public boolean isDefaultValueDefined() {
        return this.descriptor.getDefaultValue().isDefined();
      }

      /**
       * Gets the String representation of the value by default of this column when no one is
       * explicitly set.
       * @return as a {@link String} the default value of this column.
       */
      public String getDefaultValue() {
        return this.descriptor.getDefaultValue().get();
      }

      /**
       * Is this column is of type text (VARCHAR, CLOB, ...)?
       * @return true if the values of this column are textual. False otherwise.
       */
      public boolean isOfTypeText() {
        return SqlTypes.isText(getType());
      }

      /**
       * Is this column is of type binary (BLOB, VARBINARY, ...)?
       * @return true if the values of this column are binary. False otherwise.
       */
      public boolean isOfTypeBinary() {
        return SqlTypes.isBinary(getType());
      }

      /**
       * Is this column of type number (INTEGER, BIGDECIMAL, ...)?
       * @return true if the values of this column are numbers. False otherwise.
       */
      public boolean isOfTypeNumber() {
        final int type = getType();
        return SqlTypes.isBigInteger(type) || SqlTypes.isDouble(type) || SqlTypes.isFloat(type) ||
            SqlTypes.isInteger(type) || SqlTypes.isDecimal(type);
      }

      /**
       * Is this column of type date time (TIMESTAMP, TIMESTAMP_WITH_TIMEZONE, ...)?
       * @return true if the values of this column are date times. False otherwise.
       */
      public boolean isOfTypeDateTime() {
        return SqlTypes.isTimestamp(getType());
      }

      /**
       * Gets a JDBC value of given value. The aim is to get a value to fill into
       * JDBC query criteria filters.
       * <p>
       * Comma character is used to define a list of values.
       * </p>
       * @param value a value to convert.
       * @return a JDBC value according to the SQL type of the column.
       */
      public Object getJdbcValueOf(final String value) {
        try {
          if (isDefined(value)) {
            final Object[] values = Stream
                .of(value.replace("\\,", "\\@@##@@").split(","))
                .map(v -> v.replace("\\@@##@@", "\\,"))
                .map(v -> TableFieldValue.fromString(v, getType()).toSQLObject())
                .toArray();
            return values.length == 1 ? values[0] : values;
          }
        } catch (final Exception e) {
          SilverLogger.getLogger(this).warn(e);
        }
        return value;
      }
    }