content: /*
   * Copyright (C) 2012-2023 52°North Spatial Information Research GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *     - Apache License, version 2.0
   *     - Apache Software License, version 1.0
   *     - GNU Lesser General Public License, version 3
   *     - Mozilla Public License, versions 1.0, 1.1 and 2.0
   *     - Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * 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 General
   * Public License for more details.
   */
  package org.n52.sos.ds.datasource;
  
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Properties;
  import java.util.Set;
  
  import org.n52.faroe.SettingDefinition;
  import org.n52.faroe.settings.BooleanSettingDefinition;
  import org.n52.faroe.settings.IntegerSettingDefinition;
  import org.n52.faroe.settings.StringSettingDefinition;
  import org.n52.shetland.util.JavaHelper;
  import org.n52.sos.ds.hibernate.util.HibernateConstants;
  
  import com.google.common.collect.Sets;
  
  
  public abstract class AbstractHibernateFullDBDatasource extends AbstractHibernateDatasource {
  
      private String schemaDefault;
      private String schemaDescription;
      private int batchSizeDefault;
      private boolean providedJdbc;
      private boolean supportsSchema;
  
      AbstractHibernateFullDBDatasource(boolean supportsSchema) {
          this.supportsSchema = supportsSchema;
          setMinPoolSizeDefault(MIN_POOL_SIZE_DEFAULT_VALUE);
          setMaxPoolSizeDefault(MAX_POOL_SIZE_DEFAULT_VALUE);
          setBatchSizeDefault(BATCH_SIZE_DEFAULT_VALUE);
      }
  
      public AbstractHibernateFullDBDatasource() {
          this(true);
      }
  
      @Override
      public Set<SettingDefinition<?>> getSettingDefinitions() {
          Set<SettingDefinition<?>> set = super.getSettingDefinitions();
          if (supportsSchema) {
              set.add(createSchemaDefinition(schemaDefault));
          }
          set.add(createBatchSizeDefinition(batchSizeDefault));
          set.add(createProvidedJdbcDriverDefinition(providedJdbc));
          set.add(getDatabaseConceptDefinition());
          set.add(getDatabaseExtensionDefinition());
          set.add(getFeatureConceptDefinition());
          return set;
      }
  
      @Override
      public Set<SettingDefinition<?>> getChangableSettingDefinitions(final Properties current) {
          final Map<String, Object> settings = parseDatasourceProperties(current);
          StringSettingDefinition schemaSetting = createSchemaDefinition((String) settings.get(SCHEMA_KEY));
          HashSet<SettingDefinition<?>> settingDefinitions
                  = Sets.<SettingDefinition<?>>newHashSet(
                          createUsernameDefinition((String) settings.get(USERNAME_KEY)),
                          createPasswordDefinition((String) settings.get(PASSWORD_KEY)),
                          createDatabaseDefinition((String) settings.get(DATABASE_KEY)),
                          createHostDefinition((String) settings.get(HOST_KEY)),
                          createPortDefinition(JavaHelper.asInteger(settings.get(PORT_KEY))),
                          createMinPoolSizeDefinition(JavaHelper.asInteger(settings.get(MIN_POOL_SIZE_KEY))),
                          createMaxPoolSizeDefinition(JavaHelper.asInteger(settings.get(MAX_POOL_SIZE_KEY))),
                          createBatchSizeDefinition(JavaHelper.asInteger(settings.get(BATCH_SIZE_KEY))),
                          createTimeZoneDefinition((String) settings.get(TIMEZONE_KEY)));
          if (supportsSchema) {
              settingDefinitions.add(schemaSetting);
          }
          return settingDefinitions;
      }
  
      protected StringSettingDefinition createSchemaDefinition(final String defaultValue) {
          StringSettingDefinition def = createSchemaDefinition();
          def.setDescription(schemaDescription);
          def.setDefaultValue(defaultValue);
          return def;
      }
  
      protected SettingDefinition<?> createBatchSizeDefinition(final Integer defaultValue) {
          IntegerSettingDefinition def = createBatchSizeDefinition();
          def.setDefaultValue(defaultValue);
          return def;
      }
  
      protected SettingDefinition<?> createProvidedJdbcDriverDefinition(final Boolean defaultValue) {
          BooleanSettingDefinition def = createProvidedJdbcDriverDefinition();
          def.setDefaultValue(defaultValue);
          return def;
      }
  
      @Override
      public Properties getDatasourceProperties(final Map<String, Object> settings) {
          final Properties p = new Properties();
          if (supportsSchema) {
              p.put(HibernateConstants.DEFAULT_SCHEMA, settings.get(SCHEMA_KEY));
          }
          p.put(HibernateConstants.CONNECTION_USERNAME, settings.get(USERNAME_KEY));
          p.put(HibernateConstants.CONNECTION_PASSWORD, settings.get(PASSWORD_KEY));
          p.put(HibernateConstants.CONNECTION_URL, toURL(settings));
          p.put(HibernateConstants.CONNECTION_PROVIDER_CLASS, C3P0_CONNECTION_POOL);
          p.put(HibernateConstants.DIALECT, getDialectClass());
          p.put(HibernateConstants.DRIVER_CLASS, getDriverClass());
          p.put(HibernateConstants.C3P0_MIN_SIZE, settings.get(MIN_POOL_SIZE_KEY).toString());
          p.put(HibernateConstants.C3P0_MAX_SIZE, settings.get(MAX_POOL_SIZE_KEY).toString());
          p.put(HibernateConstants.C3P0_IDLE_TEST_PERIOD, "30");
          p.put(HibernateConstants.C3P0_ACQUIRE_INCREMENT, "1");
          p.put(HibernateConstants.C3P0_TIMEOUT, "0");
          p.put(HibernateConstants.C3P0_MAX_STATEMENTS, "0");
          p.put(HibernateConstants.C3P0_PRIVILEGE_SPAWNED_THREAD, Boolean.TRUE.toString());
          p.put(HibernateConstants.C3P0_CONTEXT_CLASS_LOADER_SOURCE, "library");
          if (settings.containsKey(BATCH_SIZE_KEY)) {
              p.put(HibernateConstants.JDBC_BATCH_SIZE, settings.get(BATCH_SIZE_KEY).toString());
          }
          p.put(HibernateConstants.CONNECTION_AUTO_RECONNECT, Boolean.TRUE.toString());
          p.put(HibernateConstants.CONNECTION_AUTO_RECONNECT_FOR_POOLS, Boolean.TRUE.toString());
          p.put(HibernateConstants.CONNECTION_TEST_ON_BORROW, Boolean.TRUE.toString());
          p.put(PROVIDED_JDBC, settings.get(PROVIDED_JDBC_DRIVER_KEY).toString());
          p.put(DATABASE_CONCEPT_KEY, settings.get(DATABASE_CONCEPT_KEY));
          p.put(DATABASE_EXTENSION_KEY, settings.get(DATABASE_EXTENSION_KEY));
          p.put(FEATURE_CONCEPT_KEY, settings.get(FEATURE_CONCEPT_KEY));
          if (settings.containsKey(TIMEZONE_KEY)) {
              p.put(HibernateConstants.JDBC_TIME_ZONE, settings.get(TIMEZONE_KEY));
          }
          if (settings.containsKey(TIME_STRING_FORMAT_KEY) && settings.get(TIME_STRING_FORMAT_KEY) != null) {
              p.put(HIBERNATE_DATASOURCE_TIME_STRING_FORMAT, settings.get(TIME_STRING_FORMAT_KEY));
          }
          if (settings.containsKey(TIME_STRING_Z_KEY)) {
              p.put(HIBERNATE_DATASOURCE_TIME_STRING_Z, settings.get(TIME_STRING_Z_KEY).toString());
          }
          if (getSpringProfiles() != null && !getSpringProfiles().isEmpty()) {
              p.put(SPRING_PROFILE_KEY,  String.join(",", getSpringProfiles()));
          }
          addMappingFileDirectories(settings, p);
  
          return p;
      }
  
      @Override
      public Map<String, Object> parseDatasourceProperties(final Properties current) {
          final Map<String, Object> settings = new HashMap<String, Object>(current.size());
          if (supportsSchema) {
              settings.put(SCHEMA_KEY, current.getProperty(HibernateConstants.DEFAULT_SCHEMA));
          }
          settings.put(HIBERNATE_DIRECTORY, current.get(HIBERNATE_DIRECTORY));
          settings.put(USERNAME_KEY, current.getProperty(HibernateConstants.CONNECTION_USERNAME));
          settings.put(PASSWORD_KEY, current.getProperty(HibernateConstants.CONNECTION_PASSWORD));
          settings.put(MIN_POOL_SIZE_KEY, current.getProperty(HibernateConstants.C3P0_MIN_SIZE));
          settings.put(MAX_POOL_SIZE_KEY, current.getProperty(HibernateConstants.C3P0_MAX_SIZE));
          if (current.containsKey(HibernateConstants.JDBC_BATCH_SIZE)) {
              settings.put(BATCH_SIZE_KEY, current.getProperty(HibernateConstants.JDBC_BATCH_SIZE));
          }
          settings.put(DATABASE_CONCEPT_KEY,  current.getProperty(DATABASE_CONCEPT_KEY));
          settings.put(DATABASE_EXTENSION_KEY, current.getProperty(DATABASE_EXTENSION_KEY));
          settings.put(FEATURE_CONCEPT_KEY,  current.getProperty(FEATURE_CONCEPT_KEY));
          settings.put(PROVIDED_JDBC_DRIVER_KEY,
                  current.getProperty(PROVIDED_JDBC, PROVIDED_JDBC_DRIVER_DEFAULT_VALUE.toString()));
          if (current.containsKey(HIBERNATE_DATASOURCE_TIMEZONE)) {
              settings.put(TIMEZONE_KEY, current.getProperty(HIBERNATE_DATASOURCE_TIMEZONE));
          } else {
              settings.put(TIMEZONE_KEY, current.getProperty(HibernateConstants.JDBC_TIME_ZONE));
          }
          if (current.containsKey(HIBERNATE_DATASOURCE_TIME_STRING_FORMAT)) {
              settings.put(TIME_STRING_FORMAT_KEY, current.getProperty(HIBERNATE_DATASOURCE_TIME_STRING_FORMAT));
          }
          if (current.containsKey(HIBERNATE_DATASOURCE_TIME_STRING_Z)) {
              settings.put(TIME_STRING_Z_KEY, Boolean.valueOf(current.getProperty(HIBERNATE_DATASOURCE_TIME_STRING_Z)));
          }
          if (current.containsKey(SPRING_PROFILE_KEY)) {
              settings.put(SPRING_PROFILE_KEY, current.getProperty(SPRING_PROFILE_KEY));
          }
          final String url = current.getProperty(HibernateConstants.CONNECTION_URL);
  
          final String[] parsed = parseURL(url);
          final String host = parsed[0];
          final String port = parsed[1];
          final String db = parsed[2];
  
          settings.put(createHostDefinition().getKey(), host);
          settings.put(createPortDefinition().getKey(), JavaHelper.asInteger(port));
          settings.put(createDatabaseDefinition().getKey(), db);
          return settings;
      }
  
      private String getDialectClass() {
          return createDialect().getClass().getCanonicalName();
      }
  
      /**
       * @param schemaDefault
       *            the schemaDefault to set
       */
      public void setSchemaDefault(final String schemaDefault) {
          this.schemaDefault = schemaDefault;
      }
  
      /**
       * @param schemaDescription
       *            the schemaDescription to set
       */
      public void setSchemaDescription(final String schemaDescription) {
          this.schemaDescription = schemaDescription;
      }
  
      public void setBatchSizeDefault(Integer batchSizeDefaultValue) {
          this.batchSizeDefault = batchSizeDefaultValue;
      }
  
      public void setProvidedJdbcDefault(final boolean providedJdbc) {
          this.providedJdbc = providedJdbc;
      }
  }
  