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}