001package de.monochromata.anaphors.ast.strategy; 002 003import static de.monochromata.anaphors.ast.relatedexp.strategy.MethodInvocationStrategy.MI_KIND; 004import static java.util.Arrays.asList; 005import static java.util.Collections.emptyList; 006import static java.util.Collections.singletonList; 007 008import java.util.List; 009import java.util.Optional; 010import java.util.function.Supplier; 011 012import de.monochromata.anaphors.ast.ASTBasedAnaphora; 013import de.monochromata.anaphors.ast.AnaphorPart; 014import de.monochromata.anaphors.ast.DefaultAnaphorPart; 015import de.monochromata.anaphors.ast.DefaultRelatedExpressionPart; 016import de.monochromata.anaphors.ast.KindComposition; 017import de.monochromata.anaphors.ast.RelatedExpressionPart; 018import de.monochromata.anaphors.ast.reference.AbstractReferent; 019import de.monochromata.anaphors.ast.reference.Referent; 020import de.monochromata.anaphors.ast.reference.strategy.ReferentializationStrategy; 021import de.monochromata.anaphors.ast.relatedexp.RelatedExpression; 022import de.monochromata.anaphors.ast.relatedexp.strategy.MethodInvocationStrategy; 023import de.monochromata.anaphors.ast.relatedexp.strategy.RelatedExpressionStrategy; 024import de.monochromata.anaphors.ast.spi.AnaphoraResolutionSpi; 025import de.monochromata.anaphors.ast.spi.AnaphorsSpi; 026import de.monochromata.anaphors.ast.spi.RelatedExpressionsSpi; 027import de.monochromata.anaphors.perspectivation.Perspectivation; 028 029/** 030 * An indirect-anaphora resolution strategy that anchors anaphors in the return 031 * value of a method invocation that acts as the anchor. 032 * 033 * @param <N> The node type in the AST 034 * @param <E> The expression type 035 * @param <T> The type type 036 * @param <B> The binding type 037 * @param <VB> The variable binding type 038 * @param <FB> The field binding type 039 * @param <MB> The method binding type 040 * @param <TB> The type binding type 041 * @param <S> The scope type (optional) 042 * @param <I> The type used to represent identifiers 043 * @param <QI> The type used to represent qualified identifiers 044 * @param <EV> The type of the event contained in the condition that is 045 * evaluated to check when the perspectivations shall be applied. 046 * @param <PP> The type used for positions that carry perspectivations 047 * @param <R> The sub-type of related expression to use 048 * @param <A> The sub-type of AST-based anaphora to use 049 */ 050public class IA1MrStrategy<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>> 051 extends AbstractAnchoringStrategy<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A> 052 implements StoresReferentInLocalTempVariable<N, E, T, B, TB, S, I, QI, R, A> { 053 054 public static final List<Class<? extends RelatedExpressionStrategy>> IA1Mr_SUPPORTED_RELATED_EXPRESSION_STRATEGIES = asList( 055 MethodInvocationStrategy.class); 056 057 /** 058 * Used in contract testing. 059 */ 060 @SuppressWarnings("unused") 061 protected IA1MrStrategy() { 062 } 063 064 public IA1MrStrategy(final AnaphorsSpi<N, E, TB, S, I, QI, EV, PP> anaphorsSpi, 065 final RelatedExpressionsSpi<N, E, T, B, MB, TB, S, I, QI, EV, PP, R> relatedExpressionsSpi, 066 final AnaphoraResolutionSpi<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> anaphoraResolutionSpi) { 067 super(IA1Mr_SUPPORTED_RELATED_EXPRESSION_STRATEGIES, anaphorsSpi, relatedExpressionsSpi, anaphoraResolutionSpi); 068 } 069 070 @Override 071 protected List<Referent<TB, S, I, QI>> createPotentialReferents(final S scope, final R potentialRelatedExpression) { 072 if (!MI_KIND.equals(potentialRelatedExpression.getStrategy().getKind())) { 073 throw new IllegalArgumentException( 074 "Cannot apply " + getKind() + " to " + potentialRelatedExpression.getStrategy().getKind()); 075 } 076 // TODO: Maybe use existing method binding, if it has been resolved previously 077 final MB methodBinding = relatedExpressionsSpi 078 .resolveMethodInvocationBinding(potentialRelatedExpression.getRelatedExpression(), scope); 079 if (methodBinding == null) { 080 return emptyList(); 081 } 082 // If the return type cannot be resolved, later processing will fail. 083 if (null == potentialRelatedExpression.resolveType(scope)) { 084 return emptyList(); 085 } 086 return createPotentialReferents(potentialRelatedExpression, scope, methodBinding); 087 } 088 089 protected List<Referent<TB, S, I, QI>> createPotentialReferents(final R relatedExpression, final S scope, 090 final MB methodBinding) { 091 final I methodName = anaphoraResolutionSpi.getMethodName(methodBinding); 092 final String methodDescription = anaphoraResolutionSpi.getMethodDescription(methodBinding); 093 return singletonList(new IA1MrReferent<>(relatedExpression, methodBinding, methodName, methodDescription, scope, 094 relatedExpressionsSpi, anaphoraResolutionSpi)); 095 } 096 097 @Override 098 public Referent<TB, S, I, QI> createReferent(final S scope, final R relatedExpression, final Object memento) { 099 final MB methodBinding = relatedExpressionsSpi 100 .resolveMethodInvocationBinding(relatedExpression.getRelatedExpression(), scope); 101 return createReferent(relatedExpression, scope, methodBinding, memento); 102 } 103 104 protected Referent<TB, S, I, QI> createReferent(final R relatedExpression, final S scope, final MB methodBinding, 105 final Object memento) { 106 final I methodName = anaphoraResolutionSpi.getMethodName(methodBinding); 107 final String methodDescription = anaphoraResolutionSpi.getMethodDescription(methodBinding); 108 if (methodName.equals(memento)) { 109 return new IA1MrReferent<>(relatedExpression, methodBinding, methodName, methodDescription, scope, 110 relatedExpressionsSpi, anaphoraResolutionSpi); 111 } 112 throw new IllegalArgumentException("Failed to re-create referent"); 113 } 114 115 @Override 116 public A createAnaphora(final S scope, final String anaphor, final E anaphorExpression, 117 final R potentialRelatedExpression, final Referent<TB, S, I, QI> potentialReferent, 118 final ReferentializationStrategy<E, TB, S, I, QI> refStrategy) { 119 final DefaultRelatedExpressionPart<N, E, T, B, VB, TB, S, I, QI, R> relatedExpressionPart = new DefaultRelatedExpressionPart<>( 120 potentialRelatedExpression); 121 final DefaultAnaphorPart<N, E, T, B, VB, TB, S, I, QI, R, A> anaphorPart = new DefaultAnaphorPart<>(anaphor, 122 anaphorExpression, potentialReferent, this, refStrategy, 123 ((IA1MrReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) potentialReferent) 124 .getMethodBinding()); 125 final String underspecifiedRelation = anaphoraResolutionSpi.getIA1MrUnderspecifiedRelation( 126 potentialRelatedExpression, 127 ((IA1MrReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) potentialReferent) 128 .getMethodBinding(), 129 scope); 130 final Supplier<String> descriptionSupplier = getShortenedReferenceDescription(potentialRelatedExpression, 131 potentialReferent, scope, refStrategy); 132 return anaphoraResolutionSpi.createIndirectAnaphora(relatedExpressionPart, anaphorPart, underspecifiedRelation, 133 descriptionSupplier, false); 134 } 135 136 protected Supplier<String> getShortenedReferenceDescription(final R relatedExpression, 137 final Referent<TB, S, I, QI> referent, final S scope, 138 final ReferentializationStrategy<E, TB, S, I, QI> refStrategy) { 139 return () -> { 140 final String returnType = relatedExpressionsSpi 141 .identifierToString(relatedExpressionsSpi.getIdentifier(referent.resolveType(scope))); 142 final String kind = KindComposition.getKind(relatedExpression.getStrategy(), this, refStrategy); 143 return relatedExpression.getRelatedExpression() + ":" + returnType + " at " + relatedExpression.getLine() 144 + ":" + relatedExpression.getColumn() + " (" + kind + ")"; 145 }; 146 } 147 148 @Override 149 public E realize(final RelatedExpressionPart<N, E, T, B, TB, S, I, QI, R> relatedExpressionPart, 150 final AnaphorPart<N, E, T, B, TB, S, I, QI, R, A> anaphorPart, final E replacee, 151 final Optional<I> guessedTempName, final Object... support) { 152 return anaphoraResolutionSpi.realizeIA1Mr(relatedExpressionPart, anaphorPart, replacee, guessedTempName, 153 support); 154 } 155 156 @Override 157 public String getKind() { 158 return "IA1Mr"; 159 } 160 161 @Override 162 public List<Perspectivation> underspecifyAnaphor(final R relatedExpression, final String anaphor, 163 final E anaphorExpression, final Referent<TB, S, I, QI> referent, final S scope) { 164 // No perspectivation necessary 165 return emptyList(); 166 } 167 168 @Override 169 public String getAnaphorToBeRealized(final RelatedExpressionPart<N, E, T, B, TB, S, I, QI, R> relatedExpressionPart, 170 final List<AnaphorPart<N, E, T, B, TB, S, I, QI, R, A>> allAnaphorPartsRelatedToTheRelatedExpression, 171 final AnaphorPart<N, E, T, B, TB, S, I, QI, R, A> anaphorPart, final S scope) { 172 return AnaphorCreationForReferentInLocalTempVariable.getAnaphorToBeRealized(relatedExpressionPart, 173 allAnaphorPartsRelatedToTheRelatedExpression, anaphorPart, scope, relatedExpressionsSpi); 174 } 175 176 public static class IA1MrReferent<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>> 177 extends AbstractReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> { 178 179 final MB methodBinding; 180 final I methodName; 181 final S scope; 182 183 /** 184 * Used in contract testing. 185 */ 186 @SuppressWarnings("unused") 187 protected IA1MrReferent() { 188 methodBinding = null; 189 methodName = null; 190 scope = null; 191 } 192 193 IA1MrReferent(final R relatedExpression, final MB methodBinding, final I methodName, 194 final String methodDescription, final S scope, 195 final RelatedExpressionsSpi<N, E, T, B, MB, TB, S, I, QI, EV, PP, R> relatedExpressionsSpi, 196 final AnaphoraResolutionSpi<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> anaphorResolutionSpi) { 197 super(relatedExpression, methodDescription, relatedExpressionsSpi, anaphorResolutionSpi); 198 this.methodBinding = methodBinding; 199 this.methodName = methodName; 200 this.scope = scope; 201 // TODO: add features for the method name, parameters and return 202 // types? 203 } 204 205 public MB getMethodBinding() { 206 return this.methodBinding; 207 } 208 209 @Override 210 public boolean canBeUsedInsteadOf(final Referent<TB, S, I, QI> other) { 211 return false; 212 } 213 214 @Override 215 public boolean hasName() { 216 return true; 217 } 218 219 @Override 220 public QI getName() { 221 return anaphorResolutionSpi.getReferentName(this.methodBinding); 222 } 223 224 @Override 225 public boolean hasMethodName() { 226 return true; 227 } 228 229 @Override 230 public I getMethodName() { 231 return methodName; 232 } 233 234 @Override 235 public TB resolveType(final S scope) { 236 return anaphorResolutionSpi.resolveReturnType(this.methodBinding, scope); 237 } 238 239 @Override 240 public Object getMemento() { 241 return methodName; 242 } 243 244 @Override 245 public int hashCode() { 246 final int prime = 31; 247 int result = super.hashCode(); 248 result = prime * result + ((methodBinding == null) ? 0 : methodBinding.hashCode()); 249 return result; 250 } 251 252 @Override 253 public boolean equals(final Object obj) { 254 if (this == obj) { 255 return true; 256 } 257 if (!super.equals(obj)) { 258 return false; 259 } 260 if (getClass() != obj.getClass()) { 261 return false; 262 } 263 final IA1MrReferent other = (IA1MrReferent) obj; 264 if (methodBinding == null) { 265 if (other.methodBinding != null) { 266 return false; 267 } 268 } else if (!methodBinding.equals(other.methodBinding)) { 269 return false; 270 } 271 return true; 272 } 273 274 @Override 275 public String toString() { 276 // The method binding is not output because it can be quite long. 277 return "IA1MrReferent [methodName=" + methodName + ", getDescription()=" + getDescription() 278 + ", getFeatures()=" + getFeatures() + "]"; 279 } 280 } 281 282}