001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright (c) 2009-2018 CQSE GmbH                                        |
004|                                                                          |
005+-------------------------------------------------------------------------*/
006package eu.cqse.check.framework.util.clike;
007
008import java.util.EnumSet;
009import java.util.List;
010import java.util.stream.Collectors;
011
012import org.conqat.lib.commons.collections.CollectionUtils;
013
014import eu.cqse.check.framework.scanner.ETokenType;
015import eu.cqse.check.framework.scanner.IToken;
016import eu.cqse.check.framework.shallowparser.SubTypeNames;
017import eu.cqse.check.framework.shallowparser.framework.ShallowEntity;
018
019/**
020 * This utility class provides methods that are used when working with blocks.
021 */
022public class ConditionalBlockUtils {
023        /** The blocks that have a condition. */
024        public static final EnumSet<EConditionalType> BLOCKS_WITH_CONDITION = EnumSet.of(EConditionalType.IF,
025                        EConditionalType.ELSE_IF, EConditionalType.CASE);
026
027        /** The blocks that do not have a condition. */
028        public static final EnumSet<EConditionalType> BLOCKS_WITHOUT_CONDITION = EnumSet.of(EConditionalType.SWITCH,
029                        EConditionalType.ELSE, EConditionalType.DEFAULT);
030
031        /** The blocks that correspond to an if block. */
032        public static final EnumSet<EConditionalType> BLOCKS_CORRESPONDING_TO_IF = EnumSet.of(EConditionalType.ELSE_IF,
033                        EConditionalType.ELSE);
034
035        /** The blocks that correspond to a switch block. */
036        public static final EnumSet<EConditionalType> BLOCKS_CORRESPONDING_TO_SWITCH = EnumSet.of(EConditionalType.CASE,
037                        EConditionalType.DEFAULT);
038
039        /** The token types that get sorted out when extracting the condition */
040        public static final EnumSet<ETokenType> UNWANTED_TOKEN_TYPES_IN_CONDITION = EnumSet.of(ETokenType.SWITCH,
041                        ETokenType.LBRACE, ETokenType.IF, ETokenType.ELSEIF, ETokenType.CASE, ETokenType.DOUBLE_DOT,
042                        ETokenType.COLON);
043
044        /**
045         * Extracts the {@link EConditionalType} from the given {@link ShallowEntity}.
046         */
047        public static EConditionalType getBlockTypeFromEntity(ShallowEntity entity) throws IllegalArgumentException {
048                return getBlockTypeFromEntitySubtype(entity.getSubtype());
049        }
050
051        /**
052         * Converts the Subtype of an entity represented by a string to the
053         * corresponding {@link EConditionalType}.
054         */
055        public static EConditionalType getBlockTypeFromEntitySubtype(String subtype) throws IllegalArgumentException {
056                /**
057                 * The fall-through in this switch construct is intended to clarify which
058                 * {@link #SubTypeNames} corresponds to which {@link #EConditionalType}.
059                 */
060                switch (subtype) {
061                case SubTypeNames.IF:
062                case SubTypeNames.IF_LET:
063                case SubTypeNames.IF_VAR:
064                        return EConditionalType.IF;
065                case SubTypeNames.ELIF:
066                case SubTypeNames.ELSIF:
067                case SubTypeNames.ELSE_IF:
068                case SubTypeNames.ELSE_IF_NOSPACE:
069                        return EConditionalType.ELSE_IF;
070                case SubTypeNames.ELSE:
071                        return EConditionalType.ELSE;
072                case SubTypeNames.SWITCH:
073                        return EConditionalType.SWITCH;
074                case SubTypeNames.CASE:
075                        return EConditionalType.CASE;
076                case SubTypeNames.DEFAULT:
077                        return EConditionalType.DEFAULT;
078                default:
079                        return null;
080                }
081        }
082
083        /**
084         * Extracts the tokens describing the condition out of the given list of tokens.
085         */
086        public static List<IToken> extractCondition(ShallowEntity entity) {
087                List<IToken> tokens = getHeaderTokens(entity);
088                return CollectionUtils.filter(tokens, token -> !UNWANTED_TOKEN_TYPES_IN_CONDITION.contains(token.getType())
089                                && token.getLineNumber() == tokens.get(0).getLineNumber());
090        }
091
092        /** Extracts the header tokens from the entity. */
093        private static List<IToken> getHeaderTokens(ShallowEntity entity) {
094                int startTokenIndex = entity.getStartOffset();
095                int endTokenIndex;
096                if (entity.getChildren().size() == 0) {
097                        List<IToken> includedTokens = entity.includedTokens();
098                        endTokenIndex = includedTokens.get(includedTokens.size() - 1).getOffset();
099                } else {
100                        endTokenIndex = entity.getChildren().get(0).getStartOffset();
101                }
102                return entity.includedTokens().stream()
103                                .filter(token -> token.getOffset() > startTokenIndex && token.getEndOffset() < endTokenIndex)
104                                .collect(Collectors.toList());
105        }
106}