001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.core.configuration;
007
008import java.util.HashMap;
009import java.util.Map;
010
011import dev.metaschema.core.util.CollectionUtil;
012import edu.umd.cs.findbugs.annotations.NonNull;
013
014/**
015 * Provides a basic configuration management implementation that allows mutable
016 * access to configuration state.
017 *
018 * @param <T>
019 *          the type of managed features
020 */
021public class DefaultConfiguration<T extends IConfigurationFeature<?>>
022    implements IMutableConfiguration<T> {
023  @NonNull
024  private Map<T, Object> featureValues;
025
026  /**
027   * Create a new configuration.
028   *
029   */
030  public DefaultConfiguration() {
031    this.featureValues = new HashMap<>();
032  }
033
034  /**
035   * Create a new configuration based on the provided feature value map.
036   *
037   * @param featureValues
038   *          the set of enabled features
039   */
040  public DefaultConfiguration(@NonNull Map<T, Object> featureValues) {
041    this.featureValues = new HashMap<>(featureValues);
042  }
043
044  /**
045   * Create a new configuration based on the provided configuration.
046   *
047   * @param original
048   *          the original configuration
049   */
050  public DefaultConfiguration(@NonNull DefaultConfiguration<T> original) {
051    this(original.getFeatureValues());
052  }
053
054  @Override
055  public Map<T, Object> getFeatureValues() {
056    return CollectionUtil.unmodifiableMap(featureValues);
057  }
058
059  private void ensureBooleanValue(@NonNull T feature) {
060    Class<?> valueClass = feature.getValueClass();
061    if (!Boolean.class.isAssignableFrom(valueClass)) {
062      throw new UnsupportedOperationException(
063          String.format("Feature value class '%s' is boolean valued.", valueClass.getName()));
064    }
065  }
066
067  @Override
068  public boolean isFeatureEnabled(@NonNull T feature) {
069    ensureBooleanValue(feature);
070    return get(feature);
071  }
072
073  @Override
074  public IMutableConfiguration<T> enableFeature(@NonNull T feature) {
075    ensureBooleanValue(feature);
076    featureValues.put(feature, true);
077    return this;
078  }
079
080  @Override
081  public IMutableConfiguration<T> disableFeature(@NonNull T feature) {
082    ensureBooleanValue(feature);
083    featureValues.put(feature, false);
084    return this;
085  }
086
087  @Override
088  public IMutableConfiguration<T> applyConfiguration(@NonNull IConfiguration<T> original) {
089    this.featureValues.putAll(original.getFeatureValues());
090    return this;
091  }
092
093  @Override
094  public IMutableConfiguration<T> set(T feature, Object value) {
095    Class<?> featureValueClass = feature.getValueClass();
096    Class<?> valueClass = value.getClass();
097    if (!featureValueClass.isAssignableFrom(valueClass)) {
098      throw new UnsupportedOperationException(
099          String.format("Provided value of class '%s' is not assignment compatible with feature value class '%s'.",
100              valueClass.getName(),
101              featureValueClass.getName()));
102    }
103    featureValues.put(feature, value);
104    return this;
105  }
106}