001/*-------------------------------------------------------------------------+ 002| | 003| Copyright (c) 2009-2017 CQSE GmbH | 004| | 005+-------------------------------------------------------------------------*/ 006package eu.cqse.check.framework.shallowparser.prettyprint; 007 008import static eu.cqse.check.framework.scanner.ELanguage.ABAP; 009import static eu.cqse.check.framework.scanner.ELanguage.COBOL; 010import static eu.cqse.check.framework.scanner.ETokenType.COMMA; 011import static eu.cqse.check.framework.scanner.ETokenType.DOCUMENTATION_COMMENT; 012import static eu.cqse.check.framework.scanner.ETokenType.END_OF_LINE_COMMENT; 013import static eu.cqse.check.framework.scanner.ETokenType.GT; 014import static eu.cqse.check.framework.scanner.ETokenType.LT; 015import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON; 016import static eu.cqse.check.framework.scanner.ETokenType.SIX_COLUMNS_COMMENT; 017import static eu.cqse.check.framework.scanner.ETokenType.TRADITIONAL_COMMENT; 018import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.METHOD; 019 020import java.util.EnumSet; 021 022import org.conqat.lib.commons.string.StringUtils; 023 024import eu.cqse.check.framework.scanner.ELanguage; 025import eu.cqse.check.framework.scanner.ETokenType; 026import eu.cqse.check.framework.scanner.ETokenType.ETokenClass; 027import eu.cqse.check.framework.scanner.IToken; 028import eu.cqse.check.framework.shallowparser.SubTypeNames; 029import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 030import eu.cqse.check.framework.shallowparser.framework.ShallowEntity; 031import eu.cqse.check.framework.shallowparser.languages.cobol.CobolShallowParser; 032 033/** 034 * Utility functionality for pretty-printing source code. 035 */ 036public class PrettyPrintingUtils { 037 038 /** 039 * Returns true if a method entity is not a section or paragraph otherwise false 040 */ 041 public static boolean methodEntityIsNotSectionOrParagraph(EShallowEntityType type, String subType) { 042 return type == METHOD && subType != null && !subType.equals(CobolShallowParser.SECTION_SUBTYPE_NAME) 043 && !subType.equals(CobolShallowParser.PARAGRAPH_SUBTYPE_NAME); 044 } 045 046 /** Returns if the token is a multiline token. */ 047 public static boolean isMultiLine(IToken token) { 048 return StringUtils.countLines(token.getText()) > 1; 049 } 050 051 /** Returns the larger of two spacings. */ 052 public static ESpacing maxSpacing(ESpacing spacing1, ESpacing spacing2) { 053 if (spacing1.ordinal() > spacing2.ordinal()) { 054 return spacing1; 055 } 056 return spacing2; 057 } 058 059 /** 060 * Returns true if the given entity is outside of a procedure division otherwise 061 * false. 062 */ 063 public static boolean isOutsideOfProcedureDivision(ShallowEntity entity) { 064 ShallowEntity parent = entity.getParent(); 065 if (parent == null) { 066 return false; 067 } 068 069 EShallowEntityType type = parent.getType(); 070 return type == EShallowEntityType.MODULE 071 || PrettyPrintingUtils.methodEntityIsNotSectionOrParagraph(type, parent.getSubtype()); 072 } 073 074 /** Returns the spacing to be used based on the given entity. */ 075 public static ESpacing getStatementSpacing(ShallowEntity entity, ELanguage language) { 076 if (entity.getType() == EShallowEntityType.META && entity.getSubtype().equals("package")) { 077 return ESpacing.EMPTY_LINE; 078 } else if (language == ELanguage.ABAP && SubTypeNames.VISIBILITY.equals(entity.getSubtype())) { 079 return ESpacing.EMPTY_LINE; 080 } else if (language == ELanguage.COBOL) { 081 // A parsed Cobol statement might not consume the closing 082 // DOT. But it gets added into the tokens at 083 // <code>completeTokens()</code>. 084 // We ensure here that such kinds of statements have their DOTs 085 // appear on same line 086 return ESpacing.NONE; 087 } else { 088 return ESpacing.NEW_LINE; 089 } 090 } 091 092 /** Trims a line of token text if necessary. */ 093 public static String trimTokenTextLine(String line, ELanguage language) { 094 if (language == COBOL) { 095 return line; 096 } 097 098 return line.trim(); 099 } 100 101 /** 102 * Determines any additional indentation that should be used. We use a space for 103 * Java comments. 104 */ 105 public static String determineAdditionalIndent(IToken token, ELanguage language) { 106 if (language == ELanguage.JAVA && token.getType().getTokenClass() == ETokenClass.COMMENT) { 107 return StringUtils.SPACE; 108 } 109 return StringUtils.EMPTY_STRING; 110 } 111 112 /** Determines whether no indentation should be used. */ 113 public static boolean determineNoIndent(IToken token) { 114 ETokenType tokenType = token.getType(); 115 if (token.getLanguage() == ABAP && tokenType == TRADITIONAL_COMMENT) { 116 return true; 117 } 118 119 if (token.getLanguage() == COBOL && EnumSet.of(SIX_COLUMNS_COMMENT, TRADITIONAL_COMMENT).contains(tokenType)) { 120 return true; 121 } 122 123 return false; 124 } 125 126 /** Returns the casing to use for a token. */ 127 public static ECasing determineCasing(IToken token) { 128 if (token.getLanguage() == ELanguage.ABAP) { 129 ETokenClass tokenClass = token.getType().getTokenClass(); 130 if (tokenClass == ETokenClass.KEYWORD) { 131 return ECasing.UPPER; 132 } 133 if (tokenClass == ETokenClass.IDENTIFIER) { 134 return ECasing.LOWER; 135 } 136 } 137 return ECasing.ORIGINAL; 138 } 139 140 public static boolean isDelimiter(IToken token) { 141 return token.getType().getTokenClass() == ETokenClass.DELIMITER; 142 } 143 144 /** Returns the spacing to be used after the given token. */ 145 public static ESpacing getPostTokenSpacing(IToken token, int abapChainedStatementDepth) { 146 ETokenType tokenType = token.getType(); 147 ETokenClass tokenClass = tokenType.getTokenClass(); 148 149 if (token.getLanguage() == ELanguage.ABAP && tokenType == COMMA && abapChainedStatementDepth > 0) { 150 return ESpacing.NEW_LINE; 151 } 152 153 if (tokenType == DOCUMENTATION_COMMENT || tokenType == END_OF_LINE_COMMENT) { 154 return ESpacing.NEW_LINE; 155 } 156 157 if (tokenClass == ETokenClass.COMMENT && PrettyPrintingUtils.isMultiLine(token)) { 158 return ESpacing.NEW_LINE; 159 } 160 161 if (token.getLanguage() == ELanguage.ABAP && tokenType == TRADITIONAL_COMMENT) { 162 return ESpacing.NEW_LINE; 163 } 164 165 if (EnumSet.of(COMMA, SEMICOLON, LT, GT).contains(tokenType)) { 166 return ESpacing.SPACE; 167 } 168 169 return ESpacing.NONE; 170 } 171}