001package de.monochromata.anaphors.ast.reference.strategy.concept;
002
003import java.util.List;
004import java.util.Optional;
005import java.util.function.Function;
006import java.util.function.Predicate;
007
008import de.monochromata.anaphors.ast.feature.FeatureContainer;
009import de.monochromata.anaphors.ast.reference.Referent;
010import de.monochromata.anaphors.ast.spi.AnaphorsSpi;
011
012/**
013 * Referentialization based on case-insensitive hyponymy (i.e. matching
014 * referents whose type is a - potentially transitive - super-class or a -
015 * potentially transitively - implemented interface of the type of the definite
016 * expression).
017 *
018 * @param <N>  The node type in the AST
019 * @param <E>  The expression type
020 * @param <TB> The type binding type
021 * @param <S>  The scope type (optional)
022 * @param <I>  The type used to represent identifiers
023 * @param <QI> The type used to represent qualified identifiers
024 * @param <EV> The type of the event contained in the condition that is
025 *             evaluated to check when the perspectivations shall be applied.
026 * @param <PP> The type used for positions that carry perspectivations
027 */
028public class Hyponymy<N, E, TB, S, I, QI, EV, PP>
029        extends AbstractConceptReferentializationStrategy<N, E, TB, S, I, QI, EV, PP> {
030
031    public static final String Hy_KIND = "Hy";
032
033    private final Function<TB, Optional<TB>> getSuperClass;
034    private final Function<TB, List<TB>> getImplementedInterfaces;
035    private final Function<I, Function<TB, Boolean>> nameOfIdentifierEqualsSimpleNameOfTypeBinding;
036    private final Function<I, Function<TB, Boolean>> conceptualTypeInIdentifierEqualsSimpleNameOfTypeBinding;
037
038    /**
039     * Used in contract testing.
040     */
041    @SuppressWarnings("unused")
042    protected Hyponymy() {
043        this(null, null, null, null);
044    }
045
046    public Hyponymy(final AnaphorsSpi<N, E, TB, S, I, QI, EV, PP> anaphorsSpi,
047            final Function<TB, Optional<TB>> getSuperClass, final Function<TB, List<TB>> getImplementedInterfaces) {
048        this(getSuperClass, getImplementedInterfaces,
049                id -> type -> anaphorsSpi.nameOfIdentifierEqualsSimpleNameOfTypeBinding(id, type, false),
050                id -> type -> anaphorsSpi.conceptualTypeInIdentifierEqualsSimpleNameOfType(id, type, false));
051    }
052
053    public Hyponymy(final Function<TB, Optional<TB>> getSuperClass,
054            final Function<TB, List<TB>> getImplementedInterfaces,
055            final Function<I, Function<TB, Boolean>> nameOfIdentifierEqualsSimpleNameOfTypeBinding,
056            final Function<I, Function<TB, Boolean>> conceptualTypeInIdentifierEqualsSimpleNameOfTypeBinding) {
057        super(null);
058        this.getSuperClass = getSuperClass;
059        this.getImplementedInterfaces = getImplementedInterfaces;
060        this.nameOfIdentifierEqualsSimpleNameOfTypeBinding = nameOfIdentifierEqualsSimpleNameOfTypeBinding;
061        this.conceptualTypeInIdentifierEqualsSimpleNameOfTypeBinding = conceptualTypeInIdentifierEqualsSimpleNameOfTypeBinding;
062    }
063
064    @Override
065    public boolean canReferTo(final I idFromDefiniteExpression, final Referent<TB, S, I, QI> potentialReferent,
066            final S scope) {
067        return canReferToInternal(potentialReferent, scope,
068                nameOfIdentifierEqualsSimpleNameOfTypeBinding.apply(idFromDefiniteExpression));
069    }
070
071    private boolean canReferToInternal(final Referent<TB, S, I, QI> potentialReferent, final S scope,
072            final Function<TB, Boolean> comparison) {
073        return canReferToInternal(potentialReferent, scope, (Predicate<TB>) type -> comparison.apply(type));
074    }
075
076    private boolean canReferToInternal(final Referent<TB, S, I, QI> potentialReferent, final S scope,
077            final Predicate<TB> comparison) {
078        final var canReferToAnImplementedInterface = canReferToAnImplementedInterface(comparison);
079        final var type = potentialReferent.resolveType(scope);
080        final var optionalSuperClass = getSuperClass.apply(type);
081        return canReferToASuperclass(optionalSuperClass, comparison, canReferToAnImplementedInterface)
082                || canReferToAnImplementedInterface.test(type);
083    }
084
085    private boolean canReferToASuperclass(final Optional<TB> optionalSuperClass,
086            final Predicate<TB> canReferUsingSimpleNameOfTypeBinding,
087            final Predicate<TB> canReferToAnImplementedInterface) {
088        if (optionalSuperClass.isEmpty()) {
089            return false;
090        }
091        final var superClass = optionalSuperClass.get();
092        final var canReferToSuperClass = canReferUsingSimpleNameOfTypeBinding.test(superClass);
093        if (canReferToSuperClass) {
094            return true;
095        } else if (canReferToAnImplementedInterface.test(superClass)) {
096            return true;
097        }
098        // Recurse
099        return canReferToASuperclass(getSuperClass.apply(superClass), canReferUsingSimpleNameOfTypeBinding,
100                canReferToAnImplementedInterface);
101    }
102
103    private Predicate<TB> canReferToAnImplementedInterface(final Predicate<TB> canReferUsingSimpleNameOfTypeBinding) {
104        return type -> {
105            final var implementedInterfaces = getImplementedInterfaces.apply(type);
106            final var canReferToImplementedInterface = implementedInterfaces.stream()
107                    .anyMatch(canReferUsingSimpleNameOfTypeBinding);
108            if (canReferToImplementedInterface) {
109                return true;
110            }
111            // Recurse
112            return implementedInterfaces.stream()
113                    .anyMatch(canReferToAnImplementedInterface(canReferUsingSimpleNameOfTypeBinding));
114        };
115    }
116
117    @Override
118    public boolean canReferToUsingConceptualType(final I idFromDefiniteExpression,
119            final Referent<TB, S, I, QI> potentialReferent, final S scope) {
120        return canReferToInternal(potentialReferent, scope,
121                conceptualTypeInIdentifierEqualsSimpleNameOfTypeBinding.apply(idFromDefiniteExpression));
122    }
123
124    @Override
125    public FeatureContainer<QI> getFeaturesRemainingInIdentifierIfItCanReferUsingConceptualType(
126            final I idFromDefiniteExpression, final Referent<TB, S, I, QI> potentialReferent, final S scope) {
127        throw new UnsupportedOperationException("Not yet implemented");
128    }
129
130    @Override
131    public String getKind() {
132        return Hy_KIND;
133    }
134
135}