001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 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+-------------------------------------------------------------------------*/
017package eu.cqse.check.framework.util.variable;
018
019import java.util.ArrayList;
020import java.util.EnumSet;
021import java.util.List;
022
023import eu.cqse.check.framework.scanner.ETokenType;
024import eu.cqse.check.framework.scanner.IToken;
025import eu.cqse.check.framework.shallowparser.TokenStreamTextUtils;
026
027/**
028 * A variable use extractor extracts uses of variables from token streams.
029 * Thereby it differentiates between fields and local variables and considers
030 * shadowing of variables.
031 */
032public class CLikeVariableUseExtractor {
033
034        /** The token type of the operator that is used to access fields. */
035        private final EnumSet<ETokenType> accessOperators;
036
037        /**
038         * All token types that indicate that the previous token is no variable.
039         */
040        private final EnumSet<ETokenType> noVariableSuccessorTypes;
041
042        /** Constructor. */
043        public CLikeVariableUseExtractor(ETokenType accessOperator, EnumSet<ETokenType> noVariableSuccessorTypes) {
044                this(EnumSet.of(accessOperator), noVariableSuccessorTypes);
045        }
046
047        /** Constructor. */
048        public CLikeVariableUseExtractor(EnumSet<ETokenType> accessOperators,
049                        EnumSet<ETokenType> noVariableSuccessorTypes) {
050                this.accessOperators = accessOperators;
051                this.noVariableSuccessorTypes = noVariableSuccessorTypes;
052        }
053
054        /**
055         * Extracts all uses of the given variable name from the given tokens.
056         * 
057         * @param isField
058         *            whether the given variable name is a field of a type
059         * @param isShadowed
060         *            whether the given variable name is shadowed by another variable
061         *            with the same name
062         */
063        public List<Integer> extractVariableUses(List<IToken> tokens, String variableName, boolean isField,
064                        boolean isShadowed) {
065                return extractFilteredVariableUses(tokens, variableName, isField, isShadowed, IVariableUseFilter.ACCEPT_ALL);
066        }
067
068        /**
069         * Extracts all uses of the given variable name from the given tokens that read
070         * its value.
071         * 
072         * @param isField
073         *            whether the given variable name is a field of a type
074         * @param isShadowed
075         *            whether the given variable name is shadowed by another variable
076         *            with the same name
077         */
078        public List<Integer> extractVariableReads(List<IToken> tokens, String variableName, boolean isField,
079                        boolean isShadowed) {
080                return extractFilteredVariableUses(tokens, variableName, isField, isShadowed, VariableReadFilter.INSTANCE);
081        }
082
083        /**
084         * Extracts all uses of the given variable name from the given tokens that
085         * change its value.
086         * 
087         * @param isField
088         *            whether the given variable name is a field of a type
089         * @param isShadowed
090         *            whether the given variable name is shadowed by another variable
091         *            with the same name
092         */
093        public List<Integer> extractVariableWrites(List<IToken> tokens, String variableName, boolean isField,
094                        boolean isShadowed) {
095                return extractFilteredVariableUses(tokens, variableName, isField, isShadowed, VariableWriteFilter.INSTANCE);
096        }
097
098        /**
099         * Extracts all uses of the given variable name from the given tokens.
100         * 
101         * @param isField
102         *            whether the given variable name is a field of a type
103         * @param isShadowed
104         *            whether the given variable name is shadowed by another variable
105         *            with the same name
106         */
107        public List<Integer> extractFilteredVariableUses(List<IToken> tokens, String variableName, boolean isField,
108                        boolean isShadowed, IVariableUseFilter filter) {
109                List<Integer> useIndices = TokenStreamTextUtils.findAll(tokens, variableName);
110                return filterUses(tokens, useIndices, isField, isShadowed, filter);
111        }
112
113        /**
114         * Filters then given variable uses based on whether they are fields, shadowed
115         * and the given custom filter. The parameter useIndices is a list of indices to
116         * tokens in the given token list, that are possible variable uses.
117         */
118        public List<Integer> filterUses(List<IToken> tokens, List<Integer> useIndices, boolean isField, boolean isShadowed,
119                        IVariableUseFilter filter) {
120                List<Integer> filteredUses = new ArrayList<>();
121                for (int index : useIndices) {
122                        if (isVariableUse(tokens, index, isField, isShadowed) && filter.isFiltered(tokens, index, isField)) {
123                                filteredUses.add(index);
124                        }
125                }
126                return filteredUses;
127        }
128
129        /**
130         * Returns whether the element at the given index is any kind of variable use.
131         */
132        private boolean isVariableUse(List<IToken> tokens, int index, boolean isField, boolean isShadowed) {
133                if (index < tokens.size() - 1 && noVariableSuccessorTypes.contains(tokens.get(index + 1).getType())) {
134                        return false;
135                }
136
137                if (isField) {
138                        return isFieldUse(tokens, index, isShadowed);
139                }
140                return isLocalUse(tokens, index, isShadowed);
141        }
142
143        /**
144         * Returns whether the variable use at the given index is the use of the field.
145         * If it is not shadowed, it is definitely a field, otherwise we check if it is
146         * prefixed by the field access operator.
147         */
148        private boolean isFieldUse(List<IToken> tokens, int index, boolean isShadowed) {
149                return !isShadowed || (index > 1 && accessOperators.contains(tokens.get(index - 1).getType()));
150        }
151
152        /**
153         * Returns whether the variable use at the given index is the use of the local
154         * variable. If it is shadowed by another local variable it is not, otherwise we
155         * check if it is prefixed by the field access operator. If it is not, it is the
156         * local variable.
157         */
158        private boolean isLocalUse(List<IToken> tokens, int index, boolean isShadowed) {
159                return (!isShadowed && (index <= 1 || !accessOperators.contains(tokens.get(index - 1).getType())));
160        }
161}