001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright (c) 2009-2017 CQSE GmbH                                        |
004|                                                                          |
005+-------------------------------------------------------------------------*/
006package eu.cqse.check.framework.shallowparser.languages.cobol;
007
008import static eu.cqse.check.framework.scanner.ETokenType.ADD;
009import static eu.cqse.check.framework.scanner.ETokenType.CALL;
010import static eu.cqse.check.framework.scanner.ETokenType.COMPUTE;
011import static eu.cqse.check.framework.scanner.ETokenType.DELETE;
012import static eu.cqse.check.framework.scanner.ETokenType.DISPLAY;
013import static eu.cqse.check.framework.scanner.ETokenType.DIVIDE;
014import static eu.cqse.check.framework.scanner.ETokenType.DOLLAR_END;
015import static eu.cqse.check.framework.scanner.ETokenType.DOLLAR_IF;
016import static eu.cqse.check.framework.scanner.ETokenType.DOT;
017import static eu.cqse.check.framework.scanner.ETokenType.ELSE;
018import static eu.cqse.check.framework.scanner.ETokenType.END_ADD;
019import static eu.cqse.check.framework.scanner.ETokenType.END_CALL;
020import static eu.cqse.check.framework.scanner.ETokenType.END_COMPUTE;
021import static eu.cqse.check.framework.scanner.ETokenType.END_DELETE;
022import static eu.cqse.check.framework.scanner.ETokenType.END_DISPLAY;
023import static eu.cqse.check.framework.scanner.ETokenType.END_DIVIDE;
024import static eu.cqse.check.framework.scanner.ETokenType.END_EVALUATE;
025import static eu.cqse.check.framework.scanner.ETokenType.END_IF;
026import static eu.cqse.check.framework.scanner.ETokenType.END_INVOKE;
027import static eu.cqse.check.framework.scanner.ETokenType.END_JSON;
028import static eu.cqse.check.framework.scanner.ETokenType.END_MULTIPLY;
029import static eu.cqse.check.framework.scanner.ETokenType.END_PERFORM;
030import static eu.cqse.check.framework.scanner.ETokenType.END_READ;
031import static eu.cqse.check.framework.scanner.ETokenType.END_RETURN;
032import static eu.cqse.check.framework.scanner.ETokenType.END_REWRITE;
033import static eu.cqse.check.framework.scanner.ETokenType.END_SEARCH;
034import static eu.cqse.check.framework.scanner.ETokenType.END_START;
035import static eu.cqse.check.framework.scanner.ETokenType.END_STRING;
036import static eu.cqse.check.framework.scanner.ETokenType.END_SUBTRACT;
037import static eu.cqse.check.framework.scanner.ETokenType.END_TRY;
038import static eu.cqse.check.framework.scanner.ETokenType.END_UNSTRING;
039import static eu.cqse.check.framework.scanner.ETokenType.END_WAIT;
040import static eu.cqse.check.framework.scanner.ETokenType.END_WRITE;
041import static eu.cqse.check.framework.scanner.ETokenType.END_XML;
042import static eu.cqse.check.framework.scanner.ETokenType.EVALUATE;
043import static eu.cqse.check.framework.scanner.ETokenType.IF;
044import static eu.cqse.check.framework.scanner.ETokenType.INVOKE;
045import static eu.cqse.check.framework.scanner.ETokenType.JSON;
046import static eu.cqse.check.framework.scanner.ETokenType.MULTIPLY;
047import static eu.cqse.check.framework.scanner.ETokenType.PERFORM;
048import static eu.cqse.check.framework.scanner.ETokenType.READ;
049import static eu.cqse.check.framework.scanner.ETokenType.RETURN;
050import static eu.cqse.check.framework.scanner.ETokenType.REWRITE;
051import static eu.cqse.check.framework.scanner.ETokenType.SEARCH;
052import static eu.cqse.check.framework.scanner.ETokenType.START;
053import static eu.cqse.check.framework.scanner.ETokenType.STRING;
054import static eu.cqse.check.framework.scanner.ETokenType.SUBTRACT;
055import static eu.cqse.check.framework.scanner.ETokenType.TRY;
056import static eu.cqse.check.framework.scanner.ETokenType.UNSTRING;
057import static eu.cqse.check.framework.scanner.ETokenType.WAIT;
058import static eu.cqse.check.framework.scanner.ETokenType.WRITE;
059import static eu.cqse.check.framework.scanner.ETokenType.XML;
060
061import java.util.Arrays;
062import java.util.EnumSet;
063import java.util.List;
064
065import eu.cqse.check.framework.scanner.ETokenType;
066import eu.cqse.check.framework.scanner.IToken;
067import eu.cqse.check.framework.shallowparser.TokenStreamUtils;
068
069/**
070 * Provides utility functions for scope determination-related activities.
071 */
072public class ScopeUtils {
073
074        /**
075         * Cobol statements that can create scopes (or blocks) of code with an
076         * explicit END-VERB statement.
077         */
078        private static final ETokenType[] SCOPE_VERBS = new ETokenType[] { ADD, CALL, COMPUTE, DELETE, DISPLAY, DIVIDE,
079                        DOLLAR_IF, EVALUATE, IF, ELSE, INVOKE, JSON, MULTIPLY, PERFORM, READ, RETURN, REWRITE, SEARCH, START,
080                        STRING, SUBTRACT, TRY, UNSTRING, WAIT, WRITE, XML };
081
082        /**
083         * End Tokens for the scope verbs.
084         */
085        private static final EnumSet<ETokenType> SCOPE_CLOSE_VERBS = EnumSet.of(DOLLAR_END, END_ADD, END_CALL, END_COMPUTE,
086                        END_DELETE, END_DIVIDE, END_DISPLAY, END_EVALUATE, END_IF, END_INVOKE, END_JSON, END_MULTIPLY, END_PERFORM,
087                        END_READ, END_RETURN, END_REWRITE, END_SEARCH, END_START, END_STRING, END_SUBTRACT, END_TRY, END_UNSTRING,
088                        END_WAIT, END_WRITE, END_XML);
089
090        /**
091         * Returns true if at this point the startOffset representing some token is
092         * inside a scope
093         */
094        public static boolean currentlyInScope(List<IToken> tokens, int startOffset) {
095                int openTokenBeforeOffsetIndex = TokenStreamUtils.lastTokenOfType(tokens, 0, startOffset, SCOPE_VERBS);
096                if (openTokenBeforeOffsetIndex != TokenStreamUtils.NOT_FOUND) {
097                        IToken openToken = tokens.get(openTokenBeforeOffsetIndex);
098                        ETokenType closeTokenType = getCloseTokenType(openToken);
099                        int closeTokenBeforeOffsetIndex = TokenStreamUtils.lastTokenOfType(tokens, openTokenBeforeOffsetIndex,
100                                        startOffset, closeTokenType);
101
102                        return closeTokenBeforeOffsetIndex == TokenStreamUtils.NOT_FOUND && (periodEndsScope(tokens, startOffset)
103                                        || countOfOpenVerbsAtLeastCloseVerbs(tokens, closeTokenType, startOffset));
104                }
105
106                return false;
107        }
108
109        /**
110         * Returns the close token type of a given open token
111         */
112        private static ETokenType getCloseTokenType(IToken openToken) {
113                ETokenType closeTokenType;
114                ETokenType openTokenType = openToken.getType();
115                if (EnumSet.of(IF, ELSE).contains(openTokenType)) {
116                        closeTokenType = END_IF;
117                } else if (DOLLAR_IF == openTokenType) {
118                        closeTokenType = DOLLAR_END;
119                } else {
120                        closeTokenType = ETokenType.valueOf("END_" + openToken.getText().toUpperCase());
121                }
122                return closeTokenType;
123        }
124
125        /**
126         * True if a period follows the current offset and ultimately ends all
127         * preceding open scopes.
128         */
129        private static boolean periodEndsScope(List<IToken> tokens, int startOffset) {
130                int nextIndex = startOffset + 1;
131                if (nextIndex < tokens.size() - 1) {
132                        return tokens.get(nextIndex).getType() == DOT;
133                }
134
135                return false;
136        }
137
138        /**
139         * The number of open scope verbs (e.g. IF) is at least the number of close
140         * verbs.
141         */
142        private static boolean countOfOpenVerbsAtLeastCloseVerbs(List<IToken> tokens, ETokenType closeTokenType,
143                        int startOffset) {
144                int indexCloseToken = TokenStreamUtils.firstTokenOfType(tokens, startOffset, closeTokenType, DOT);
145                if (indexCloseToken != TokenStreamUtils.NOT_FOUND) {
146                        int countOpenVerbs = TokenStreamUtils
147                                        .findAll(tokens, startOffset, indexCloseToken, EnumSet.copyOf(Arrays.asList(SCOPE_VERBS))).size();
148                        int countCloseVerbs = TokenStreamUtils.findAll(tokens, startOffset, indexCloseToken, SCOPE_CLOSE_VERBS)
149                                        .size();
150                        return countOpenVerbs >= countCloseVerbs;
151                }
152
153                return false;
154        }
155}