code: package it.unibz.inf.ontop.injection.impl;
  
  import it.unibz.inf.ontop.exception.InvalidOntopConfigurationException;
  import it.unibz.inf.ontop.injection.OntopModelSettings;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.Enumeration;
  import java.util.Optional;
  import java.util.Properties;
  
  public class OntopModelSettingsImpl implements OntopModelSettings {
  
      private static final String DEFAULT_PROPERTIES_FILE = "default.properties";
      private static final Logger LOG = LoggerFactory.getLogger(OntopModelSettings.class);
      private final Properties properties;
      private final CardinalityPreservationMode cardinalityMode;
      private final boolean testMode;
      private final boolean dsiableLimitOptimization;
  
      /**
       * Beware: immutable class!
       *
       * Recommended constructor.
       *
       * Changing the Properties object afterwards will not have any effect
       * on this OntopModelProperties object.
       */
      protected OntopModelSettingsImpl(Properties userProperties) {
          /**
           * Loads default properties
           */
          properties = loadDefaultPropertiesFromFile(OntopModelSettings.class, DEFAULT_PROPERTIES_FILE);
          /**
           * Overloads the default properties.
           */
          properties.putAll(userProperties);
  
          cardinalityMode = extractCardinalityMode(properties);
          testMode = extractBoolean(properties, OntopModelSettings.TEST_MODE);
          dsiableLimitOptimization = extractBoolean(properties, OntopModelSettings.DISABLE_LIMIT_OPTIMIZATION);
      }
  
      private static CardinalityPreservationMode extractCardinalityMode(Properties properties)
              throws InvalidOntopConfigurationException {
          Object cardinalityModeObject = Optional.ofNullable(properties.get(OntopModelSettings.CARDINALITY_MODE))
                  .orElseThrow(() -> new InvalidOntopConfigurationException(CARDINALITY_MODE + " key is missing"));
  
          if (cardinalityModeObject instanceof String) {
              try {
                  return CardinalityPreservationMode.valueOf((String)cardinalityModeObject);
              } catch (IllegalArgumentException e) {
              }
          }
          else if (cardinalityModeObject instanceof CardinalityPreservationMode)
              return (CardinalityPreservationMode) cardinalityModeObject;
  
          throw new InvalidOntopConfigurationException("Invalid value for " + CARDINALITY_MODE
                  + ": is " + cardinalityModeObject);
      }
  
      private static boolean extractBoolean(Properties properties, String key) {
          Object value = Optional.ofNullable(properties.get(key))
                  .orElseThrow(() -> new InvalidOntopConfigurationException(key + " key is missing"));
          if (value instanceof Boolean)
              return (Boolean) value;
          else if (value instanceof String)
              return Boolean.valueOf((String) value);
          else
              throw new InvalidOntopConfigurationException("Invalid value for " + key + ": is " + value);
      }
  
      protected static Properties loadDefaultPropertiesFromFile(Class localClass, String fileName) {
          try (InputStream in = localClass.getResourceAsStream(fileName)) {
              if (in == null)
                  throw new RuntimeException("Configuration " + fileName + " not found.");
              Properties properties = new Properties();
              properties.load(in);
              return properties;
          }
          catch (IOException e1) {
              LOG.error("Error reading default OBDA properties.");
              LOG.debug(e1.getMessage(), e1);
              throw new RuntimeException("Impossible to extract configuration from " + fileName);
          }
      }
  
      /**
       * Returns the value of the given key.
       */
      public Object get(Object key) {
          return properties.get(key);
      }
  
      /**
       * TODO: make it configurable
       */
      @Override
      public CardinalityPreservationMode getCardinalityPreservationMode() {
          return cardinalityMode;
      }
  
      @Override
      public boolean isTestModeEnabled() {
          return testMode;
      }
  
      @Override
      public boolean isLimitOptimizationDisabled() {
          return dsiableLimitOptimization;
      }
  
      /**
       * Returns the boolean value of the given key.
       */
      Optional<Boolean> getBoolean(String key) {
          return getBoolean(properties, key);
      }
  
      static Optional<Boolean> getBoolean(Properties properties, String key) {
          Object value = properties.get(key);
  
          if (value == null) {
              return Optional.empty();
          }
  
          if (value instanceof Boolean) {
              return Optional.of((Boolean) value);
          }
          else if (value instanceof String) {
              return Optional.of(Boolean.parseBoolean((String)value));
          }
          else {
              throw new InvalidOntopConfigurationException("A boolean was expected: " + value);
          }
      }
  
      boolean getRequiredBoolean(String key) {
          return getBoolean(key)
                  .orElseThrow(() -> new InvalidOntopConfigurationException(key + " is required but missing " +
                          "(must have a default value)"));
      }
  
      /**
       * Returns the integer value of the given key.
       */
      Optional<Integer> getInteger(String key) {
          String value = (String) get(key);
          return Optional.ofNullable((value == null) ? null : Integer.parseInt(value));
      }
  
      /**
       * Returns the long value of the given key.
       */
      Optional<Long> getLong(String key) {
          String value = (String) get(key);
          return Optional.ofNullable((value == null) ? null : Long.parseLong(value));
      }
  
      int getRequiredInteger(String key) {
          return getInteger(key)
                  .orElseThrow(() -> new InvalidOntopConfigurationException(key + " is required but missing " +
                          "(must have a default value)"));
      }
  
      long getRequiredLong(String key) {
          return getLong(key)
                  .orElseThrow(() -> new InvalidOntopConfigurationException(key + " is required but missing " +
                          "(must have a default value)"));
      }
  
      /**
       * Returns the string value of the given key.
       */
      @Override
      public Optional<String> getProperty(String key) {
          return Optional.ofNullable((String) get(key));
      }
  
      String getRequiredProperty(String key) {
          return getProperty(key)
                  .orElseThrow(() -> new InvalidOntopConfigurationException(key + " is required but missing " +
                          "(must have a default value)"));
      }
  
      @Override
      public boolean contains(Object key) {
          return properties.containsKey(key);
      }
  
      protected Properties copyProperties() {
          Properties p = new Properties();
          p.putAll(properties);
          return p;
      }
  
      protected Enumeration<Object> getPropertyKeys() {
          return properties.keys();
      }
  
  
  }
  