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.util; 018 019import java.util.ArrayList; 020import java.util.EnumSet; 021import java.util.List; 022 023import eu.cqse.check.framework.scanner.ELanguage; 024import eu.cqse.check.framework.scanner.ETokenType; 025import eu.cqse.check.framework.scanner.IToken; 026import eu.cqse.check.framework.shallowparser.TokenStreamUtils; 027import eu.cqse.check.framework.shallowparser.framework.ShallowEntity; 028import eu.cqse.check.framework.shallowparser.util.ShallowParsingUtils; 029 030/** 031 * Concerned with checks for ternary operator for c-like languages and Python 032 */ 033public class TernaryOperatorCheckUtil { 034 035 /** 036 * Returns a list of the index occurrences of ternary operators. For c-like 037 * languages, its a list of indices for the '?'. For Python, its the 'if' word. 038 * 039 * @param entity 040 * A statement which may contains ternary operators. 041 */ 042 public static List<Integer> findOccurrenes(ShallowEntity entity) { 043 List<Integer> finalIndices = new ArrayList<>(); 044 045 List<IToken> tokens = entity.ownStartTokens(); 046 if (TokenStreamUtils.containsAll(tokens, ETokenType.QUESTION, ETokenType.COLON)) { 047 List<Integer> questionMarkIndices = TokenStreamUtils.findAll(tokens, EnumSet.of(ETokenType.QUESTION)); 048 for (int i : questionMarkIndices) { 049 checkSurroundingOfQuestionMark(finalIndices, tokens, i); 050 } 051 } else if (ShallowParsingUtils.getLanguage(entity) == ELanguage.PYTHON 052 && TokenStreamUtils.containsAll(tokens, ETokenType.IF, ETokenType.ELSE)) { 053 List<Integer> elseIndices = TokenStreamUtils.findAll(tokens, EnumSet.of(ETokenType.ELSE)); 054 for (int i : elseIndices) { 055 checkElseSuccessorReportIfIndex(finalIndices, tokens, i); 056 } 057 } 058 059 return finalIndices; 060 } 061 062 /** 063 * Given the index of an 'else', check if an 'else' keyword is preceded by a 064 * colon and report the index of its corresponding 'if'. 065 * 066 * @param indices 067 * List of final set of 'if' indices 068 * @param tokens 069 * List of {@link IToken}s 070 * @param indexElse 071 * The index of one 'else' token. 072 */ 073 private static void checkElseSuccessorReportIfIndex(List<Integer> indices, List<IToken> tokens, int indexElse) { 074 int successorIndex = indexElse + 1; 075 if (successorIndex >= tokens.size()) { 076 return; 077 } 078 079 if (tokens.get(successorIndex).getType() == ETokenType.COLON) { 080 return; 081 } 082 083 int correspondingIfIndex = TokenStreamUtils.findMatchingOpeningToken(tokens, indexElse - 1, ETokenType.IF, 084 ETokenType.ELSE); 085 if (correspondingIfIndex > TokenStreamUtils.NOT_FOUND) { 086 indices.add(indexElse); 087 } 088 } 089 090 /** 091 * Checks the surrounding of the question mark to exclude all cases which are 092 * not ternary operators. 093 */ 094 private static void checkSurroundingOfQuestionMark(List<Integer> indices, List<IToken> tokens, 095 int indexQuestionMark) { 096 if (indexQuestionMark < 1) { 097 return; 098 } 099 100 // Avoid Java generic-type arguments. 101 if (isJavaGenericTypeArgument(tokens, indexQuestionMark)) { 102 return; 103 } 104 105 // What precedes or succeeds the ? character now could be (, [ or an 106 // identifier like in a C# Null conditional 107 // https://msdn.microsoft.com/en-us/library/dn986595(v=vs.140).aspx 108 if (TokenStreamUtils.findMatchingClosingToken(tokens, indexQuestionMark + 1, ETokenType.QUESTION, 109 ETokenType.COLON) > TokenStreamUtils.NOT_FOUND) { 110 indices.add(indexQuestionMark); 111 } 112 } 113 114 /** 115 * Checks if question mark is generic type argument 116 */ 117 private static boolean isJavaGenericTypeArgument(List<IToken> tokens, int indexQuestionMark) { 118 if (indexQuestionMark < 1) { 119 return false; 120 } 121 122 ETokenType predecessorTokenType = tokens.get(indexQuestionMark - 1).getType(); 123 if (predecessorTokenType == ETokenType.LT) { 124 return true; 125 } 126 127 if (indexQuestionMark + 1 >= tokens.size()) { 128 return false; 129 } 130 131 ETokenType successorTokenType = tokens.get(indexQuestionMark + 1).getType(); 132 return predecessorTokenType == ETokenType.COMMA 133 && (successorTokenType == ETokenType.GT || successorTokenType == ETokenType.COMMA 134 || successorTokenType == ETokenType.EXTENDS || successorTokenType == ETokenType.SUPER); 135 } 136}