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; 018 019import static eu.cqse.check.framework.scanner.ETokenType.BASE; 020import static eu.cqse.check.framework.scanner.ETokenType.BOOL; 021import static eu.cqse.check.framework.scanner.ETokenType.BYTE; 022import static eu.cqse.check.framework.scanner.ETokenType.CHAR; 023import static eu.cqse.check.framework.scanner.ETokenType.CONST; 024import static eu.cqse.check.framework.scanner.ETokenType.DECIMAL; 025import static eu.cqse.check.framework.scanner.ETokenType.DOT; 026import static eu.cqse.check.framework.scanner.ETokenType.DOUBLE; 027import static eu.cqse.check.framework.scanner.ETokenType.EQ; 028import static eu.cqse.check.framework.scanner.ETokenType.FLOAT; 029import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER; 030import static eu.cqse.check.framework.scanner.ETokenType.IN; 031import static eu.cqse.check.framework.scanner.ETokenType.INT; 032import static eu.cqse.check.framework.scanner.ETokenType.INTERNAL; 033import static eu.cqse.check.framework.scanner.ETokenType.LONG; 034import static eu.cqse.check.framework.scanner.ETokenType.LPAREN; 035import static eu.cqse.check.framework.scanner.ETokenType.NEW; 036import static eu.cqse.check.framework.scanner.ETokenType.OBJECT; 037import static eu.cqse.check.framework.scanner.ETokenType.OUT; 038import static eu.cqse.check.framework.scanner.ETokenType.PARAMS; 039import static eu.cqse.check.framework.scanner.ETokenType.PRIVATE; 040import static eu.cqse.check.framework.scanner.ETokenType.PROTECTED; 041import static eu.cqse.check.framework.scanner.ETokenType.PUBLIC; 042import static eu.cqse.check.framework.scanner.ETokenType.READONLY; 043import static eu.cqse.check.framework.scanner.ETokenType.RPAREN; 044import static eu.cqse.check.framework.scanner.ETokenType.SBYTE; 045import static eu.cqse.check.framework.scanner.ETokenType.SHORT; 046import static eu.cqse.check.framework.scanner.ETokenType.STATIC; 047import static eu.cqse.check.framework.scanner.ETokenType.STRING; 048import static eu.cqse.check.framework.scanner.ETokenType.UINT; 049import static eu.cqse.check.framework.scanner.ETokenType.ULONG; 050import static eu.cqse.check.framework.scanner.ETokenType.USHORT; 051import static eu.cqse.check.framework.scanner.ETokenType.VAR; 052import static eu.cqse.check.framework.scanner.ETokenType.VOID; 053import static eu.cqse.check.framework.scanner.ETokenType.VOLATILE; 054 055import java.util.ArrayList; 056import java.util.EnumSet; 057import java.util.List; 058import java.util.Set; 059 060import org.conqat.lib.commons.assertion.CCSMAssert; 061import org.conqat.lib.commons.collections.CollectionUtils; 062 063import eu.cqse.check.framework.scanner.ELanguage; 064import eu.cqse.check.framework.scanner.ETokenType; 065import eu.cqse.check.framework.scanner.IToken; 066import eu.cqse.check.framework.shallowparser.SubTypeNames; 067import eu.cqse.check.framework.shallowparser.TokenStreamTextUtils; 068import eu.cqse.check.framework.shallowparser.TokenStreamUtils; 069import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 070import eu.cqse.check.framework.shallowparser.framework.ShallowEntity; 071import eu.cqse.check.framework.shallowparser.languages.cs.CsShallowParser; 072import eu.cqse.check.framework.util.variable.CSVariableUseExtractor; 073 074/** 075 * Language feature parser for Cs. 076 */ 077public class CsLanguageFeatureParser extends CLikeLanguageFeatureParserBase { 078 079 /** IN and OUT keyword token types. */ 080 private static final EnumSet<ETokenType> IN_OUT_KEYWORDS = EnumSet.of(IN, OUT); 081 082 /** All token types that can be used to specify a type. */ 083 public static final EnumSet<ETokenType> ADDITIONAL_TYPE_TOKENS = EnumSet.of(IDENTIFIER, VAR, STRING, OBJECT); 084 085 /** All token types that can be used to specify a primitive type. */ 086 public static final EnumSet<ETokenType> PRIMITIVE_TYPE_TOKENS = EnumSet.of(BOOL, CHAR, BYTE, SBYTE, SHORT, INT, 087 LONG, USHORT, UINT, ULONG, FLOAT, DOUBLE, DECIMAL, VOID); 088 089 /** All token types that are modifiers for variables or attributes. */ 090 public static final EnumSet<ETokenType> VARIABLE_DECLARATION_MODIFIERS = EnumSet.of(PUBLIC, PRIVATE, INTERNAL, 091 PROTECTED, CONST, READONLY, STATIC, VOLATILE, NEW); 092 093 /** Suffix for CS-EventHandlers. */ 094 public static final String EVENT_ARGS_SUFFIX = "EventArgs"; 095 096 /** Constructor. */ 097 public CsLanguageFeatureParser() { 098 super(ELanguage.CS, CsShallowParser.VALID_IDENTIFIERS, PRIMITIVE_TYPE_TOKENS, ADDITIONAL_TYPE_TOKENS, DOT, ".", 099 new CSVariableUseExtractor(DOT, CsShallowParser.VALID_IDENTIFIERS)); 100 } 101 102 /** Names of classes that count as generic exceptions. */ 103 private static final Set<String> GENERIC_EXCEPTION_NAMES = CollectionUtils.asHashSet("Exception", 104 "ApplicationException", "SystemException"); 105 106 /** 107 * Returns the imported namespace name from a given "using" entity. If the 108 * "using" clause is an aliasing clause, null is returned. 109 */ 110 @Override 111 public String getImportName(ShallowEntity entity) { 112 CCSMAssert.isTrue(isImport(entity), "entity.getType() must be equal to EShallowEntityType.META and " 113 + "entity.getSubtype() must be equal to SubTypeNames.USING"); 114 115 List<IToken> tokens = entity.ownStartTokens(); 116 117 if (TokenStreamUtils.containsAll(tokens, EQ)) { 118 // if this using directive is aliasing a namespace (thus it contains 119 // a "="), we don't extract the namespace 120 return null; 121 } 122 123 int lastIdentifierIndex = TokenStreamUtils.lastTokenOfType(tokens, CsShallowParser.VALID_IDENTIFIERS); 124 if (lastIdentifierIndex == TokenStreamUtils.NOT_FOUND) { 125 return null; 126 } 127 128 return TokenStreamTextUtils.concatTokenTexts(tokens.subList(1, lastIdentifierIndex + 1)); 129 } 130 131 /** Returns whether the given entity is a using directive. */ 132 @Override 133 public boolean isImport(ShallowEntity entity) { 134 return entity.getType().equals(EShallowEntityType.META) && entity.getSubtype().equals(SubTypeNames.USING); 135 } 136 137 /** Checks whether the given method is a CS-EventHandler. */ 138 public boolean isEventHandler(ShallowEntity method) { 139 CCSMAssert.isTrue(method.getType() == EShallowEntityType.METHOD, "method.getType() must be \"METHOD\""); 140 if (!hasVoidReturnType(method)) { 141 return false; 142 } 143 144 List<List<IToken>> parameterTokens = getSplitParameterTokens(method); 145 if (parameterTokens.size() != 2) { 146 return false; 147 } 148 149 List<IToken> firstParameter = parameterTokens.get(0); 150 List<IToken> secondParameter = parameterTokens.get(1); 151 152 if (!TokenStreamUtils.hasTypes(firstParameter, ETokenType.OBJECT, ETokenType.IDENTIFIER)) { 153 return false; 154 } 155 156 String secondType = getModifiersAndTypeFromTokens(secondParameter).getSecond(); 157 return secondType.endsWith(EVENT_ARGS_SUFFIX); 158 } 159 160 /** Returns whether the given entity is partial. */ 161 public boolean isPartial(ShallowEntity entity) { 162 return TokenStreamUtils.firstTokenOfType(entity.ownStartTokens(), 163 ETokenType.PARTIAL) != TokenStreamUtils.NOT_FOUND; 164 } 165 166 /** Returns all parameter tokens of a base call in a constructor. */ 167 public List<IToken> getBaseCallParameterTokens(ShallowEntity constructor) { 168 CCSMAssert.isTrue( 169 constructor.getType() == EShallowEntityType.METHOD 170 && SubTypeNames.CONSTRUCTOR.equals(constructor.getSubtype()), 171 "The given entity must be a constructor"); 172 List<IToken> headTokens = constructor.ownStartTokens(); 173 int baseIndex = TokenStreamUtils.firstTokenOfTypeSequence(headTokens, 0, BASE, LPAREN); 174 if (baseIndex < 0) { 175 return CollectionUtils.emptyList(); 176 } 177 return TokenStreamUtils.tokensBetweenWithNesting(headTokens, baseIndex, LPAREN, RPAREN); 178 } 179 180 @Override 181 public boolean hasVariableLengthArgumentList(List<IToken> tokens) { 182 return TokenStreamUtils.containsAll(tokens, PARAMS); 183 } 184 185 /** 186 * Filters IN and OUT tokens from generics. 187 * 188 * {@inheritDoc} 189 */ 190 @Override 191 protected List<IToken> filterGenericTokens(List<IToken> genericTokens) { 192 return CollectionUtils.filter(genericTokens, token -> !IN_OUT_KEYWORDS.contains(token.getType())); 193 } 194 195 /** Returns a list of all class names the given entity inherits from. */ 196 public List<String> getParentNames(ShallowEntity type) { 197 CCSMAssert.isTrue(type.getType() == EShallowEntityType.TYPE, "type.getType() must be \"TYPE\""); 198 List<IToken> inheritanceTokens = TokenStreamUtils.tokensBetween(type.ownStartTokens(), ETokenType.COLON, 199 ETokenType.LBRACE); 200 if (inheritanceTokens.isEmpty()) { 201 return CollectionUtils.emptyList(); 202 } 203 204 List<List<IToken>> splitInheritanceTokens = TokenStreamUtils.splitWithNesting(inheritanceTokens, 205 ETokenType.COMMA, ETokenType.LT, ETokenType.GT); 206 return TokenStreamTextUtils.concatAllTokenTexts(splitInheritanceTokens); 207 } 208 209 /** 210 * Returns all variable names from a variable declaration within a for 211 * statement. 212 */ 213 public List<IToken> getVariableNamesFromForLikeTokens(List<IToken> forLikeTokens, ETokenType endToken) { 214 List<IToken> variableDeclarationTokens = getVariableTokensFromForLikeTokens(forLikeTokens, endToken); 215 return getVariableDeclarationNamesFromTokens(variableDeclarationTokens); 216 } 217 218 /** 219 * Returns all variable names from the given variable declaration tokens. Names 220 * are only returned if they are newly introduced, that means, that e. g. the 221 * variable "t" from "using(t) {" will not be returned. This method does not 222 * support generic types at the moment. 223 */ 224 public List<IToken> getVariableDeclarationNamesFromTokens(List<IToken> variableDeclarationTokens) { 225 List<IToken> variableNames = new ArrayList<>(); 226 227 int offset = 0; 228 while (variableDeclarationTokens.size() > 0 229 && VARIABLE_DECLARATION_MODIFIERS.contains(variableDeclarationTokens.get(offset).getType())) { 230 offset += 1; 231 } 232 233 if (variableDeclarationTokens.size() >= 2 + offset) { 234 IToken token1 = variableDeclarationTokens.get(offset); 235 IToken token2 = variableDeclarationTokens.get(1 + offset); 236 if (isVariableDeclaration(token1, token2)) { 237 variableNames.add(token2); 238 addAdditionalVariableNameTokens(variableDeclarationTokens, offset, variableNames); 239 } 240 241 } 242 243 return variableNames; 244 } 245 246 /** Adds additional variable name tokens. */ 247 private static void addAdditionalVariableNameTokens(List<IToken> variableDeclarationTokens, int offset, 248 List<IToken> variableNames) { 249 int parenthesisNesting = 0; 250 for (int i = 2 + offset; i < variableDeclarationTokens.size(); ++i) { 251 IToken token = variableDeclarationTokens.get(i); 252 ETokenType tokenType = token.getType(); 253 if (tokenType == ETokenType.LPAREN) { 254 parenthesisNesting += 1; 255 } else if (tokenType == ETokenType.RPAREN) { 256 parenthesisNesting -= 1; 257 } else if (tokenType == ETokenType.IDENTIFIER && parenthesisNesting == 0 258 && variableDeclarationTokens.get(i - 1).getType() == ETokenType.COMMA) { 259 variableNames.add(token); 260 } 261 } 262 } 263 264 /** Returns whether the two given tokens indicate a variable declaration. */ 265 private boolean isVariableDeclaration(IToken token1, IToken token2) { 266 return (typeTokens.contains(token1.getType()) && validIdentifiers.contains(token2.getType())); 267 } 268 269 /** 270 * Determines whether the given class name represents a generic exception. 271 */ 272 public boolean isGenericExceptionClass(String className) { 273 return GENERIC_EXCEPTION_NAMES.contains(className); 274 } 275}