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.perspectivation.Perspectivation; 026import de.monochromata.anaphors.perspectivation.Perspectivation.Hide; 027import de.monochromata.anaphors.perspectivation.Perspectivation.ToLowerCase; 028 029/** 030 * An indirect-anaphora resolution strategy that anchors anaphors in a field 031 * declared by 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 IA2FStrategy<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 StoresAnchorInLocalTempVariable<N, E, T, B, TB, S, I, QI, R, A> { 053 054 public static final List<Class<? extends RelatedExpressionStrategy>> IA2F_SUPPORTED_RELATED_EXPRESSION_STRATEGIES = asList( 055 ClassInstanceCreationStrategy.class, ParameterDeclarationStrategy.class, 056 LocalVariableDeclarationStrategy.class); 057 058 /** 059 * Used in contract testing. 060 */ 061 @SuppressWarnings("unused") 062 protected IA2FStrategy() { 063 } 064 065 public IA2FStrategy(final AnaphorsSpi<N, E, TB, S, I, QI, EV, PP> anaphorsSpi, 066 final RelatedExpressionsSpi<N, E, T, B, MB, TB, S, I, QI, EV, PP, R> relatedExpressionsSpi, 067 final AnaphoraResolutionSpi<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> anaphoraResolutionSpi) { 068 super(IA2F_SUPPORTED_RELATED_EXPRESSION_STRATEGIES, anaphorsSpi, relatedExpressionsSpi, anaphoraResolutionSpi); 069 } 070 071 @Override 072 protected List<Referent<TB, S, I, QI>> createPotentialReferents(final S scope, final R potentialRelatedExpression) { 073 // TODO: Maybe use existing type binding, if it has been resolved 074 // previously 075 return createPotentialReferents(potentialRelatedExpression, scope, 076 potentialRelatedExpression.resolveType(scope)); 077 } 078 079 protected List<Referent<TB, S, I, QI>> createPotentialReferents(final R relatedExpression, final S scope, 080 final TB typeBinding) { 081 final List<Referent<TB, S, I, QI>> potentialReferents = new ArrayList<>(); 082 final List<FB> fieldBindings = anaphoraResolutionSpi.getAccessibleFields(typeBinding, scope); 083 for (final FB fieldBinding : fieldBindings) { 084 final I fieldName = anaphoraResolutionSpi.getFieldName(fieldBinding); 085 final QI qualifiedFieldName = relatedExpressionsSpi.toQualifiedIdentifier(fieldName); 086 final String fieldDescription = anaphoraResolutionSpi.getFieldDescription(fieldBinding); 087 potentialReferents.add(new IA2FReferent<>(relatedExpression, fieldBinding, qualifiedFieldName, 088 fieldDescription, 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<FB> fieldBindings = anaphoraResolutionSpi.getAccessibleFields(typeBinding, scope); 101 for (final FB fieldBinding : fieldBindings) { 102 final I fieldName = anaphoraResolutionSpi.getFieldName(fieldBinding); 103 final QI qualifiedFieldName = relatedExpressionsSpi.toQualifiedIdentifier(fieldName); 104 final String fieldDescription = anaphoraResolutionSpi.getFieldDescription(fieldBinding); 105 if (fieldName.equals(memento)) { 106 return new IA2FReferent<>(relatedExpression, fieldBinding, qualifiedFieldName, fieldDescription, 107 relatedExpressionsSpi, anaphoraResolutionSpi); 108 } 109 } 110 throw new IllegalArgumentException("Could not re-create referent from memento " + memento); 111 } 112 113 @SuppressWarnings("unchecked") 114 @Override 115 public A createAnaphora(final S scope, final String anaphor, final E anaphorExpression, 116 final R potentialRelatedExpression, final Referent<TB, S, I, QI> potentialReferent, 117 final ReferentializationStrategy<E, TB, S, I, QI> refStrategy) { 118 final DefaultRelatedExpressionPart<N, E, T, B, VB, TB, S, I, QI, R> relatedExpressionPart = new DefaultRelatedExpressionPart<>( 119 potentialRelatedExpression); 120 final DefaultAnaphorPart<N, E, T, B, VB, TB, S, I, QI, R, A> anaphorPart = new DefaultAnaphorPart<>(anaphor, 121 anaphorExpression, potentialReferent, this, refStrategy, 122 ((IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) potentialReferent) 123 .getFieldBinding()); 124 final String underspecifiedRelation = anaphoraResolutionSpi.getIA2FUnderspecifiedRelation( 125 potentialRelatedExpression, 126 ((IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) potentialReferent) 127 .getFieldBinding(), 128 scope); 129 return anaphoraResolutionSpi.createIndirectAnaphora(relatedExpressionPart, anaphorPart, underspecifiedRelation, 130 false); 131 } 132 133 @Override 134 public E realize(final RelatedExpressionPart<N, E, T, B, TB, S, I, QI, R> relatedExpressionPart, 135 final AnaphorPart<N, E, T, B, TB, S, I, QI, R, A> anaphorPart, final E replacee, 136 final Optional<I> guessedTempName, final Object... support) { 137 return anaphoraResolutionSpi.realizeIA2F(relatedExpressionPart, anaphorPart, replacee, guessedTempName, 138 support); 139 } 140 141 @Override 142 public String getKind() { 143 return "IA2F"; 144 } 145 146 @Override 147 public List<Perspectivation> underspecifyAnaphor(final R relatedExpression, final String anaphor, 148 final E anaphorExpression, final Referent<TB, S, I, QI> referent, final S scope) { 149 return underspecifyAnaphorForIA2F(relatedExpression, anaphor, anaphorExpression, 150 (IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) referent, scope); 151 } 152 153 protected List<Perspectivation> underspecifyAnaphorForIA2F(final R relatedExpression, final String anaphor, 154 final E anaphorExpression, final IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A> referent, 155 final S scope) { 156 final QI qualifier = getQualifierForIA(relatedExpression, anaphor, scope); 157 final int qualifierLength = anaphorsSpi.getLength(qualifier) + ".".length(); 158 final I fieldName = anaphoraResolutionSpi.getFieldName(referent.getFieldBinding()); 159 final boolean fieldNameStartsUpperCase = anaphoraResolutionSpi.identifierStartsUpperCase(fieldName); 160 return underspecifyAnaphorForIA2F(qualifierLength, fieldNameStartsUpperCase); 161 } 162 163 /** 164 * Only public to ease testing. Should be moved to a mixin interface and be 165 * hidden/separated using Java modules. 166 */ 167 public List<Perspectivation> underspecifyAnaphorForIA2F(final int qualifierLength, 168 final boolean fieldNameStartsUpperCase) { 169 final List<Perspectivation> perspectivations = new ArrayList<>(); 170 perspectivations.add(new Hide(0, qualifierLength)); 171 if (fieldNameStartsUpperCase) { 172 perspectivations.add(new ToLowerCase(qualifierLength)); 173 } 174 return perspectivations; 175 } 176 177 @Override 178 public String getAnaphorToBeRealized(final RelatedExpressionPart<N, E, T, B, TB, S, I, QI, R> relatedExpressionPart, 179 final List<AnaphorPart<N, E, T, B, TB, S, I, QI, R, A>> allAnaphorPartsRelatedToTheRelatedExpression, 180 final AnaphorPart<N, E, T, B, TB, S, I, QI, R, A> anaphorPart, final S scope) { 181 final IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A> referent = ((IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) anaphorPart 182 .getReferent()); 183 final I fieldName = anaphoraResolutionSpi.getFieldName(referent.getFieldBinding()); 184 return getAnaphorToBeRealized(fieldName); 185 } 186 187 protected String getAnaphorToBeRealized(final I fieldName) { 188 final String rawAnaphor = relatedExpressionsSpi.identifierToString(fieldName); 189 return getAnaphorToBeRealized(rawAnaphor); 190 } 191 192 protected String getAnaphorToBeRealized(final String rawAnaphor) { 193 if (rawAnaphor.isEmpty()) { 194 return rawAnaphor; 195 } 196 final String prefix = Character.toLowerCase(rawAnaphor.charAt(0)) + ""; 197 final String suffix = rawAnaphor.length() > 1 ? rawAnaphor.substring(1) : ""; 198 return prefix + suffix; 199 } 200 201 public static class IA2FReferent<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>> 202 extends AbstractReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> { 203 204 final FB fieldBinding; 205 final QI fieldName; 206 207 /** 208 * Used in contract testing. 209 */ 210 @SuppressWarnings("unused") 211 protected IA2FReferent() { 212 fieldBinding = null; 213 fieldName = null; 214 } 215 216 IA2FReferent(final R relatedExpression, final FB fieldBinding, final QI fieldName, 217 final String fieldDescription, 218 final RelatedExpressionsSpi<N, E, T, B, MB, TB, S, I, QI, EV, PP, R> relatedExpressionsSpi, 219 final AnaphoraResolutionSpi<N, E, T, B, VB, FB, MB, TB, S, I, QI, R, A> anaphorResolutionSpi) { 220 super(relatedExpression, fieldDescription, relatedExpressionsSpi, anaphorResolutionSpi); 221 this.fieldBinding = fieldBinding; 222 this.fieldName = fieldName; 223 // TODO: Add field name or binding as feature? 224 } 225 226 public FB getFieldBinding() { 227 return this.fieldBinding; 228 } 229 230 @Override 231 public boolean canBeUsedInsteadOf(final Referent<TB, S, I, QI> other) { 232 // TODO: implement additional alias analysis 233 return false; 234 } 235 236 @Override 237 public boolean hasName() { 238 return true; 239 } 240 241 @Override 242 public QI getName() { 243 return this.fieldName; 244 } 245 246 @Override 247 public boolean hasMethodName() { 248 return false; 249 } 250 251 @Override 252 public I getMethodName() { 253 throw new UnsupportedOperationException("IA2FReferent has no method name"); 254 } 255 256 @Override 257 public TB resolveType(final S scope) { 258 return anaphorResolutionSpi.resolveType(fieldBinding, scope); 259 } 260 261 @Override 262 public Object getMemento() { 263 return this.fieldName; 264 } 265 266 @Override 267 public int hashCode() { 268 final int prime = 31; 269 int result = super.hashCode(); 270 result = prime * result + ((this.fieldBinding == null) ? 0 : this.fieldBinding.hashCode()); 271 return result; 272 } 273 274 @SuppressWarnings("unchecked") 275 @Override 276 public boolean equals(final Object obj) { 277 if (this == obj) { 278 return true; 279 } 280 if (!super.equals(obj)) { 281 return false; 282 } 283 if (getClass() != obj.getClass()) { 284 return false; 285 } 286 final IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A> other = (IA2FReferent<N, E, T, B, VB, FB, MB, TB, S, I, QI, EV, PP, R, A>) obj; 287 if (this.fieldBinding == null) { 288 if (other.fieldBinding != null) { 289 return false; 290 } 291 } else if (!this.fieldBinding.equals(other.fieldBinding)) { 292 return false; 293 } 294 return true; 295 } 296 297 @Override 298 public String toString() { 299 // The field binding is not output because it can be quite long. 300 return "IA2FReferent [fieldName=" + fieldName + ", getDescription()=" + getDescription() 301 + ", getFeatures()=" + getFeatures() + "]"; 302 } 303 304 } 305 306}