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}