001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright (c) 2005-2018 The ConQAT Project                               |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016|                                                                          |
017+-------------------------------------------------------------------------*/
018package eu.cqse.check.base;
019
020import java.util.List;
021
022import org.conqat.lib.commons.collections.CollectionUtils;
023
024import eu.cqse.check.framework.core.CheckException;
025import eu.cqse.check.framework.scanner.ETokenType;
026import eu.cqse.check.framework.scanner.IToken;
027import eu.cqse.check.framework.shallowparser.framework.ShallowEntity;
028import eu.cqse.check.framework.util.tokens.TokenPattern;
029import eu.cqse.check.framework.util.tokens.TokenPatternMatch;
030
031/**
032 * Base class for custom checks which reveal when exceptions are thrown.
033 */
034public abstract class ThrowExceptionCheckBase extends EntityTokenPatternCheckBase {
035
036        /** Arbitrarily chosen int as group number for the token matcher. */
037        private static final int GROUP_NAME = 0;
038
039        private static final TokenPattern THROW_NEW_PATTERN = new TokenPattern().sequence(ETokenType.THROW)
040                        .sequence(ETokenType.NEW)
041                        .sequence(ETokenType.IDENTIFIER, new TokenPattern().repeated(ETokenType.DOT, ETokenType.IDENTIFIER))
042                        .group(GROUP_NAME).sequence(ETokenType.LPAREN);
043
044        /** {@inheritDoc} */
045        @Override
046        protected String getXPathSelectionString() {
047                return "//METHOD";
048        }
049
050        /** {@inheritDoc} */
051        @Override
052        protected TokenPattern getFindingPattern() {
053                return THROW_NEW_PATTERN;
054        }
055
056        /** {@inheritDoc} */
057        @Override
058        protected void processEntity(ShallowEntity entity) throws CheckException {
059                if (skipMethod(entity)) {
060                        return;
061                }
062                processTokens(entity.includedTokens());
063        }
064
065        /**
066         * Returns true if given method should be skipped from the check analysis.
067         */
068        public abstract boolean skipMethod(ShallowEntity entity);
069
070        /** {@inheritDoc} */
071        @Override
072        protected void processTokens(List<IToken> tokens) throws CheckException {
073
074                TokenPattern pattern = getFindingPattern();
075
076                for (TokenPatternMatch match : pattern.findAll(tokens)) {
077                        List<IToken> matchedTokens = match.groupTokens(GROUP_NAME);
078                        // The identifier with the exception name is the last token in the matching
079                        // sequence
080                        IToken exceptionToken = CollectionUtils.getLast(matchedTokens);
081
082                        if (createFindingForException(exceptionToken.getText())) {
083                                createFinding(getFindingMessage(matchedTokens) + exceptionToken.getText(), exceptionToken);
084                        }
085
086                }
087        }
088
089        /**
090         * Returns true if this check should create a finding for the thrown exception.
091         */
092        public abstract boolean createFindingForException(String className) throws CheckException;
093
094}