001package de.monochromata.anaphors.ast; 002 003import static de.monochromata.anaphors.ast.unify.DirectAnaphoraPrecedesIndirectAnaphora.preferDirectOverIndirectAnaphora; 004import static de.monochromata.anaphors.ast.unify.HyponymyPrecedesFauxHyponymy.preferHynonymyOverFauxHyponymy; 005import static de.monochromata.anaphors.ast.unify.TypeRecurrencePrecedesHyponymy.preferTypeRecurrenceOverHynonymy; 006import static java.util.Collections.singletonList; 007 008import java.util.ArrayList; 009import java.util.List; 010import java.util.function.BiPredicate; 011import java.util.function.Supplier; 012 013import de.monochromata.anaphors.ast.reference.strategy.ReferentializationStrategy; 014import de.monochromata.anaphors.ast.reference.strategy.concept.NameRecurrence; 015import de.monochromata.anaphors.ast.relatedexp.RelatedExpression; 016import de.monochromata.anaphors.ast.relatedexp.RelatedExpressionsCollector; 017import de.monochromata.anaphors.ast.relatedexp.strategy.LocalTempVariableIntroducingStrategy; 018import de.monochromata.anaphors.ast.relatedexp.strategy.RelatedExpressionStrategy; 019import de.monochromata.anaphors.ast.strategy.AnaphorResolutionStrategy; 020 021/** 022 * A generic interface to anaphora resolution. Note that invocations of the 023 * interface need to be aware of a specific AST implementation to correctly 024 * invoke the interface. 025 * 026 * @param <N> The node type in the AST 027 * @param <E> The expression type 028 * @param <T> The type type 029 * @param <B> The binding type 030 * @param <TB> The type binding type 031 * @param <S> The scope type (optional) 032 * @param <I> The type used to represent identifiers 033 * @param <QI> The type used to represent qualified identifiers 034 * @param <R> The sub-type of related expression to use 035 * @param <A> The sub-type of AST-based anaphora to use 036 */ 037public class ASTBasedAnaphorResolution<N, E, T, B, TB extends B, S, I, QI, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>> { 038 039 private final List<RelatedExpressionStrategy<N, T, B, TB, S, QI, R>> relatedExpressionStrategies; 040 private final List<AnaphorResolutionStrategy<N, E, T, B, TB, S, I, QI, R, A>> resolutionStrategies; 041 private final List<ReferentializationStrategy<E, TB, S, I, QI>> referentializationStrategies; 042 043 private final Supplier<RelatedExpressionsCollector<N, E, T, B, TB, S, QI, R>> relatedExpressionsCollectorSupplier; 044 045 /** 046 * Used in contract testing. 047 */ 048 @SuppressWarnings("unused") 049 protected ASTBasedAnaphorResolution() { 050 relatedExpressionStrategies = null; 051 resolutionStrategies = null; 052 referentializationStrategies = null; 053 relatedExpressionsCollectorSupplier = null; 054 } 055 056 public ASTBasedAnaphorResolution( 057 final List<RelatedExpressionStrategy<N, T, B, TB, S, QI, R>> relatedExpressionStrategies, 058 final List<AnaphorResolutionStrategy<N, E, T, B, TB, S, I, QI, R, A>> resolutionStrategies, 059 final List<ReferentializationStrategy<E, TB, S, I, QI>> referentializationStrategies, 060 final Supplier<RelatedExpressionsCollector<N, E, T, B, TB, S, QI, R>> relatedExpressionsCollectorSupplier) { 061 this.relatedExpressionStrategies = relatedExpressionStrategies; 062 this.resolutionStrategies = resolutionStrategies; 063 this.referentializationStrategies = validate(referentializationStrategies); 064 this.relatedExpressionsCollectorSupplier = relatedExpressionsCollectorSupplier; 065 } 066 067 protected List<ReferentializationStrategy<E, TB, S, I, QI>> validate( 068 final List<ReferentializationStrategy<E, TB, S, I, QI>> referentializationStrategies) { 069 if (referentializationStrategies.isEmpty()) { 070 throw new IllegalArgumentException("No referentialization strategies provided. At least " 071 + NameRecurrence.class.getSimpleName() + " needs to be provided because " 072 + LocalTempVariableIntroducingStrategy.class.getSimpleName() 073 + " instances use it for re-resolution."); 074 } else if (!(referentializationStrategies.get(0) instanceof NameRecurrence)) { 075 throw new IllegalArgumentException("No referentialization strategies provided. At least " 076 + NameRecurrence.class.getSimpleName() + " needs to be provided because " 077 + LocalTempVariableIntroducingStrategy.class.getSimpleName() 078 + " instances use it for re-resolution."); 079 } 080 return referentializationStrategies; 081 } 082 083 /** 084 * Perform anaphora resolution on the AST implementation configured via the 085 * service provider interfaces. 086 * 087 * @param anaphor The anaphor that is to be (re-)resolved. 088 * @param definiteExpression The expression that may function as anaphor in the 089 * anaphora relation to be generated by this method. 090 * If the anaphora relation is to be re-resolved, this 091 * can be a non-trivial expression. If the anaphora 092 * relation is to be resolved for the first time, this 093 * is typically a simple name and might as well be 094 * called a definite expression at this point. 095 * @param scope May be null if not required by the AST 096 * implementation configured via the service provider 097 * interfaces. 098 * @return A list of anaphors that the given definite expression can function 099 * as. The list is empty is the anaphor could not be resolved and will 100 * contain more than one element if the anaphor is ambiguous. 101 */ 102 public List<A> resolveAnaphor(final String anaphor, final E definiteExpression, final S scope) { 103 104 // Get potential related expressions 105 final var reCollector = relatedExpressionsCollectorSupplier.get(); 106 final List<R> potentialRelatedExpressions = reCollector.traverse(definiteExpression, scope); 107 108 return resolveAnaphor(potentialRelatedExpressions, anaphor, definiteExpression, scope); 109 } 110 111 public List<A> resolveAnaphor(final R potentialRelatedExpression, final String anaphor, final E definiteExpression, 112 final S scope) { 113 return resolveAnaphor(singletonList(potentialRelatedExpression), anaphor, definiteExpression, scope); 114 } 115 116 public List<A> resolveAnaphor(final List<R> potentialRelatedExpressions, final String anaphor, 117 final E definiteExpression, final S scope) { 118 // Unify co-referential related expressions 119 final List<R> unifiedPotentialRelatedExpressions = unifyCoReferentialRelatedExpressions( 120 potentialRelatedExpressions); 121 122 // Get potential anaphora relations 123 List<A> potentialAnaphoraRelations = new ArrayList<>(); 124 for (final AnaphorResolutionStrategy<N, E, T, B, TB, S, I, QI, R, A> resolutionStrat : this.resolutionStrategies) { 125 potentialAnaphoraRelations.addAll(resolutionStrat.generatePotentialAnaphora(scope, anaphor, 126 definiteExpression, unifiedPotentialRelatedExpressions, this.referentializationStrategies)); 127 } 128 129 // Unify co-referential referents 130 potentialAnaphoraRelations = unifyCoReferentialReferents(potentialAnaphoraRelations); 131 132 // Apply precedence rule 133 potentialAnaphoraRelations = preferTypeRecurrenceOverHynonymy(potentialAnaphoraRelations); 134 potentialAnaphoraRelations = preferHynonymyOverFauxHyponymy(potentialAnaphoraRelations); 135 potentialAnaphoraRelations = preferDirectOverIndirectAnaphora(potentialAnaphoraRelations); 136 137 return potentialAnaphoraRelations; 138 } 139 140 /** 141 * @return a potential {@link RelatedExpression} representing the given node, or 142 * {@literal null}, if the node cannot function as related expression. 143 */ 144 public R getRelatedExpression(final N node) { 145 throw new UnsupportedOperationException("Creation of related expressions should be moved to d.m.e.a entirely"); 146 /* 147 * for (final RelatedExpressionStrategy<N, T, B, TB, S, QI, R> strategy : 148 * relatedExpressionStrategies) { if 149 * (strategy.isPotentialRelatedExpression(node)) { return 150 * strategy.createRelatedExpression(node); } } return null; 151 */ 152 } 153 154 protected List<R> unifyCoReferentialRelatedExpressions(final List<R> potentialRelatedExpressions) { 155 return unifyListElements(potentialRelatedExpressions, (re1, re2) -> re1.canBeUsedInsteadOf(re2)); 156 } 157 158 protected List<A> unifyCoReferentialReferents(final List<A> potentialAnaphoraRelations) { 159 return unifyListElements(potentialAnaphoraRelations, 160 (anaphora1, anaphora2) -> anaphora1.getReferent().canBeUsedInsteadOf(anaphora2.getReferent())); 161 } 162 163 protected static <T> List<T> unifyListElements(final List<T> elements, final BiPredicate<T, T> check) { 164 final List<T> retainedElements = new ArrayList<>(); 165 final List<T> omittedElements = new ArrayList<>(); 166 for (final T toCheck : elements) { 167 if (!omittedElements.contains(toCheck)) { 168 boolean retainedInsteadOfAnotherOne = false; 169 for (final T other : elements) { 170 if (toCheck != other && check.test(toCheck, other)) { 171 if (!retainedInsteadOfAnotherOne) { 172 retainedElements.add(toCheck); 173 retainedInsteadOfAnotherOne = true; 174 } 175 System.err.println("Omitting " + other + " in favour of " + toCheck); 176 omittedElements.add(other); 177 retainedElements.remove(other); 178 } 179 } 180 if (!retainedInsteadOfAnotherOne) { 181 retainedElements.add(toCheck); 182 } 183 } 184 } 185 return retainedElements; 186 } 187}