001/*-----------------------------------------------------------------------+
002 | com.teamscale.checks
003 |                                                                       |
004   $Id$            
005 |                                                                       |
006 | Copyright (c)  2009-2015 CQSE GmbH                                 |
007 +-----------------------------------------------------------------------*/
008package eu.cqse.check.java;
009
010import static eu.cqse.check.framework.scanner.ETokenType.DOT;
011import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
012import static eu.cqse.check.framework.scanner.ETokenType.POINTERTO;
013import static eu.cqse.check.framework.scanner.ETokenType.SCOPE;
014
015import java.util.EnumSet;
016import java.util.List;
017
018import org.conqat.lib.commons.assertion.CCSMAssert;
019import org.conqat.lib.commons.collections.CollectionUtils;
020
021import eu.cqse.check.framework.scanner.ETokenType;
022import eu.cqse.check.framework.scanner.IToken;
023import eu.cqse.check.framework.shallowparser.TokenStreamTextUtils;
024
025/**
026 * Finds static method calls in token streams.
027 */
028public class StaticMethodCallRecognizer {
029
030        /**
031         * The {@link ETokenType}s that are used for accessing a method of an object.
032         */
033        private static final EnumSet<ETokenType> METHOD_CALL_TOKEN_TYPES = EnumSet.of(DOT, SCOPE, POINTERTO);
034
035        /** The full qualified method name. */
036        private final String fullQualifiedName;
037
038        /**
039         * The sequence of token texts, that identifies the method in a token stream.
040         */
041        private String[] recognitionSequence;
042
043        /** Constructor. */
044        public StaticMethodCallRecognizer(String fullQualifiedMethodName) {
045                CCSMAssert.isNotNull(fullQualifiedMethodName);
046                this.fullQualifiedName = fullQualifiedMethodName;
047                initializeRecognitionSequence();
048        }
049
050        /** Build the recognition sequence out of the full qualified method name. */
051        private void initializeRecognitionSequence() {
052                String[] splitTokens = fullQualifiedName.split("\\.");
053                recognitionSequence = new String[splitTokens.length * 2];
054                for (int i = 0; i < splitTokens.length; i++) {
055                        int j = i * 2;
056                        recognitionSequence[j] = splitTokens[i];
057                        if (i == splitTokens.length - 1) {
058                                recognitionSequence[j + 1] = "(";
059                        } else {
060                                recognitionSequence[j + 1] = ".";
061                        }
062                }
063        }
064
065        /** Returns all indices of method calls within the given tokens. */
066        public List<Integer> findCallsInTokens(List<IToken> tokens) {
067                List<Integer> potentialCalls = TokenStreamTextUtils.findAllSequences(tokens, 0, IDENTIFIER,
068                                recognitionSequence);
069                return CollectionUtils.filter(potentialCalls,
070                                index -> atBeginningOrNotPreceededByMethodCallSeparator(tokens, index));
071        }
072
073        /**
074         * Returns <code>true</code> if a system call is not preceded by one of the
075         * {@link #METHOD_CALL_TOKEN_TYPES}.
076         */
077        private static boolean atBeginningOrNotPreceededByMethodCallSeparator(List<IToken> tokens, int index) {
078                if (index == 0) {
079                        return true;
080                }
081                ETokenType previousType = tokens.get(index - 1).getType();
082                return !METHOD_CALL_TOKEN_TYPES.contains(previousType);
083        }
084
085        /** Returns the length of the full qualified method name in tokens. */
086        public int getTokenLength() {
087                return recognitionSequence.length - 1;
088        }
089
090        /** Returns a string representation of the full qualified method name. */
091        public String getFullQualifiedMethodName() {
092                return fullQualifiedName;
093        }
094}