code: package it.unibz.inf.ontop.injection.impl;
  
  
  import com.google.inject.Module;
  import it.unibz.inf.ontop.exception.InvalidMappingException;
  import it.unibz.inf.ontop.exception.MappingIOException;
  import it.unibz.inf.ontop.exception.OBDASpecificationException;
  import it.unibz.inf.ontop.injection.impl.OntopSQLCoreConfigurationImpl.DefaultOntopSQLCoreBuilderFragment;
  import it.unibz.inf.ontop.injection.impl.OntopSQLCredentialConfigurationImpl.DefaultOntopSQLCredentialBuilderFragment;
  import it.unibz.inf.ontop.injection.impl.OntopSQLCredentialConfigurationImpl.OntopSQLCredentialOptions;
  import it.unibz.inf.ontop.exception.InvalidOntopConfigurationException;
  import it.unibz.inf.ontop.injection.OntopMappingSQLConfiguration;
  import it.unibz.inf.ontop.injection.OntopMappingSQLSettings;
  import it.unibz.inf.ontop.spec.mapping.parser.SQLMappingParser;
  import it.unibz.inf.ontop.spec.mapping.pp.PreProcessedMapping;
  import it.unibz.inf.ontop.spec.OBDASpecification;
  import it.unibz.inf.ontop.spec.mapping.pp.SQLPPMapping;
  import org.apache.commons.rdf.api.Graph;
  
  import javax.annotation.Nonnull;
  import java.io.File;
  import java.io.Reader;
  import java.util.Optional;
  import java.util.Properties;
  import java.util.function.Supplier;
  import java.util.stream.Stream;
  
  public class OntopMappingSQLConfigurationImpl extends OntopMappingConfigurationImpl implements OntopMappingSQLConfiguration {
  
      private final OntopMappingSQLSettings settings;
      private final OntopMappingSQLOptions options;
      private final OntopSQLCredentialConfigurationImpl sqlConfiguration;
  
      OntopMappingSQLConfigurationImpl(OntopMappingSQLSettings settings, OntopMappingSQLOptions options) {
          super(settings, options.mappingOptions);
          this.settings = settings;
          this.options = options;
          this.sqlConfiguration = new OntopSQLCredentialConfigurationImpl(settings, options.sqlOptions, this::getInjector);
      }
  
      boolean isInputMappingDefined() {
          return options.ppMapping.isPresent();
      }
  
      /**
       * To be overloaded
       *
       */
      @Override
      protected Stream<Module> buildGuiceModules() {
          return Stream.concat(Stream.concat(
                      super.buildGuiceModules(),
                      sqlConfiguration.buildGuiceModules()),
                  Stream.of(
                          new OntopMappingSQLModule(this),
                          new OntopMappingPostModule(getSettings())));
      }
  
      @Override
      public OntopMappingSQLSettings getSettings() {
          return settings;
      }
  
      /**
       * Can be overloaded
       *
       * With this default implementation, can load a given pre-processed mapping
       *
       */
      @Override
      protected OBDASpecification loadOBDASpecification() throws OBDASpecificationException {
          return loadSpecification(Optional::empty, Optional::empty, Optional::empty, Optional::empty, Optional::empty,
                  Optional::empty, Optional::empty, Optional::empty, Optional::empty);
      }
  
      OBDASpecification loadSpecification(OntologySupplier ontologySupplier,
                                          Supplier<Optional<File>> mappingFileSupplier,
                                          Supplier<Optional<Reader>> mappingReaderSupplier,
                                          Supplier<Optional<Graph>> mappingGraphSupplier,
                                          Supplier<Optional<File>> constraintFileSupplier,
                                          Supplier<Optional<File>> dbMetadataFileSupplier,
                                          Supplier<Optional<Reader>> dbMetadataReaderSupplier,
                                          Supplier<Optional<File>> lensesFileSupplier,
                                          Supplier<Optional<Reader>> lensesReaderSupplier)
              throws OBDASpecificationException {
          return loadSpecification(
                  ontologySupplier,
                  () -> options.ppMapping.map(m -> (PreProcessedMapping) m),
                  mappingFileSupplier,
                  mappingReaderSupplier,
                  mappingGraphSupplier,
                  constraintFileSupplier,
                  dbMetadataFileSupplier,
                  dbMetadataReaderSupplier,
                  lensesFileSupplier,
                  lensesReaderSupplier
          );
      }
  
  
      @Override
      public Optional<SQLPPMapping> loadPPMapping() throws MappingIOException, InvalidMappingException {
          return loadPPMapping(Optional::empty, Optional::empty, Optional::empty);
      }
  
      /**
       * TODO: also consider the other steps
       */
      Optional<SQLPPMapping> loadPPMapping(Supplier<Optional<File>> mappingFileSupplier,
                                           Supplier<Optional<Reader>> mappingReaderSupplier,
                                           Supplier<Optional<Graph>> mappingGraphSupplier)
              throws MappingIOException, InvalidMappingException {
  
          if (options.ppMapping.isPresent()) {
              return options.ppMapping;
          }
  
          SQLMappingParser parser = getInjector().getInstance(SQLMappingParser.class);
  
          Optional<File> optionalMappingFile = mappingFileSupplier.get();
          if (optionalMappingFile.isPresent()) {
              return Optional.of(parser.parse(optionalMappingFile.get()));
          }
  
          Optional<Reader> optionalMappingReader = mappingReaderSupplier.get();
          if (optionalMappingReader.isPresent()) {
              // The parser is in charge of closing the reader
              return Optional.of(parser.parse(optionalMappingReader.get()));
          }
          Optional<Graph> optionalMappingGraph = mappingGraphSupplier.get();
          if (optionalMappingGraph.isPresent()) {
              return Optional.of(parser.parse(optionalMappingGraph.get()));
          }
  
          return Optional.empty();
      }
  
  
      /**
       * Groups all the options required by the OBDAConfiguration.
       *
       * Useful for extensions
       *
       */
      public static class OntopMappingSQLOptions {
          final OntopSQLCredentialOptions sqlOptions;
          final OntopMappingOptions mappingOptions;
          final Optional<SQLPPMapping> ppMapping;
  
          private OntopMappingSQLOptions(Optional<SQLPPMapping> ppMapping, OntopSQLCredentialOptions sqlOptions,
                                         OntopMappingOptions mappingOptions) {
              this.sqlOptions = sqlOptions;
              this.mappingOptions = mappingOptions;
              this.ppMapping = ppMapping;
          }
      }
  
      protected static class DefaultMappingSQLBuilderFragment<B extends OntopMappingSQLConfiguration.Builder<B>>
              implements OntopMappingSQLBuilderFragment<B> {
  
          private final B builder;
          private final Supplier<Boolean> isMappingDefinedSupplier;
          private final Runnable declareMappingDefinedCB;
          private Optional<SQLPPMapping> ppMapping = Optional.empty();
  
          /**
           * Default constructor
           */
          protected DefaultMappingSQLBuilderFragment(B builder,
                                                     Supplier<Boolean> isMappingDefinedSupplier,
                                                     Runnable declareMappingDefinedCB) {
              this.builder = builder;
              this.isMappingDefinedSupplier = isMappingDefinedSupplier;
              this.declareMappingDefinedCB = declareMappingDefinedCB;
          }
  
          /**
           * Not for end-users! Please consider giving a mapping file or a mapping reader.
           */
          @Override
          public B ppMapping(@Nonnull SQLPPMapping ppMapping) {
              if (isMappingDefinedSupplier.get()) {
                  throw new InvalidOntopConfigurationException("OBDA model or mappings already defined!");
              }
              declareMappingDefinedCB.run();
              this.ppMapping = Optional.of(ppMapping);
              return builder;
          }
  
  
          final OntopMappingSQLOptions generateMappingSQLOptions(OntopSQLCredentialOptions sqlOptions,
                                                                 OntopMappingOptions mappingOptions) {
              return new OntopMappingSQLOptions(ppMapping, sqlOptions, mappingOptions);
          }
  
          Properties generateProperties() {
              return new Properties();
          }
      }
  
      protected abstract static class OntopMappingSQLBuilderMixin<B extends OntopMappingSQLConfiguration.Builder<B>>
              extends OntopMappingConfigurationImpl.OntopMappingBuilderMixin<B>
              implements OntopMappingSQLConfiguration.Builder<B> {
  
          private final DefaultMappingSQLBuilderFragment<B> localBuilderFragment;
          private final DefaultOntopSQLCoreBuilderFragment<B> sqlCoreBuilderFragment;
          private final DefaultOntopSQLCredentialBuilderFragment<B> sqlCredentialBuilderFragment;
  
          protected OntopMappingSQLBuilderMixin() {
              B builder = (B) this;
              localBuilderFragment = new DefaultMappingSQLBuilderFragment<>(builder,
                      this::isMappingDefined,
                      this::declareMappingDefined);
              sqlCoreBuilderFragment = new DefaultOntopSQLCoreBuilderFragment<>(builder);
              sqlCredentialBuilderFragment = new DefaultOntopSQLCredentialBuilderFragment<>(builder);
          }
  
          @Override
          public B ppMapping(@Nonnull SQLPPMapping ppMapping) {
              return localBuilderFragment.ppMapping(ppMapping);
          }
  
          @Override
          protected Properties generateProperties() {
              Properties properties = super.generateProperties();
              properties.putAll(localBuilderFragment.generateProperties());
              properties.putAll(sqlCoreBuilderFragment.generateProperties());
              properties.putAll(sqlCredentialBuilderFragment.generateProperties());
              return properties;
          }
  
          final OntopMappingSQLOptions generateMappingSQLOptions() {
              OntopOBDAConfigurationImpl.OntopOBDAOptions obdaOptions = generateOBDAOptions();
  
              OntopSQLCredentialOptions sqlOptions = sqlCredentialBuilderFragment.generateSQLCredentialOptions(
                      sqlCoreBuilderFragment.generateSQLCoreOptions(obdaOptions.modelOptions));
  
              return localBuilderFragment.generateMappingSQLOptions(sqlOptions, generateMappingOptions(obdaOptions));
          }
  
          @Override
          public B jdbcUrl(String jdbcUrl) {
              return sqlCoreBuilderFragment.jdbcUrl(jdbcUrl);
          }
  
          @Override
          public B jdbcUser(String username) {
              return sqlCredentialBuilderFragment.jdbcUser(username);
          }
  
          @Override
          public B jdbcPassword(String password) {
              return sqlCredentialBuilderFragment.jdbcPassword(password);
          }
  
          @Override
          public B jdbcDriver(String jdbcDriver) {
              return sqlCoreBuilderFragment.jdbcDriver(jdbcDriver);
          }
      }
  
  
      public static class BuilderImpl<B extends OntopMappingSQLConfiguration.Builder<B>>
              extends OntopMappingSQLBuilderMixin<B> {
  
          @Override
          public OntopMappingSQLConfiguration build() {
              Properties properties = generateProperties();
              OntopMappingSQLSettings settings = new OntopMappingSQLSettingsImpl(properties);
  
              OntopMappingSQLOptions options = generateMappingSQLOptions();
  
              return new OntopMappingSQLConfigurationImpl(settings, options);
          }
      }
  
  }
  