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}