001package de.monochromata.anaphors.ast.chain;
002
003import static java.util.Collections.emptyList;
004import static java.util.Collections.singletonList;
005import static java.util.stream.Collectors.toList;
006
007import java.util.ArrayList;
008import java.util.List;
009import java.util.concurrent.atomic.AtomicBoolean;
010import java.util.stream.Collectors;
011import java.util.stream.Stream;
012
013import org.apache.commons.lang3.tuple.ImmutablePair;
014import org.apache.commons.lang3.tuple.Pair;
015
016import de.monochromata.anaphors.ast.ASTBasedAnaphora;
017import de.monochromata.anaphors.ast.AnaphorPart;
018import de.monochromata.anaphors.ast.RelatedExpressionPart;
019import de.monochromata.anaphors.ast.relatedexp.RelatedExpression;
020
021/**
022 * Used to maintain a list of anaphoric chains.
023 *
024 * {@link ChainElement}
025 */
026public interface Chaining {
027
028        /**
029         * @param <N>  The node type in the AST
030         * @param <E>  The expression type
031         * @param <T>  The type type
032         * @param <B>  The binding type
033         * @param <TB> The type binding type
034         * @param <S>  The scope type (optional)
035         * @param <I>  The type used to represent identifiers
036         * @param <QI> The type used to represent qualified identifiers
037         * @param <AT> The type used for attachments to chain elements that have an
038         *             anaphor part. The attachment may be used to stored e.g. an old
039         *             anaphora relation.
040         * @param <R>  The sub-type of related expression to use
041         * @param <A>  The sub-type of AST-based anaphora to use
042         * @param <C>  The sub-type of chain element to use
043         */
044        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> List<C> emptyListOfChains() {
045                return emptyList();
046        }
047
048        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> List<C> singletonListOfChains(
049                        final A anaphora, final AT anaphorAttachment,
050                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
051                return singletonList(toChain(anaphora, anaphorAttachment, factory));
052        }
053
054        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> C toChain(
055                        final A anaphora, final AT anaphorAttachment,
056                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
057                return toChain(anaphora.getRelatedExpressionPart(), anaphora.getAnaphorPart(), anaphorAttachment, factory);
058        }
059
060        static <TB extends B, PP, E, T, I, B, QI, EV, N, S, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> C toChain(
061                        final RelatedExpressionPart<N, E, T, B, TB, S, I, QI, R> relatedExpressionPart,
062                        final AnaphorPart<N, E, T, B, TB, S, I, QI, R, A> anaphorPart, final AT anaphorAttachment,
063                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
064                final List<C> nextElements = new ArrayList<>();
065                final C relatedExpressionElement = factory.create(null, nextElements, relatedExpressionPart, null, null);
066                final C nextElement = factory.create(relatedExpressionElement, new ArrayList<>(), null, anaphorPart,
067                                anaphorAttachment);
068                nextElements.add(nextElement);
069                return relatedExpressionElement;
070        }
071
072        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> List<C> merge(
073                        final List<C> existingChains, final A anaphora, final AT anaphorAttachment,
074                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
075                return merge(existingChains, toChain(anaphora, anaphorAttachment, factory), factory);
076        }
077
078        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> List<C> merge(
079                        final List<C> existingChains, final List<C> newChains,
080                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
081                List<C> result = existingChains;
082                for (final C newChain : newChains) {
083                        result = merge(result, newChain, factory);
084                }
085                return result;
086        }
087
088        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> List<C> merge(
089                        final List<C> existingChains, final C newChain,
090                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
091                final Pair<Boolean, List<C>> mergedAndResult = mergeForEqualRelatedExpression(existingChains, newChain,
092                                factory);
093                if (mergedAndResult.getLeft()) {
094                        return mergedAndResult.getRight();
095                }
096                return addChain(existingChains, newChain);
097        }
098
099        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> Pair<Boolean, List<C>> mergeForEqualRelatedExpression(
100                        final List<C> existingChains, final C newChain,
101                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
102                final AtomicBoolean ruleApplied = new AtomicBoolean(false);
103                final List<C> mappedChains = existingChains.stream()
104                                .map(existingChainElement -> mergeForEqualRelatedExpression(existingChainElement, newChain, ruleApplied,
105                                                factory))
106                                .collect(toList());
107                return new ImmutablePair<>(ruleApplied.get(), mappedChains);
108        }
109
110        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> C mergeForEqualRelatedExpression(
111                        final C existingRoot, final C newRoot, final AtomicBoolean ruleApplied,
112                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
113                if (relatedExpressionsAreEqual(existingRoot, newRoot)) {
114                        final C mergedChain = mergeByRelatedExpression(existingRoot, newRoot, factory);
115                        ruleApplied.set(true);
116                        return mergedChain;
117                }
118                return existingRoot;
119        }
120
121        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> boolean relatedExpressionsAreEqual(
122                        final C existingRoot, final C newRoot) {
123                return existingRoot.relatedExpression.getRelatedExpression().getRelatedExpression()
124                                .equals(newRoot.relatedExpression.getRelatedExpression().getRelatedExpression());
125        }
126
127        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> C mergeByRelatedExpression(
128                        final C existingRoot, final C newRoot,
129                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
130                final List<C> next = new ArrayList<>();
131                next.addAll(existingRoot.next);
132                next.addAll(linkToPrevious(newRoot.next, existingRoot, factory));
133                return factory.create(null, next, existingRoot.relatedExpression, null, null);
134        }
135
136        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> List<C> linkToPrevious(
137                        final List<C> nextElements, final C previous,
138                        final ChainElementFactory<N, E, T, B, TB, S, I, QI, AT, R, A, C> factory) {
139                return nextElements.stream().map(next -> factory.create(previous, linkToPrevious(next.next, next, factory),
140                                next.relatedExpression, next.anaphor, next.anaphorAttachment)).collect(Collectors.toList());
141        }
142
143        static <N, E, T, B, TB extends B, S, I, QI, AT, R extends RelatedExpression<N, T, B, TB, S, QI, R>, A extends ASTBasedAnaphora<N, E, T, B, TB, S, I, QI, R, A>, C extends ChainElement<N, E, T, B, TB, S, I, QI, AT, R, A, C>> List<C> addChain(
144                        final List<C> existingChains, final C newChain) {
145                return Stream.concat(existingChains.stream(), Stream.of(newChain)).collect(toList());
146        }
147
148}