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}