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.carbondata.core.util;
  
  import java.io.Serializable;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  
  import org.apache.carbondata.common.constants.LoggerAction;
  import org.apache.carbondata.common.logging.LogServiceFactory;
  import org.apache.carbondata.core.constants.CarbonCommonConstants;
  import org.apache.carbondata.core.constants.CarbonLoadOptionConstants;
  import org.apache.carbondata.core.exception.InvalidConfigurationException;
  
  import static org.apache.carbondata.core.constants.CarbonCommonConstants.*;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_BAD_RECORDS_ACTION;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_BAD_RECORDS_LOGGER_ENABLE;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_BAD_RECORD_PATH;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_DATEFORMAT;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_GLOBAL_SORT_PARTITIONS;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_IS_EMPTY_DATA_BAD_RECORD;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_SERIALIZATION_NULL_FORMAT;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_SORT_SCOPE;
  import static org.apache.carbondata.core.constants.CarbonLoadOptionConstants.CARBON_OPTIONS_TIMESTAMPFORMAT;
  import static org.apache.carbondata.core.constants.CarbonV3DataFormatConstants.BLOCKLET_SIZE_IN_MB;
  
  import org.apache.log4j.Logger;
  
  /**
   * This class maintains carbon session params
   */
  public class SessionParams implements Serializable, Cloneable {
  
    private static final Logger LOGGER =
        LogServiceFactory.getLogService(SessionParams.class.getName());
    private static final long serialVersionUID = -7801994600594915264L;
  
    private Map<String, String> sProps;
    private ConcurrentHashMap<String, String> addedProps;
    // below field to be used when we want the objects to be serialized
    private Map<String, Object> extraInfo;
    public SessionParams() {
      sProps = new HashMap<>();
      addedProps = new ConcurrentHashMap<>();
      extraInfo = new HashMap<>();
    }
  
    public void setExtraInfo(String key, Object value) {
      this.extraInfo.put(key, value);
    }
  
    public Object getExtraInfo(String key) {
      return this.extraInfo.get(key);
    }
  
    /**
     * This method will be used to get the properties value
     *
     * @param key
     * @return properties value
     */
    public String getProperty(String key) {
      return sProps.get(key);
    }
  
    public String getProperty(String key, String defaultValue) {
      if (!sProps.containsKey(key)) {
        return defaultValue;
      }
      return sProps.get(key);
    }
  
    /**
     * This method will be used to add a new property
     *
     * @param key
     * @return properties value
     */
    public SessionParams addProperty(String key, String value) throws InvalidConfigurationException {
      boolean isValidConf = validateKeyValue(key, value);
      if (isValidConf) {
        if (key.equals(CarbonLoadOptionConstants.CARBON_OPTIONS_BAD_RECORDS_ACTION)) {
          value = value.toUpperCase();
        }
        LOGGER.info(
            "The key " + key + " with value " + value + " added in the session param");
        sProps.put(key, value);
      }
      return this;
    }
  
    public Map<String, String> getAll() {
      return sProps;
    }
  
    public SessionParams addProps(Map<String, String> addedProps) {
      this.addedProps.putAll(addedProps);
      return this;
    }
  
    public Map<String, String> getAddedProps() {
      return addedProps;
    }
  
    /**
     * validate the key value to be set using set command
     * @param key
     * @param value
     * @return
     * @throws InvalidConfigurationException
     */
    private boolean validateKeyValue(String key, String value) throws InvalidConfigurationException {
      boolean isValid = false;
      switch (key) {
        case ENABLE_SI_LOOKUP_PARTIALSTRING:
        case ENABLE_UNSAFE_SORT:
        case ENABLE_OFFHEAP_SORT:
        case CARBON_CUSTOM_BLOCK_DISTRIBUTION:
        case CARBON_OPTIONS_BAD_RECORDS_LOGGER_ENABLE:
        case CARBON_OPTIONS_IS_EMPTY_DATA_BAD_RECORD:
        case ENABLE_VECTOR_READER:
        case ENABLE_UNSAFE_IN_QUERY_EXECUTION:
        case ENABLE_AUTO_LOAD_MERGE:
        case CARBON_PUSH_ROW_FILTERS_FOR_VECTOR:
        case CARBON_LOAD_DATEFORMAT_SETLENIENT_ENABLE:
        case CARBON_LOAD_SI_REPAIR:
        case CARBON_ENABLE_INDEX_SERVER:
        case CARBON_QUERY_STAGE_INPUT:
        case CARBON_ENABLE_MV:
        case CARBON_COARSE_GRAIN_SECONDARY_INDEX:
          isValid = CarbonUtil.validateBoolean(value);
          if (!isValid) {
            throw new InvalidConfigurationException("Invalid value " + value + " for key " + key);
          }
          break;
        case CARBON_OPTIONS_BAD_RECORDS_ACTION:
          try {
            LoggerAction.valueOf(value.toUpperCase());
            isValid = true;
          } catch (IllegalArgumentException iae) {
            throw new InvalidConfigurationException(
                "The key " + key + " can have only either FORCE or IGNORE or REDIRECT or FAIL.");
          }
          break;
        case CARBON_OPTIONS_SORT_SCOPE:
          isValid = CarbonUtil.isValidSortOption(value);
          if (!isValid) {
            throw new InvalidConfigurationException("The sort scope " + key
                + " can have only either NO_SORT, LOCAL_SORT or GLOBAL_SORT.");
          }
          break;
        case CARBON_OPTIONS_GLOBAL_SORT_PARTITIONS:
        case NUM_CORES_LOADING:
        case CARBON_SI_REPAIR_LIMIT:
        case NUM_CORES_COMPACTING:
        case BLOCKLET_SIZE_IN_MB:
        case CARBON_MAJOR_COMPACTION_SIZE:
        case CARBON_MINOR_COMPACTION_SIZE:
          isValid = CarbonUtil.validateValidIntType(value);
          if (!isValid) {
            throw new InvalidConfigurationException(
                "The configured value for key " + key + " must be valid integer.");
          }
          break;
        case CARBON_OPTIONS_BAD_RECORD_PATH:
          isValid = CarbonUtil.isValidBadStorePath(value);
          if (!isValid) {
            throw new InvalidConfigurationException("Invalid bad records location.");
          }
          break;
        // no validation needed while set for CARBON_OPTIONS_DATEFORMAT
        // no validation needed while set for CARBON_OPTIONS_TIMESTAMPFORMAT
        // no validation needed while set for CARBON_OPTIONS_SERIALIZATION_NULL_FORMAT
        case CARBON_OPTIONS_DATEFORMAT:
        case CARBON_OPTIONS_TIMESTAMPFORMAT:
        case CARBON_OPTIONS_SERIALIZATION_NULL_FORMAT:
          isValid = true;
          break;
        case COMPACTION_SEGMENT_LEVEL_THRESHOLD:
          int[] values = CarbonProperties.getInstance().getIntArray(value);
          if (values.length != 2) {
            throw new InvalidConfigurationException(
                "Invalid COMPACTION_SEGMENT_LEVEL_THRESHOLD: " + value);
          }
          isValid = true;
          break;
        default:
          if (key.startsWith(CARBON_ENABLE_INDEX_SERVER) && key.split("\\.").length == 6) {
            isValid = true;
          } else if (key.startsWith(CARBON_SI_REPAIR_LIMIT)) {
            isValid = CarbonUtil.validateValidIntType(value);
            if (!isValid) {
              throw new InvalidConfigurationException("Invalid CARBON_SI_REPAIR_LIMIT");
            }
          } else if (key.startsWith(CARBON_LOAD_SI_REPAIR) && key.split("\\.").length == 6) {
            isValid = true;
          } else if (key.startsWith(CarbonCommonConstants.CARBON_INPUT_SEGMENTS)) {
            isValid = CarbonUtil.validateRangeOfSegmentList(value);
            if (!isValid) {
              throw new InvalidConfigurationException("Invalid CARBON_INPUT_SEGMENT_IDs");
            }
          } else if (key.startsWith(CarbonCommonConstants.CARBON_INDEX_VISIBLE)) {
            isValid = true;
          } else if (key.startsWith(CarbonCommonConstants.CARBON_LOAD_INDEXES_PARALLEL) || (
              key.startsWith(CARBON_COARSE_GRAIN_SECONDARY_INDEX) && key.split("\\.").length == 7)) {
            // validate the value field when property key is with database name and table name.
            // Like, carbon.coarse.grain.secondary.index.<dbname>.<tablename>
            isValid = CarbonUtil.validateBoolean(value);
            if (!isValid) {
              throw new InvalidConfigurationException("Invalid value " + value + " for key " + key);
            }
          } else if (key.startsWith(CarbonLoadOptionConstants.CARBON_TABLE_LOAD_SORT_SCOPE)) {
            isValid = CarbonUtil.isValidSortOption(value);
            if (!isValid) {
              throw new InvalidConfigurationException("The sort scope " + key
                  + " can have only either NO_SORT, LOCAL_SORT or GLOBAL_SORT.");
            }
          } else if (key.equalsIgnoreCase(CARBON_REORDER_FILTER)) {
            isValid = true;
          } else if (key.startsWith(CARBON_MAP_ORDER_PUSHDOWN)) {
            isValid = true;
          } else {
            throw new InvalidConfigurationException(
                "The key " + key + " not supported for dynamic configuration.");
          }
      }
      return isValid;
    }
  
    public void removeProperty(String property) {
      sProps.remove(property);
    }
  
    public void removeExtraInfo(String key) {
      extraInfo.remove(key);
    }
  
    /**
     * clear the set properties
     */
    public void clear() {
      sProps.clear();
    }
  
    public SessionParams clone() throws CloneNotSupportedException {
      super.clone();
      SessionParams newObj = new SessionParams();
      newObj.addedProps.putAll(this.addedProps);
      newObj.sProps.putAll(this.sProps);
      newObj.extraInfo.putAll(this.extraInfo);
      return newObj;
    }
  }
  