001package de.monochromata.anaphors.ast.reference.strategy.concept;
002
003import java.util.function.Predicate;
004
005import de.monochromata.anaphors.ast.feature.FeatureContainer;
006import de.monochromata.anaphors.ast.reference.Referent;
007import de.monochromata.anaphors.ast.spi.AnaphorsSpi;
008
009/**
010 * Referentialization based on case-insensitive faux hyponymy (i.e. matching
011 * referents whose type has a name that is equal to the simple name that acts as
012 * definite expression plus a non-empty prefix).
013 *
014 * <p>
015 * TODO: Reformulate the definition of faux hyponymy in a stricter form that
016 * does not cover features prefixing the conceptual type.
017 * </p>
018 *
019 * @param <N>  The node type in the AST
020 * @param <E>  The expression type
021 * @param <TB> The type binding type
022 * @param <S>  The scope type (optional)
023 * @param <I>  The type used to represent identifiers
024 * @param <QI> The type used to represent qualified identifiers
025 * @param <EV> The type of the event contained in the condition that is
026 *             evaluated to check when the perspectivations shall be applied.
027 * @param <PP> The type used for positions that carry perspectivations
028 */
029public class FauxHyponymy<N, E, TB, S, I, QI, EV, PP>
030        extends AbstractConceptReferentializationStrategy<N, E, TB, S, I, QI, EV, PP> {
031
032    public static final String HyFx_KIND = "HyFx";
033
034    /**
035     * Used in contract testing.
036     */
037    @SuppressWarnings("unused")
038    protected FauxHyponymy() {
039    }
040
041    public FauxHyponymy(final AnaphorsSpi<N, E, TB, S, I, QI, EV, PP> anaphorsSpi) {
042        super(anaphorsSpi);
043    }
044
045    private boolean canReferToInternal(final Referent<TB, S, I, QI> potentialReferent, final S scope,
046            final Predicate<TB> comparison) {
047        final TB typeOfPotentialReferent = potentialReferent.resolveType(scope);
048        // TODO: should cache type binding / use resolved type
049        // TODO: Need to test cases in which multiple types with identical
050        // simple name
051        // are available in the compilation unit
052        return comparison.test(typeOfPotentialReferent);
053    }
054
055    /**
056     * Returns true, if the given definite expression is a simple name, and the
057     * simple name is equal to a true suffix of the simple name of the type of the
058     * referent. (True suffix means that there is a non-empty prefix before the
059     * suffix.)
060     *
061     * @param idFromDefiniteExpression the ID from the definite expression that
062     *                                 shall refer
063     * @param potentialReferent        the potential referent of the definite
064     *                                 expression
065     * @param scope                    the scope in which the definite expression
066     *                                 occurs
067     * @return {@code true}, if the definite expression is a simple name that is
068     *         equal to the simple name of the type of the referent, {@code false}
069     *         otherwise.
070     * @see #isCaseSensitive()
071     * @see #canReferToUsingConceptualType(Object, Referent, Object)
072     */
073    @Override
074    public boolean canReferTo(final I idFromDefiniteExpression, final Referent<TB, S, I, QI> potentialReferent,
075            final S scope) {
076        // TODO: Rename
077        // nameOfIdentifierEqualsFauxHyponymOfSimpleNameOfTypeBinding to
078        // nameOfIdentifierEqualsFauxHyponymOfSimpleNameOfType
079        return canReferToInternal(potentialReferent, scope,
080                typeOfPotentialReferent -> anaphorsSpi.nameOfIdentifierEqualsFauxHyponymOfSimpleNameOfTypeBinding(
081                        idFromDefiniteExpression, typeOfPotentialReferent, isCaseSensitive()));
082    }
083
084    /**
085     * Returns true, if the given definite expression is a simple name, and the
086     * conceptual type expressed in the simple name is equal to a true suffix of the
087     * simple name of the type of the referent. (True suffix means that there is a
088     * non-empty prefix before the suffix.)
089     *
090     * @param idFromDefiniteExpression the ID from the definite expression that
091     *                                 shall refer
092     * @param potentialReferent        the potential referent of the definite
093     *                                 expression
094     * @param scope                    the scope in which the definite expression
095     *                                 occurs
096     * @return {@code true}, if the definite expression is a simple name that is
097     *         equal to the simple name of the type of the referent, {@code false}
098     *         otherwise.
099     * @see #isCaseSensitive()
100     * @see #canReferTo(Object, Referent, Object)
101     */
102    @Override
103    public boolean canReferToUsingConceptualType(final I idFromDefiniteExpression,
104            final Referent<TB, S, I, QI> potentialReferent, final S scope) {
105        return canReferToInternal(potentialReferent, scope,
106                typeOfPotentialReferent -> anaphorsSpi.conceptualTypeInIdentifierEqualsFauxHyponymyOfSimpleNameOfType(
107                        idFromDefiniteExpression, typeOfPotentialReferent, isCaseSensitive()));
108    }
109
110    @Override
111    public FeatureContainer<QI> getFeaturesRemainingInIdentifierIfItCanReferUsingConceptualType(
112            final I idFromDefiniteExpression, final Referent<TB, S, I, QI> potentialReferent, final S scope) {
113        // TODO: This implementation actually performs a number of checks twice
114        // in canReferToUsingConceptualType and
115        // spi.getFeaturesRemainingInIdentifierBesidesConceptualTypeOfReferentName
116        // . It should be replaced by one that implements all checks in
117        // spi.getFeaturesRemainingInIdentifierBesidesConceptualTypeOfReferentName
118        // so that canReferToUsingConceptualType does not need to be invoked.
119        if (canReferToUsingConceptualType(idFromDefiniteExpression, potentialReferent, scope)) {
120            final TB typeOfPotentialReferent = potentialReferent.resolveType(scope);
121            return anaphorsSpi.getFeaturesRemainingInIdentifierBesidesConceptualTypeOfReferentTypeWithFauxHyponymy(
122                    idFromDefiniteExpression, typeOfPotentialReferent, isCaseSensitive());
123        }
124        return null;
125    }
126
127    /**
128     * Whether or not type names are matched in a case-sensitive way.
129     *
130     * @return false
131     */
132    protected boolean isCaseSensitive() {
133        return false;
134    }
135
136    @Override
137    public String getKind() {
138        return HyFx_KIND;
139    }
140}