001package eu.cqse.check.framework.preprocessor.iec61131; 002 003import java.util.ArrayList; 004import java.util.EnumSet; 005import java.util.List; 006 007import org.conqat.lib.commons.collections.CollectionUtils; 008import org.conqat.lib.commons.string.StringUtils; 009 010import eu.cqse.check.framework.preprocessor.IPreprocessor; 011import eu.cqse.check.framework.scanner.ETokenType; 012import eu.cqse.check.framework.scanner.IToken; 013 014/** 015 * Preprocessing of {@link eu.cqse.check.framework.scanner.ELanguage#IEC61131} 016 * files. Most files are returned without change. We need this to fix files 017 * stored by the IDE PLCNext from PhoenixContact. 018 */ 019public class Iec61131Preprocessor implements IPreprocessor { 020 021 @Override 022 public List<IToken> preprocess(String uniformPath, List<IToken> tokens) { 023 String dirPath = StringUtils.removeLastPart(uniformPath, '/'); 024 // string comparison with == intended since removeLastPart returns the input 025 // object if '/' not found. Next line is the intellij annotation to hide the 026 // warning 027 // noinspection StringEquality 028 if (tokens.isEmpty() || dirPath == uniformPath || !dirPath.endsWith(".pou") || !uniformPath.endsWith(".st")) { 029 return tokens; 030 } 031 // We assume that this is a file stored by Phoenix Contact's PLCNext IDE. 032 return preprocessPlcNextFile(tokens, uniformPath); 033 } 034 035 /** 036 * Preprocesses an ST-Code file stored by Phoenix Contact's PLCNext IDE. Inserts 037 * FUNCTION_BLOCK, NAME, and FUNCTION_BLOCK_END tokens. 038 * 039 * The IDE stores ST-Code in xyz.pou/ directories. One directory per POU 040 * (Program organization unit). Each can contain multiple .st files. Somehow 041 * they do not write the FUNCTION_BLOCK NAME / END_FUNCTION_BLOCK tokens. The 042 * files could also be FUNCTION type this is stored in a .meta file in the 043 * directory, but for teamscale the difference between FUNCTION and 044 * FUNCTION_BLOCK does probably not matter. 045 * 046 * @param tokens 047 * non-empty list of tokens of an st-code file stored by PLCNext 048 */ 049 private static List<IToken> preprocessPlcNextFile(List<IToken> tokens, String uniformPath) { 050 IToken firstToken = tokens.get(0); 051 if (EnumSet.of(ETokenType.FUNCTION_BLOCK, ETokenType.FUNCTION).contains(firstToken.getType())) { 052 return tokens; 053 } 054 String blockName = StringUtils.stripSuffix(StringUtils.getLastPart(uniformPath, '/'), ".st").replaceAll(" ", 055 "_"); 056 IToken lastToken = CollectionUtils.getLast(tokens); 057 IToken start = lastToken.newToken(ETokenType.FUNCTION_BLOCK, 0, 0, "FUNCTION_BLOCK", 058 "##preprocessor-generated##"); 059 IToken blockNameToken = lastToken.newToken(ETokenType.IDENTIFIER, 0, 0, blockName, 060 "##preprocessor-generated##"); 061 IToken end = lastToken.newToken(ETokenType.END_FUNCTION_BLOCK, lastToken.getOffset(), lastToken.getLineNumber(), 062 "END_FUNCTION_BLOCK", "##preprocessor-generated##"); 063 List<IToken> result = new ArrayList<>(tokens.size() + 2); 064 result.add(start); 065 result.add(blockNameToken); 066 result.addAll(tokens); 067 result.add(end); 068 return result; 069 } 070}