001package de.monochromata.anaphors.ast.strategy; 002 003import static java.util.Arrays.asList; 004 005import java.util.ArrayList; 006import java.util.List; 007import java.util.Optional; 008 009import de.monochromata.anaphors.ast.ASTBasedAnaphora; 010import de.monochromata.anaphors.ast.AnaphorPart; 011import de.monochromata.anaphors.ast.DefaultAnaphorPart; 012import de.monochromata.anaphors.ast.DefaultRelatedExpressionPart; 013import de.monochromata.anaphors.ast.RelatedExpressionPart; 014import de.monochromata.anaphors.ast.reference.AbstractReferent; 015import de.monochromata.anaphors.ast.reference.Referent; 016import de.monochromata.anaphors.ast.reference.strategy.ReferentializationStrategy; 017import de.monochromata.anaphors.ast.relatedexp.RelatedExpression; 018import de.monochromata.anaphors.ast.relatedexp.strategy.ClassInstanceCreationStrategy; 019import de.monochromata.anaphors.ast.relatedexp.strategy.LocalVariableDeclarationStrategy; 020import de.monochromata.anaphors.ast.relatedexp.strategy.ParameterDeclarationStrategy; 021import de.monochromata.anaphors.ast.relatedexp.strategy.RelatedExpressionStrategy; 022import de.monochromata.anaphors.ast.spi.AnaphoraResolutionSpi; 023import de.monochromata.anaphors.ast.spi.AnaphorsSpi; 024import de.monochromata.anaphors.ast.spi.RelatedExpressionsSpi; 025import de.monochromata.anaphors.ast.strategy.IA2FStrategy.IA2FReferent; 026import de.monochromata.anaphors.perspectivation.Perspectivation; 027import de.monochromata.anaphors.perspectivation.Perspectivation.Hide; 028import de.monochromata.anaphors.perspectivation.Perspectivation.ToLowerCase; 029 030/** 031 * An indirect-anaphora resolution strategy that anchors anaphors in a getter 032 * method declared by the anchor. 033 * 034 * @param <N> The node type in the AST 035 * @param <E> The expression type 036 * @param <T> The type type 037 * @param <B> The binding type 038 * @param <VB> The variable binding type 039 * @param <FB> The field binding type 040 * @param <MB> The method binding type 041 * @param <TB> The type binding type 042 * @param <S> The scope type (optional) 043 * @param <I> The type used to represent identifiers 044 * @param <QI> The type used to represent qualified identifiers 045 * @param <EV> The type of the event contained in the condition that is 046 * evaluated to check when the perspectivations shall be applied. 047 * @param <PP> The type used for positions that carry perspectivations 048 * @param <R> The sub-type of related expression to use 049 * @param <A> The sub-type of AST-based anaphora to use 050 */ 051public class IA2MgStrategy<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>> 052 extends AbstractAnchoringStrategy<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A> 053 implements StoresAnchorInLocalTempVariable<N, E, T, B, TB, S, I, QI, R, A> { 054 055 public static final List<Class<? extends RelatedExpressionStrategy>> IA2Mg_SUPPORTED_RELATED_EXPRESSION_STRATEGIES = asList( 056 ClassInstanceCreationStrategy.class, ParameterDeclarationStrategy.class, 057 LocalVariableDeclarationStrategy.class); 058 059 /** 060 * Used in contract testing. 061 */ 062 @SuppressWarnings("unused") 063 protected IA2MgStrategy() { 064 } 065 066 public IA2MgStrategy(final AnaphorsSpi<N, E, TB, S, I, QI, EV, PP> anaphorsSpi, 067 final RelatedExpressionsSpi<N, E, T, B, MB, TB, S, I, QI, EV, PP, R> relatedExpressionsSpi, 068 final AnaphoraResolutionSpi<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> anaphoraResolutionSpi) { 069 super(IA2Mg_SUPPORTED_RELATED_EXPRESSION_STRATEGIES, anaphorsSpi, relatedExpressionsSpi, anaphoraResolutionSpi); 070 } 071 072 @Override 073 protected List<Referent<TB, S, I, QI>> createPotentialReferents(final S scope, final R potentialRelatedExpression) { 074 // TODO: Maybe use existing type binding, if it has been resolved 075 // previously 076 return createPotentialReferents(potentialRelatedExpression, scope, 077 potentialRelatedExpression.resolveType(scope)); 078 } 079 080 protected List<Referent<TB, S, I, QI>> createPotentialReferents(final R relatedExpression, final S scope, 081 final TB typeBinding) { 082 final List<Referent<TB, S, I, QI>> potentialReferents = new ArrayList<>(); 083 final List<MB> methodBindings = anaphoraResolutionSpi.getAccessibleGetterMethods(typeBinding, scope); 084 for (final MB methodBinding : methodBindings) { 085 final I methodName = anaphoraResolutionSpi.getMethodName(methodBinding); 086 final String methodDescription = anaphoraResolutionSpi.getMethodDescription(methodBinding); 087 potentialReferents.add(new IA2MgReferent<>(relatedExpression, methodBinding, methodName, methodDescription, 088 scope, relatedExpressionsSpi, anaphoraResolutionSpi)); 089 } 090 return potentialReferents; 091 } 092 093 @Override 094 public Referent<TB, S, I, QI> createReferent(final S scope, final R relatedExpression, final Object memento) { 095 return createReferent(relatedExpression, scope, relatedExpression.resolveType(scope), memento); 096 } 097 098 protected Referent<TB, S, I, QI> createReferent(final R relatedExpression, final S scope, final TB typeBinding, 099 final Object memento) { 100 final List<MB> methodBindings = anaphoraResolutionSpi.getAccessibleGetterMethods(typeBinding, scope); 101 for (final MB methodBinding : methodBindings) { 102 final I methodName = anaphoraResolutionSpi.getMethodName(methodBinding); 103 final String methodDescription = anaphoraResolutionSpi.getMethodDescription(methodBinding); 104 if (methodName.equals(memento)) { 105 return new IA2MgReferent<>(relatedExpression, methodBinding, methodName, methodDescription, scope, 106 relatedExpressionsSpi, anaphoraResolutionSpi); 107 } 108 } 109 throw new IllegalArgumentException("Failed to re-create referent"); 110 } 111 112 @Override 113 public A createAnaphora(final S scope, final String anaphor, final E anaphorExpression, 114 final R potentialRelatedExpression, final Referent<TB, S, I, QI> potentialReferent, 115 final ReferentializationStrategy<E, TB, S, I, QI> refStrategy) { 116 final DefaultRelatedExpressionPart<N, E, T, B, VB, TB, S, I, QI, R> relatedExpressionPart = new DefaultRelatedExpressionPart<>( 117 potentialRelatedExpression); 118 final DefaultAnaphorPart<N, E, T, B, VB, TB, S, I, QI, R, A> anaphorPart = new DefaultAnaphorPart<>(anaphor, 119 anaphorExpression, potentialReferent, this, refStrategy, 120 ((IA2MgReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) potentialReferent) 121 .getMethodBinding()); 122 final String underspecifiedRelation = anaphoraResolutionSpi.getIA2MgUnderspecifiedRelation( 123 potentialRelatedExpression, 124 ((IA2MgReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) potentialReferent) 125 .getMethodBinding(), 126 scope); 127 return anaphoraResolutionSpi.createIndirectAnaphora(relatedExpressionPart, anaphorPart, underspecifiedRelation, 128 false); 129 } 130 131 @Override 132 public E realize(final RelatedExpressionPart<N, E, T, B, TB, S, I, QI, R> relatedExpressionPart, 133 final AnaphorPart<N, E, T, B, TB, S, I, QI, R, A> anaphorPart, final E replacee, 134 final Optional<I> guessedTempName, final Object... support) { 135 return anaphoraResolutionSpi.realizeIA2Mg(relatedExpressionPart, anaphorPart, replacee, guessedTempName, 136 support); 137 } 138 139 @Override 140 public String getKind() { 141 return "IA2Mg"; 142 } 143 144 @Override 145 public List<Perspectivation> underspecifyAnaphor(final R relatedExpression, final String anaphor, 146 final E anaphorExpression, final Referent<TB, S, I, QI> referent, final S scope) { 147 return underspecifyAnaphorForIA2Mg(relatedExpression, anaphor, anaphorExpression, referent, scope); 148 } 149 150 protected List<Perspectivation> underspecifyAnaphorForIA2Mg(final R relatedExpression, final String anaphor, 151 final E anaphorExpression, final Referent<TB, S, I, QI> referent, final S scope) { 152 final QI qualifier = getQualifierForIA(relatedExpression, anaphor, scope); 153 final int qualifierLength = anaphorsSpi.getLength(qualifier) + ".".length(); 154 final I methodName = referent.getMethodName(); 155 final int lengthOfMethodName = relatedExpressionsSpi.getLength(methodName); 156 return asList(new Hide(0, qualifierLength), new Hide(qualifierLength, "get".length()), 157 new Hide(qualifierLength + lengthOfMethodName, 2), 158 // Always add ToLowerCase because 159 // AnaphoraResolutionSpi.getAccessibleGetterMethods(...) only returns methods 160 // that have an upper-case character following the "get-" prefix. 161 new ToLowerCase(qualifierLength + "get".length())); 162 } 163 164 @Override 165 public String getAnaphorToBeRealized(final RelatedExpressionPart<N, E, T, B, TB, S, I, QI, R> relatedExpressionPart, 166 final List<AnaphorPart<N, E, T, B, TB, S, I, QI, R, A>> allAnaphorPartsRelatedToTheRelatedExpression, 167 final AnaphorPart<N, E, T, B, TB, S, I, QI, R, A> anaphorPart, final S scope) { 168 final var referent = (IA2MgReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) anaphorPart 169 .getReferent(); 170 final I methodName = referent.getMethodName(); 171 final String rawAnaphor = relatedExpressionsSpi.identifierToString(methodName); 172 return getAnaphorToBeRealized(rawAnaphor); 173 } 174 175 protected String getAnaphorToBeRealized(final String rawAnaphor) { 176 if (!rawAnaphor.startsWith("get")) { 177 throw new IllegalArgumentException("Missing get- prefix in method name " + rawAnaphor); 178 } 179 if (rawAnaphor.length() == 3) { 180 throw new IllegalArgumentException("Method name contains get- prefix only"); 181 } 182 if (!Character.isUpperCase(rawAnaphor.charAt(3))) { 183 throw new IllegalArgumentException( 184 "Method name must continue in upper case after get- prefix: " + rawAnaphor); 185 } 186 final char firstChar = Character.toLowerCase(rawAnaphor.charAt("get".length())); 187 final String suffix = rawAnaphor.length() >= ("get".length() + 1) ? rawAnaphor.substring("get".length() + 1) 188 : ""; 189 return firstChar + suffix; 190 } 191 192 public static class IA2MgReferent<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>> 193 extends AbstractReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> { 194 195 final MB methodBinding; 196 final I methodName; 197 final S scope; 198 199 /** 200 * Used in contract testing. 201 */ 202 @SuppressWarnings("unused") 203 protected IA2MgReferent() { 204 methodBinding = null; 205 methodName = null; 206 scope = null; 207 } 208 209 IA2MgReferent(final R relatedExpression, final MB methodBinding, final I methodName, 210 final String methodDescription, final S scope, 211 final RelatedExpressionsSpi<N, E, T, B, MB, TB, S, I, QI, EV, PP, R> relatedExpressionsSpi, 212 final AnaphoraResolutionSpi<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> anaphorResolutionSpi) { 213 super(relatedExpression, methodDescription, relatedExpressionsSpi, anaphorResolutionSpi); 214 this.methodBinding = methodBinding; 215 this.methodName = methodName; 216 this.scope = scope; 217 // TODO: add features for the method name, parameters and return 218 // types? 219 } 220 221 public MB getMethodBinding() { 222 return methodBinding; 223 } 224 225 @SuppressWarnings("unchecked") 226 @Override 227 public boolean canBeUsedInsteadOf(final Referent<TB, S, I, QI> other) { 228 if (!(other instanceof IA2FStrategy.IA2FReferent)) { 229 return false; 230 } else { 231 final IA2FStrategy.IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A> ia2fReferent = (IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) other; 232 final FB fieldBinding = ia2fReferent.getFieldBinding(); 233 return anaphorResolutionSpi.doesGetterMethodOnlyReturnValueOf(methodBinding, fieldBinding, scope); 234 } 235 } 236 237 @Override 238 public boolean hasName() { 239 return true; 240 } 241 242 @Override 243 public QI getName() { 244 return anaphorResolutionSpi.getReferentName(this.methodBinding); 245 } 246 247 @Override 248 public boolean hasMethodName() { 249 return true; 250 } 251 252 @Override 253 public I getMethodName() { 254 return this.methodName; 255 } 256 257 @Override 258 public TB resolveType(final S scope) { 259 return anaphorResolutionSpi.resolveReturnType(this.methodBinding, scope); 260 } 261 262 @Override 263 public Object getMemento() { 264 return this.methodName; 265 } 266 267 @Override 268 public int hashCode() { 269 final int prime = 31; 270 int result = super.hashCode(); 271 result = prime * result + ((this.methodBinding == null) ? 0 : this.methodBinding.hashCode()); 272 return result; 273 } 274 275 @SuppressWarnings("unchecked") 276 @Override 277 public boolean equals(final Object obj) { 278 if (this == obj) { 279 return true; 280 } 281 if (!super.equals(obj)) { 282 return false; 283 } 284 if (getClass() != obj.getClass()) { 285 return false; 286 } 287 final IA2MgReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A> other = (IA2MgReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) obj; 288 if (this.methodBinding == null) { 289 if (other.methodBinding != null) { 290 return false; 291 } 292 } else if (!this.methodBinding.equals(other.methodBinding)) { 293 return false; 294 } 295 return true; 296 } 297 298 @Override 299 public String toString() { 300 // The method binding is not output because it can be quite long. 301 return "IA2MgReferent [methodName=" + methodName + ", getDescription()=" + getDescription() 302 + ", getFeatures()=" + getFeatures() + "]"; 303 } 304 305 } 306}