001/*-------------------------------------------------------------------------+ 002| | 003| Copyright (c) 2009-2018 CQSE GmbH | 004| | 005+-------------------------------------------------------------------------*/ 006package eu.cqse.check.framework.util.clike; 007 008import java.util.ArrayList; 009import java.util.List; 010 011import eu.cqse.check.framework.core.CheckException; 012import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 013import eu.cqse.check.framework.shallowparser.framework.ShallowEntity; 014 015/** 016 * This class provides various helper methods to extract conditional block 017 * groups out of the abstract syntax tree or Lists of ShallowEntities. 018 */ 019public class ConditionalBlockGroupExtractor { 020 021 /** 022 * Recursively checks the entity and its children for corresponding statements 023 * in conditional expressions. Use this for example on the Document Root coming 024 * from the AST to find all {@link ConditionalBlockGroupBase} in the Document. 025 * 026 * @param entity 027 * the entity whose children are checked for if/else/else if or 028 * switch/case statements 029 * @throws CheckException 030 */ 031 public static List<ConditionalBlockGroupBase> findCorrespondingBlockGroups(ShallowEntity entity) 032 throws CheckException { 033 List<ConditionalBlockGroupBase> groupedBlocks = new ArrayList<>(); 034 EConditionalType blockType = ConditionalBlockUtils.getBlockTypeFromEntity(entity); 035 036 if (blockType != null) { 037 switch (blockType) { 038 case IF: 039 groupedBlocks.add(createIfBlockGroup(entity)); 040 break; 041 case SWITCH: 042 groupedBlocks.add(createSwitchBlockGroup(entity)); 043 break; 044 default: 045 break; 046 } 047 } 048 049 groupedBlocks.addAll(findCorrespondingBlockGroups(entity.getChildren())); 050 051 return groupedBlocks; 052 } 053 054 /** 055 * Recursively checks the given entities and their children for corresponding 056 * statements in conditional expressions. 057 * 058 * @param entities 059 * the entities whose children are checked for if/else/else if or 060 * switch/case statements 061 * @throws CheckException 062 */ 063 public static List<ConditionalBlockGroupBase> findCorrespondingBlockGroups(List<ShallowEntity> entities) 064 throws CheckException { 065 List<ConditionalBlockGroupBase> groupedBlocks = new ArrayList<>(); 066 if (entities.size() == 0) { 067 return groupedBlocks; 068 } 069 070 ShallowEntity parent = entities.get(0).getParent(); 071 for (ShallowEntity entity : entities) { 072 ShallowEntity actualParent = entity.getParent(); 073 if (actualParent != parent) { 074 String message = "The ShallowEntity: [" + entity.getSubtype().toUpperCase() + "] spanning lines "; 075 message += entity.getStartLine() + " through " + entity.getEndLine(); 076 message += " has a different parent from its siblings"; 077 message += " .Expected parent: [" + parent.getSubtype() + "] spanning lines " + parent.getStartLine() 078 + " through " + parent.getEndLine(); 079 message += " .Actual parent: [" + actualParent.getSubtype() + "] spanning lines " 080 + actualParent.getStartLine() + " through " + actualParent.getEndLine(); 081 message += " File: " + entity.getAllTokens().get(0).getOriginId(); 082 throw new IllegalArgumentException(message); 083 } 084 085 List<ConditionalBlockGroupBase> childrenBlockGroups = findCorrespondingBlockGroups(entity); 086 if (childrenBlockGroups.size() > 0) { 087 groupedBlocks.addAll(childrenBlockGroups); 088 } 089 } 090 091 return groupedBlocks; 092 } 093 094 /** 095 * Finds all corresponding else/ else if blocks for the given if statement 096 * 097 * @param ifEntity 098 * the if statement for which the else/ else if blocks are searched 099 */ 100 private static IfBlockGroup createIfBlockGroup(ShallowEntity ifEntity) { 101 List<ShallowEntity> neighboringEntities = ifEntity.getParent().getChildren(); 102 int ifIndex = neighboringEntities.indexOf(ifEntity); 103 104 IfBlockGroup ifblockGroup = new IfBlockGroup(); 105 106 ConditionalBlock ifBlock = new ConditionalBlock(ConditionalBlockUtils.getBlockTypeFromEntity(ifEntity), 107 ConditionalBlockUtils.extractCondition(ifEntity), ifEntity.getChildren()); 108 ifblockGroup.getCorrespondingBlocks().add(ifBlock); 109 110 for (int nextIndex = ifIndex + 1; nextIndex < neighboringEntities.size(); nextIndex++) { 111 ShallowEntity nextEntity = neighboringEntities.get(nextIndex); 112 EConditionalType blockType = ConditionalBlockUtils.getBlockTypeFromEntity(nextEntity); 113 if (ConditionalBlockUtils.BLOCKS_CORRESPONDING_TO_IF.contains(blockType)) { 114 if (blockType == EConditionalType.ELSE_IF) { 115 ifblockGroup.getCorrespondingBlocks().add(new ConditionalBlock(blockType, 116 ConditionalBlockUtils.extractCondition(nextEntity), nextEntity.getChildren())); 117 } else if (blockType == EConditionalType.ELSE) { 118 ifblockGroup.getCorrespondingBlocks() 119 .add(new ConditionalBlock(blockType, nextEntity.getChildren())); 120 break; 121 } 122 } else { 123 break; 124 } 125 } 126 127 return ifblockGroup; 128 } 129 130 /** 131 * Finds all corresponding case blocks for the given switch statement 132 * 133 * @param switchEntity 134 * the switch statement for which the cases are searched 135 */ 136 private static SwitchBlockGroup createSwitchBlockGroup(ShallowEntity switchEntity) { 137 List<ShallowEntity> metas = findMetaStatements(switchEntity); 138 List<ShallowEntity> innerStatements = switchEntity.getChildren(); 139 140 SwitchBlockGroup switchBlockGroup = new SwitchBlockGroup(); 141 switchBlockGroup.setSurroundingEntity(switchEntity); 142 143 for (int i = 0; i < metas.size(); i++) { 144 ShallowEntity currentMeta = metas.get(i); 145 146 int currentMetaParentIndex = innerStatements.indexOf(currentMeta); 147 int nextMetaParentIndex; 148 if (i + 1 < metas.size()) { 149 nextMetaParentIndex = innerStatements.indexOf(metas.get(i + 1)); 150 } else { 151 nextMetaParentIndex = innerStatements.size(); 152 } 153 154 List<ShallowEntity> metaStatements = innerStatements.subList(currentMetaParentIndex + 1, 155 nextMetaParentIndex); 156 157 if (metaStatements.isEmpty()) { 158 // empty statements are not relevant for this check (e.g. fallthrough case 159 // statement) 160 continue; 161 } 162 163 EConditionalType blockType = ConditionalBlockUtils.getBlockTypeFromEntity(currentMeta); 164 165 if (blockType == EConditionalType.CASE) { 166 switchBlockGroup.getCorrespondingBlocks().add(new ConditionalBlock(blockType, 167 ConditionalBlockUtils.extractCondition(currentMeta), metaStatements)); 168 } else { 169 switchBlockGroup.getCorrespondingBlocks().add(new ConditionalBlock(blockType, metaStatements)); 170 } 171 } 172 173 return switchBlockGroup; 174 } 175 176 /** 177 * Searches for the meta statements inside the switch case construct. Meta 178 * statements are case and default statements. 179 */ 180 private static List<ShallowEntity> findMetaStatements(ShallowEntity entity) { 181 List<ShallowEntity> metas = new ArrayList<>(); 182 for (ShallowEntity innerStatement : entity.getChildren()) { 183 if (innerStatement.getType() == EShallowEntityType.META) { 184 metas.add(innerStatement); 185 } 186 } 187 return metas; 188 } 189}