All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.dakusui.floorplan.component.Configurator Maven / Gradle / Ivy

package com.github.dakusui.floorplan.component;

import com.github.dakusui.floorplan.exception.Exceptions;
import com.github.dakusui.floorplan.policy.Policy;
import com.github.dakusui.floorplan.resolver.Resolver;
import com.github.dakusui.floorplan.utils.FloorPlanUtils;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

import static com.github.dakusui.floorplan.utils.Checks.require;
import static com.github.dakusui.floorplan.utils.Checks.requireNonNull;
import static java.util.stream.Collectors.toMap;

public interface Configurator extends AttributeBundle {
  Configurator configure(A attr, Resolver resolver);

  Configurator addOperatorFactory(Operator.Factory operator);

  Component build(Policy policy, Map> pool);

  /**
   * Returns a resolver for a specified attribute {@code attr} set to this object
   * itself. If it is not present, an empty {@code Optional} will be returned.
   *
   * @param attr An attribute for which a resolver is searched.
   * @param   Type of a value of an attribute {@code attr}.
   * @return An optional of a resolver for the given attribute {@code attr}.
   */
   Optional> resolverFor(A attr);

  /**
   * Returns a resolver for a specified attribute {@code attr}. If no resolver is
   * set to this object, this method returns tries to find a resolver from a
   * given {@code policy} object.
   *
   * @param attr   An attribute for which a resolver will be returned.
   * @param policy A policy object from which a resolver is searched.
   * @param     A type of attribute value.
   * @return A resolver for the given attribute {@code attr}.
   */
  default  Resolver resolverFor(A attr, Policy policy) {
    require(
        attr,
        (A a) -> a.spec().getClass().isAssignableFrom(this.spec().getClass()),
        n -> Exceptions.inconsistentSpec(() ->
            String.format("An attribute '%s' is not compatible with '%s'", n.name(), this.spec())
        ));
    return this.resolverFor(attr).orElseGet(() -> policy.fallbackResolverFor(this.ref(), attr));
  }

  class Impl implements Configurator {
    private final ComponentSpec                        spec;
    private final Map>                  resolvers = new LinkedHashMap<>();
    private final Ref                                     ref;
    private final Map> operatorFactories;

    Impl(ComponentSpec spec, String id) {
      this.spec = spec;
      this.ref = Ref.ref(this.spec, id);
      this.operatorFactories = this.spec.operatorFactories();

    }

    @Override
    public Ref ref() {
      return this.ref;
    }

    @Override
    public ComponentSpec spec() {
      return this.spec;
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Optional> resolverFor(A attr) {
      return resolvers.containsKey(attr) ?
          Optional.of((Resolver) resolvers.get(attr)) :
          Optional.empty();
    }

    @Override
    public Configurator configure(A attr, Resolver resolver) {
      this.resolvers.put(
          attr,
          resolver
      );
      return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Configurator addOperatorFactory(Operator.Factory operatorFactory) {
      this.operatorFactories.put(requireNonNull(operatorFactory.type()), requireNonNull(operatorFactory));
      return this;
    }

    @Override
    public Component build(Policy policy, Map> pool) {
      return new Component.Impl<>(
          this.ref,
          new LinkedHashMap() {{
            spec.attributes().forEach(
                (A attr) -> {
                  Object u;
                  put(attr,
                      require(
                          u = FloorPlanUtils.resolve(attr, Impl.this, policy),
                          attr::test,
                          Exceptions.typeMismatch(attr, u)
                      ));
                });
          }},
          operatorFactories.entrySet().stream().map(this::convertEntry
          ).collect(toMap(Map.Entry::getKey, Map.Entry::getValue)),
          pool
      );
    }

    Map.Entry> convertEntry(Map.Entry> inEntry) {
      return new Map.Entry>() {
        Operator value = inEntry.getValue().apply(spec());

        @Override
        public Operator.Type getKey() {
          return inEntry.getKey();
        }

        @Override
        public Operator getValue() {
          return value;
        }

        @Override
        public Operator setValue(Operator value) {
          this.value = value;
          return value;
        }
      };
    }


    @Override
    public String toString() {
      return String.format("configurator(%s)", this.ref);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy