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

cdc.applic.dictionaries.impl.bindings.DictionariesBindingImpl Maven / Gradle / Ivy

package cdc.applic.dictionaries.impl.bindings;

import java.io.PrintStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import cdc.applic.dictionaries.bindings.AliasAliasBinding;
import cdc.applic.dictionaries.bindings.BindingRole;
import cdc.applic.dictionaries.bindings.DItemsBinding;
import cdc.applic.dictionaries.bindings.DictionariesBinding;
import cdc.applic.dictionaries.bindings.PropertyPropertyBinding;
import cdc.applic.dictionaries.bindings.TypesBinding;
import cdc.applic.dictionaries.impl.AbstractDictionaryImpl;
import cdc.applic.dictionaries.impl.RepositoryImpl;
import cdc.applic.dictionaries.types.Type;
import cdc.applic.expressions.literals.Name;
import cdc.util.debug.ControlledPrintable;
import cdc.util.debug.Verbosity;
import cdc.util.lang.Checks;
import cdc.util.lang.ImplementationException;
import cdc.util.lang.NotFoundException;
import cdc.util.paths.Path;

/**
 * Dictionary/Dictionary binding implementation.
 *
 * @author Damien Carbonne
 */
public class DictionariesBindingImpl implements DictionariesBinding, ControlledPrintable {
    private static final String BINDING = "binding";
    private static final String SOURCE = "source";
    private static final String TARGET = "target";

    private final AbstractDictionaryImpl source;
    private final AbstractDictionaryImpl target;
    final Set typeBindings = new HashSet<>();
    private final Map> typeKeyToTypeBinding = new HashMap<>();
    private final Set itemBindings = new HashSet<>();
    private final Map> nameForward = new HashMap<>();
    private final Map> nameBackward = new HashMap<>();

    private static record SourceTarget(Name source,
                                       Name target) {
    }

    public DictionariesBindingImpl(Builder builder) {
        this.source = Checks.isNotNull(builder.source, SOURCE);
        this.target = Checks.isNotNull(builder.target, TARGET);
        Checks.isTrue(this.source.getRepository() == this.target.getRepository(), "Repository mismatch");
    }

     B addTypesBinding(B binding) {
        Checks.isNotNull(binding, BINDING);

        typeBindings.add(binding);
        final SourceTarget key = new SourceTarget(binding.getSourceType().getName(),
                                                  binding.getTargetType().getName());
        typeKeyToTypeBinding.computeIfAbsent(key, k -> new HashSet<>()).add(binding);
        return binding;
    }

     B addDItemsBinding(B binding) {
        Checks.isNotNull(binding, "binding");

        // Item name may contain prefix
        // So we add bindings twice (even when there is no prefix)
        nameForward.computeIfAbsent(binding.getDItem(BindingRole.SOURCE).getName(),
                                    k -> new HashSet<>())
                   .add(binding);
        nameForward.computeIfAbsent(binding.getDItem(BindingRole.SOURCE).getName().removePrefix(),
                                    k -> new HashSet<>())
                   .add(binding);
        nameBackward.computeIfAbsent(binding.getDItem(BindingRole.TARGET).getName(),
                                     k -> new HashSet<>())
                    .add(binding);
        nameBackward.computeIfAbsent(binding.getDItem(BindingRole.TARGET).getName().removePrefix(),
                                     k -> new HashSet<>())
                    .add(binding);
        itemBindings.add(binding);
        return binding;
    }

    public BooleanBooleanBindingImpl.Builder booleanBooleanBinding() {
        return BooleanBooleanBindingImpl.builder(this);
    }

    public BooleanEnumeratedBindingImpl.Builder booleanEnumeratedBinding() {
        return BooleanEnumeratedBindingImpl.builder(this);
    }

    public BooleanIntegerBindingImpl.Builder booleanIntegerBinding() {
        return BooleanIntegerBindingImpl.builder(this);
    }

    public EnumeratedBooleanBindingImpl.Builder enumeratedBooleanBinding() {
        return EnumeratedBooleanBindingImpl.builder(this);
    }

    public EnumeratedEnumeratedBindingImpl.Builder enumeratedEnumeratedBinding() {
        return EnumeratedEnumeratedBindingImpl.builder(this);
    }

    public EnumeratedIntegerBindingImpl.Builder enumeratedIntegerBinding() {
        return EnumeratedIntegerBindingImpl.builder(this);
    }

    public IntegerBooleanBindingImpl.Builder integerBooleanBinding() {
        return IntegerBooleanBindingImpl.builder(this);
    }

    public IntegerEnumeratedBindingImpl.Builder integerEnumeratedBinding() {
        return IntegerEnumeratedBindingImpl.builder(this);
    }

    public IntegerIntegerBindingImpl.Builder integerIntegerBinding() {
        return IntegerIntegerBindingImpl.builder(this);
    }

    public PatternPatternBindingImpl.Builder patternPatternBinding() {
        return PatternPatternBindingImpl.builder(this);
    }

    public RealRealBindingImpl.Builder realRealBinding() {
        return RealRealBindingImpl.builder(this);
    }

    public AliasAliasBindingImpl.Builder aliasAliasBinding() {
        return AliasAliasBindingImpl.builder(this);
    }

    public PropertyPropertyBindingImpl.Builder propertyPropertyBinding() {
        return PropertyPropertyBindingImpl.builder(this);
    }

    @Override
    public AbstractDictionaryImpl getDictionary(BindingRole role) {
        Checks.isNotNull(role, "role");
        return role == BindingRole.SOURCE ? source : target;
    }

    @Override
    public Set getTypesBindings() {
        return typeBindings;
    }

    @Override
    public Set getTypesBindings(Type sourceType,
                                              Type targetType) {
        final SourceTarget key = new SourceTarget(sourceType.getName(), targetType.getName());
        return typeKeyToTypeBinding.getOrDefault(key, Collections.emptySet());
    }

    @Override
    public Set getDItemBindings() {
        return itemBindings;
    }

    @Override
    public Set getDItemBindings(Name name,
                                               BindingRole role) {
        Checks.isNotNull(name, "name");
        Checks.isNotNull(role, "role");

        return role == BindingRole.SOURCE
                ? nameForward.getOrDefault(name, Collections.emptySet())
                : nameBackward.getOrDefault(name, Collections.emptySet());
    }

    @Override
    public AliasAliasBinding getAliasAliasBinding(Name name,
                                                  BindingRole role) {
        final Set set = getDItemBindings(name, role);
        if (set.isEmpty()) {
            throw new NotFoundException(name + " has no associated bindings");
        } else if (set.size() == 1) {
            final DItemsBinding binding = set.iterator().next();
            if (binding instanceof final AliasAliasBinding aab) {
                return aab;
            } else {
                throw new NotFoundException(name + " is not an Alias name");
            }
        } else {
            if (getDictionary(role).hasAlias(name)) {
                throw new ImplementationException(name + " has several associated bindings.");
            } else {
                throw new NotFoundException(name + " is not an Alias name");
            }
        }
    }

    @Override
    public Set getPropertyPropertyBindings(Name name,
                                                                    BindingRole role) {
        final Set set = getDItemBindings(name, role);

        final Set result = new HashSet<>();
        for (final DItemsBinding binding : set) {
            if (binding instanceof final PropertyPropertyBinding x) {
                result.add(x);
            } else {
                throw new NotFoundException(name + " is not associated to PropertyBinding");
            }
        }
        return result;
    }

    @Override
    public void print(PrintStream out,
                      int level,
                      Verbosity verbosity) {
        indent(out, level);
        out.println("Binding " + getDictionary(BindingRole.TARGET).getName() + " <<< " +
                getDictionary(BindingRole.SOURCE).getName());

        if (!getTypesBindings().isEmpty()) {
            indent(out, level + 1);
            out.println("Types " + getTypesBindings().size());
            if (verbosity != Verbosity.ESSENTIAL) {
                for (final TypesBinding binding : getTypesBindings()) {
                    ((AbstractTypesBinding) binding).print(out, level + 2);
                }
            }
        }

        if (!getDItemBindings().isEmpty()) {
            indent(out, level + 1);
            out.println("Items " + getDItemBindings().size());
            if (verbosity != Verbosity.ESSENTIAL) {
                for (final DItemsBinding binding : getDItemBindings()) {
                    ((AbstractDItemsBinding) binding).print(out, level + 2);
                }
            }
        }
    }

    // Do not use
    public static Builder builder(RepositoryImpl repository) {
        return new Builder(repository);
    }

    public static class Builder {
        private final RepositoryImpl repository;
        private AbstractDictionaryImpl source;
        private AbstractDictionaryImpl target;

        protected Builder(RepositoryImpl repository) {
            this.repository = Checks.isNotNull(repository, "repository");
        }

        public Builder source(AbstractDictionaryImpl source) {
            Checks.isNotNull(source, SOURCE);
            this.source = source;
            return this;
        }

        public Builder source(Path source) {
            Checks.isNotNull(source, SOURCE);
            return source(repository.getDictionary(source));
        }

        public Builder source(String source) {
            Checks.isNotNull(source, SOURCE);
            return source(Path.of(source));
        }

        public Builder target(AbstractDictionaryImpl target) {
            Checks.isNotNull(target, TARGET);
            this.target = target;
            return this;
        }

        public Builder target(Path target) {
            Checks.isNotNull(target, TARGET);
            return target(repository.getDictionary(target));
        }

        public Builder target(String target) {
            Checks.isNotNull(target, TARGET);
            return target(Path.of(target));
        }

        public DictionariesBindingImpl build() {
            return repository.addBinding(new DictionariesBindingImpl(this));
        }

        public RepositoryImpl back() {
            build();
            return repository;
        }
    }
}