001package de.monochromata.anaphors.ast.strategy;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006import java.util.Optional;
007
008import de.monochromata.AbstractStrategy;
009import de.monochromata.anaphors.ast.ASTBasedAnaphora;
010import de.monochromata.anaphors.ast.reference.Referent;
011import de.monochromata.anaphors.ast.reference.strategy.ReferentializationStrategy;
012import de.monochromata.anaphors.ast.relatedexp.RelatedExpression;
013import de.monochromata.anaphors.ast.relatedexp.strategy.RelatedExpressionStrategy;
014import de.monochromata.anaphors.ast.spi.AnaphoraResolutionSpi;
015import de.monochromata.anaphors.ast.spi.AnaphorsSpi;
016import de.monochromata.anaphors.ast.spi.RelatedExpressionsSpi;
017
018/**
019 * An abstract base class for strategies used to for resolve or construct the
020 * referents of anaphors.
021 *
022 * @param <N>  The node type in the AST
023 * @param <E>  The expression type
024 * @param <T>  The type type
025 * @param <B>  The binding type
026 * @param <VB> The variable binding type
027 * @param <FB> The field binding type
028 * @param <MB> The method binding type
029 * @param <TB> The type binding type
030 * @param <S>  The scope type (optional)
031 * @param <I>  The type used to represent identifiers
032 * @param <QI> The type used to represent qualified identifiers
033 * @param <EV> The type of the event contained in the condition that is
034 *             evaluated to check when the perspectivations shall be applied.
035 * @param <PP> The type used for positions that carry perspectivations
036 * @param <R>  The sub-type of related expression to use
037 * @param <A>  The sub-type of AST-based anaphora to use
038 */
039public abstract class AbstractAnaphorResolutionStrategy<N, E, T, B, VB extends B, FB extends B, MB extends B, TB extends B, S, I, QI, EV, PP, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>>
040        extends AbstractStrategy implements AnaphorResolutionStrategy<N, E, T, B, TB, S, I, QI, R, A> {
041
042    protected final List<Class<? extends RelatedExpressionStrategy>> supportedRelatedExpressionStrategies;
043    protected final AnaphorsSpi<N, E, TB, S, I, QI, EV, PP> anaphorsSpi;
044    protected final RelatedExpressionsSpi<N, E, T, B, MB, TB, S, I, QI, EV, PP, R> relatedExpressionsSpi;
045    protected final AnaphoraResolutionSpi<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> anaphoraResolutionSpi;
046
047    /**
048     * Used in contract testing.
049     */
050    @SuppressWarnings("unused")
051    protected AbstractAnaphorResolutionStrategy() {
052        this(null, null, null, null);
053    }
054
055    protected AbstractAnaphorResolutionStrategy(
056            final List<Class<? extends RelatedExpressionStrategy>> supportedRelatedExpressionStrategies,
057            final AnaphorsSpi<N, E, TB, S, I, QI, EV, PP> anaphorsSpi,
058            final RelatedExpressionsSpi<N, E, T, B, MB, TB, S, I, QI, EV, PP, R> relatedExpressionsSpi,
059            final AnaphoraResolutionSpi<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> anaphoraResolutionSpi) {
060        this.supportedRelatedExpressionStrategies = supportedRelatedExpressionStrategies;
061        this.anaphorsSpi = anaphorsSpi;
062        this.relatedExpressionsSpi = relatedExpressionsSpi;
063        this.anaphoraResolutionSpi = anaphoraResolutionSpi;
064    }
065
066    @Override
067    public boolean canRelateTo(
068            final RelatedExpressionStrategy<N, T, B, TB, S, QI, R> potentialRelatedExpressionStrategy) {
069        return supportedRelatedExpressionStrategies.stream()
070                .anyMatch(supportedType -> supportedType.isInstance(potentialRelatedExpressionStrategy));
071    }
072
073    @Override
074    public List<A> generatePotentialAnaphora(final S scope, final String anaphor, final E definiteExpression,
075            final List<R> potentialRelatedExpressions,
076            final List<ReferentializationStrategy<E, TB, S, I, QI>> refStrategies) {
077        final List<A> anaphoras = new ArrayList<>();
078        return getIdForDefiniteExpression(definiteExpression)
079                .map(idFromDefiniteExpression -> generatePotentialAnaphora(scope, anaphor, definiteExpression,
080                        potentialRelatedExpressions, refStrategies, anaphoras, idFromDefiniteExpression))
081                .orElseGet(Collections::emptyList);
082    }
083
084    protected List<A> generatePotentialAnaphora(final S scope, final String anaphor, final E definiteExpression,
085            final List<R> potentialRelatedExpressions,
086            final List<ReferentializationStrategy<E, TB, S, I, QI>> refStrategies, final List<A> anaphoras,
087            final I idFromDefiniteExpression) {
088        // Create anaphora relations for all related expressions
089        // for which at least one referentialization strategy can be applied.
090        for (final R potentialRelatedExpression : potentialRelatedExpressions) {
091            if (canRelateTo(potentialRelatedExpression.getStrategy())) {
092                final List<Referent<TB, S, I, QI>> potentialReferents = createPotentialReferents(scope,
093                        potentialRelatedExpression);
094                for (final Referent<TB, S, I, QI> potentialReferent : potentialReferents) {
095                    for (final ReferentializationStrategy<E, TB, S, I, QI> refStrategy : refStrategies) {
096                        if (refStrategy.canReferTo(idFromDefiniteExpression, potentialReferent, scope)) {
097                            anaphoras.add(createAnaphora(scope, anaphor, definiteExpression, potentialRelatedExpression,
098                                    potentialReferent, refStrategy));
099                            break;
100                        }
101                    }
102                }
103            }
104            // TODO: Assuming that there is no precedence / hiding
105        }
106
107        return anaphoras;
108    }
109
110    protected Optional<I> getIdForDefiniteExpression(final E definiteExpression) {
111        if (anaphorsSpi.isSimpleName(definiteExpression)) {
112            // cannot resolve type again, since that would trigger anaphor
113            // resolution, too
114            // TODO: Maybe use another approach that also checks visibility etc.
115            return Optional.of(anaphorsSpi.getIdentifierOfSimpleName(definiteExpression));
116        } else if (couldBeAPreviousRealization(definiteExpression)) {
117            return Optional.of(getIdForPreviousRealization(definiteExpression));
118        }
119        return Optional.empty();
120    }
121
122    /**
123     * @return {@literal true} if the given definite expression has been or could
124     *         have been created by this or any other
125     *         {@link AnaphorResolutionStrategy}.
126     * @see #getIdForPreviousRealization(Object)
127     */
128    protected boolean couldBeAPreviousRealization(final E definiteExpression) {
129        return anaphoraResolutionSpi.couldBeAPreviousRealization(definiteExpression);
130    }
131
132    /**
133     * Should only be invoked if {@link #couldBeAPreviousRealization(Object)}
134     * returned {@literal true}.
135     *
136     * @see #couldBeAPreviousRealization(Object)
137     */
138    protected I getIdForPreviousRealization(final E definiteExpression) {
139        return anaphoraResolutionSpi.getIdFromPreviousRealization(definiteExpression);
140    }
141
142    protected abstract List<Referent<TB, S, I, QI>> createPotentialReferents(S scope, R potentialRelatedExpression);
143
144    protected void requireThisAnaphorResolutionStrategy(final A anaphora) {
145        if (anaphora.getAnaphorResolutionStrategy() != this) {
146            throw new IllegalArgumentException("The given anaphora relation was not created by this strategy");
147        }
148    }
149}