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}