001/*-------------------------------------------------------------------------+ 002| | 003| Copyright (c) 2005-2017 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| | 017+-------------------------------------------------------------------------*/ 018package eu.cqse.check.framework.postprocessor.cobol; 019 020import static eu.cqse.check.framework.shallowparser.SubTypeNames.ANONYMOUS_METHOD; 021import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.META; 022import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.METHOD; 023import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.MODULE; 024import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.STATEMENT; 025import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.TYPE; 026import static eu.cqse.check.framework.shallowparser.languages.cobol.CobolShallowParser.PARAGRAPH_SUBTYPE_NAME; 027import static eu.cqse.check.framework.shallowparser.languages.cobol.CobolShallowParser.SECTION_SUBTYPE_NAME; 028 029import java.util.ArrayList; 030import java.util.EnumSet; 031import java.util.List; 032 033import org.conqat.lib.commons.collections.CollectionUtils; 034 035import eu.cqse.check.framework.scanner.IToken; 036import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 037import eu.cqse.check.framework.shallowparser.framework.ShallowEntity; 038import eu.cqse.check.framework.util.tokens.TokenUtils; 039 040/** 041 * A Cobol post processor that transforms sections and paragraph statements into 042 * methods. Sections are checked first as priority before paragraphs. 043 */ 044public class SectionParagraphToMethodProcessor { 045 046 /** List of non-comment tokens as seen by the Cobol Shallow Parser */ 047 private final List<IToken> tokens; 048 049 /** Constructor. */ 050 public SectionParagraphToMethodProcessor(List<IToken> tokens) { 051 this.tokens = CollectionUtils.filter(tokens, token -> !TokenUtils.isCommentToken(token)); 052 } 053 054 /** 055 * Transforms a list of shallow entities from a parsing phase, post-processing 056 * them. 057 */ 058 public List<ShallowEntity> postProcess(List<ShallowEntity> entities) { 059 List<ShallowEntity> result = new ArrayList<ShallowEntity>(); 060 // Enterprise COBOL now supports method definition in the ID 061 // division so we do not include it here 062 EnumSet<EShallowEntityType> cobolProgramTypes = EnumSet.of(MODULE, TYPE); 063 for (ShallowEntity entity : entities) { 064 ShallowEntity child; 065 if (cobolProgramTypes.contains(entity.getType())) { 066 child = processProgramTypeAndChildren(entity); 067 } else { 068 child = SectionParagraphToMethodProcessor.createCopy(entity, tokens); 069 } 070 071 child.setComplete(); 072 result.add(child); 073 } 074 075 fixEndTokenIndex(result); 076 077 return result; 078 } 079 080 /** Recursively fixes the end token index to include all children. */ 081 private void fixEndTokenIndex(List<ShallowEntity> entities) { 082 for (ShallowEntity entity : entities) { 083 if (entity.hasChildren()) { 084 List<ShallowEntity> children = entity.getChildren(); 085 fixEndTokenIndex(children); 086 entity.setEndTokenIndex( 087 Math.max(entity.getEndTokenIndex(), CollectionUtils.getLast(children).getEndTokenIndex())); 088 } 089 } 090 } 091 092 /** 093 * Processes Cobol program types i.e. programs, classes, factories or objects 094 * and their children, depending on the presence or absence of section or 095 * paragraph statements. 096 */ 097 private ShallowEntity processProgramTypeAndChildren(ShallowEntity programTypeEntity) { 098 List<ShallowEntity> children = programTypeEntity.getChildren(); 099 List<Integer> indices = getIndicesForEntitiesToBecomeMethods(children, SECTION_SUBTYPE_NAME); 100 if (!indices.isEmpty()) { 101 return makeSectionsParagraphsIntoMethods(programTypeEntity, indices); 102 } 103 104 indices = getIndicesForEntitiesToBecomeMethods(children, PARAGRAPH_SUBTYPE_NAME); 105 if (!indices.isEmpty()) { 106 return makeSectionsParagraphsIntoMethods(programTypeEntity, indices); 107 } 108 109 return makeProgramTypeIntoMethod(programTypeEntity); 110 } 111 112 /** 113 * Retrieve the indices for sections or paragraphs as indicated in the sub-type 114 * parameter. 115 */ 116 private static List<Integer> getIndicesForEntitiesToBecomeMethods(List<ShallowEntity> entities, String subType) { 117 List<Integer> indices = new ArrayList<Integer>(); 118 for (int i = 0; i < entities.size(); ++i) { 119 if (isStatementWithSubTypeAs(entities.get(i), subType)) { 120 indices.add(i); 121 } 122 } 123 124 return indices; 125 } 126 127 /** 128 * Transform the entities of a Cobol program, converting section or paragraph 129 * statements into methods and reorganizing other statements as children of the 130 * methods. 131 */ 132 private ShallowEntity makeSectionsParagraphsIntoMethods(ShallowEntity programTypeEntity, List<Integer> indices) { 133 List<ShallowEntity> children = programTypeEntity.getChildren(); 134 int entityToBecomeMethodIndex = 0; 135 int firstChildIndex = 0; 136 int lastChildIndex = indices.get(0) - 1; 137 ShallowEntity newModuleOrTypeEntity = transformProgramType(programTypeEntity, children, firstChildIndex, 138 lastChildIndex); 139 while (entityToBecomeMethodIndex <= indices.size() - 1) { 140 int index = indices.get(entityToBecomeMethodIndex); 141 ShallowEntity entityToBecomeMethod = children.get(index); 142 firstChildIndex = index + 1; 143 entityToBecomeMethodIndex += 1; 144 if (entityToBecomeMethodIndex == indices.size()) { 145 // We have reached the last entity to become method 146 lastChildIndex = children.size() - 1; 147 } else { 148 lastChildIndex = indices.get(entityToBecomeMethodIndex) - 1; 149 } 150 151 ShallowEntity transformedEntity = transformEntityAndAddChildren(entityToBecomeMethod, children, 152 firstChildIndex, lastChildIndex, true); 153 transformedEntity.setComplete(); 154 newModuleOrTypeEntity.addChild(transformedEntity); 155 } 156 157 newModuleOrTypeEntity.setComplete(); 158 return newModuleOrTypeEntity; 159 } 160 161 /** 162 * For a case where there are no sections and paragraphs, we transform the Cobol 163 * program, factory, object or class statement into a method block. 164 */ 165 private ShallowEntity makeProgramTypeIntoMethod(ShallowEntity programTypeEntity) { 166 ShallowEntity changedProgramTypeEntity = SectionParagraphToMethodProcessor.createCopy(programTypeEntity, 167 tokens); 168 if (programTypeEntity.getType() != METHOD) { 169 // Enterprise Cobol method files will not come in here 170 changedProgramTypeEntity = SectionParagraphToMethodProcessor.changeToMethodEntity(programTypeEntity, 171 tokens); 172 } 173 174 for (ShallowEntity entity : programTypeEntity.getChildren()) { 175 changedProgramTypeEntity.addChild(transformChild(entity)); 176 } 177 178 return changedProgramTypeEntity; 179 } 180 181 /** 182 * Recursively transform an entity and its children. 183 */ 184 private ShallowEntity transformChild(ShallowEntity parent) { 185 ShallowEntity newParent = SectionParagraphToMethodProcessor.createCopy(parent, tokens); 186 newParent.setComplete(); 187 parent.getChildren().forEach(child -> newParent.addChild(transformChild(child))); 188 return newParent; 189 } 190 191 /** 192 * Transform a Cobol program type and its children. If there are statements 193 * between the procedure division entity and first entity to be transformed to a 194 * method, they are made into an artificial method. 195 */ 196 private ShallowEntity transformProgramType(ShallowEntity parent, List<ShallowEntity> programTypeChildren, 197 int firstChildIndex, int lastChildIndex) { 198 ShallowEntity newParent = SectionParagraphToMethodProcessor.createCopy(parent, tokens); 199 int i; 200 for (i = firstChildIndex; i <= lastChildIndex; ++i) { 201 ShallowEntity entity = programTypeChildren.get(i); 202 newParent.addChild(transformChild(entity)); 203 if (isProcedureDivisionEntity(entity)) { 204 // break out to decide if we create an artificial method for 205 // subsequent entities before the first method in the procedure 206 // division 207 break; 208 } 209 } 210 211 if (i == lastChildIndex) { 212 return newParent; 213 } 214 215 i += 1; 216 ShallowEntity result = new ShallowEntity(METHOD, ANONYMOUS_METHOD, "#artificial#", tokens, 217 programTypeChildren.get(i).getStartTokenIndex()); 218 result.setEndTokenIndex(programTypeChildren.get(lastChildIndex).getEndTokenIndex()); 219 ShallowEntity artificialMethod = result; 220 while (i <= lastChildIndex) { 221 artificialMethod.addChild(transformChild(programTypeChildren.get(i))); 222 i += 1; 223 } 224 225 artificialMethod.setComplete(); 226 newParent.addChild(artificialMethod); 227 return newParent; 228 } 229 230 /** 231 * Returns true if an entity is a procedure division otherwise false. 232 */ 233 private static boolean isProcedureDivisionEntity(ShallowEntity entity) { 234 return entity.getType() == META && entity.getSubtype().equals("division") 235 && entity.getName().equals("procedure"); 236 } 237 238 /** 239 * Transforms a given entity into a Cobol Shallow Entity and/or changing its 240 * shallow entity type into a method. For a collection of shallow entities under 241 * a Cobol program type, given entities A, B and C that will be transformed into 242 * methods, this procedure adds all entities between A and B as children of A 243 * while entities between B and C as children of B. 244 */ 245 private ShallowEntity transformEntityAndAddChildren(ShallowEntity parent, List<ShallowEntity> programTypeChildren, 246 int firstChildIndex, int lastChildIndex, boolean transformParentToMethod) { 247 ShallowEntity newParent = SectionParagraphToMethodProcessor.createCopy(parent, tokens); 248 if (transformParentToMethod) { 249 newParent = SectionParagraphToMethodProcessor.changeToMethodEntity(parent, tokens); 250 } 251 252 for (int i = firstChildIndex; i <= lastChildIndex; ++i) { 253 newParent.addChild(transformChild(programTypeChildren.get(i))); 254 } 255 256 if (transformParentToMethod) { 257 newParent.setComplete(); 258 } 259 260 return newParent; 261 } 262 263 /** Creates a copy of a shallow entity. */ 264 private static ShallowEntity createCopy(ShallowEntity entity, List<IToken> tokens) { 265 ShallowEntity result = new ShallowEntity(entity.getType(), entity.getSubtype(), entity.getName(), tokens, 266 entity.getStartTokenIndex()); 267 result.setEndTokenIndex(entity.getEndTokenIndex()); 268 return result; 269 } 270 271 /** Creates copy of a shallow entity with changed type "method". */ 272 private static ShallowEntity changeToMethodEntity(ShallowEntity entity, List<IToken> tokens) { 273 ShallowEntity result = new ShallowEntity(METHOD, entity.getSubtype(), entity.getName(), tokens, 274 entity.getStartTokenIndex()); 275 result.setEndTokenIndex(entity.getEndTokenIndex()); 276 return result; 277 } 278 279 /** 280 * Returns true if a given entity is a statement and has a sub-type as indicated 281 * in the parameter otherwise false. 282 */ 283 private static boolean isStatementWithSubTypeAs(ShallowEntity entity, String subType) { 284 return entity.getSubtype() != null && entity.getSubtype().equals(subType) && entity.getType() == STATEMENT; 285 } 286}