com.cisco.yangide.core.parser.YangTokenFormatter.java Source code

Java tutorial

Introduction

Here is the source code for com.cisco.yangide.core.parser.YangTokenFormatter.java

Source

/*******************************************************************************
 * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
 *  
 *  This program and the accompanying materials are made available under the
 *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
 *  and is available at http://www.eclipse.org/legal/epl-v10.html
 *  
 *******************************************************************************/
package com.cisco.yangide.core.parser;

import static org.opendaylight.yangtools.antlrv4.code.gen.YangLexer.LEFT_BRACE;
import static org.opendaylight.yangtools.antlrv4.code.gen.YangLexer.RIGHT_BRACE;
import static org.opendaylight.yangtools.antlrv4.code.gen.YangLexer.S;
import static org.opendaylight.yangtools.antlrv4.code.gen.YangLexer.SEMICOLON;
import static org.opendaylight.yangtools.antlrv4.code.gen.YangLexer.STRING;
import static org.opendaylight.yangtools.antlrv4.code.gen.YangLexer.WS;
import static org.opendaylight.yangtools.antlrv4.code.gen.YangLexer.LINE_COMMENT;
import static org.opendaylight.yangtools.antlrv4.code.gen.YangLexer.END_BLOCK_COMMENT;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

import org.antlr.v4.runtime.Token;
import org.opendaylight.yangtools.antlrv4.code.gen.YangLexer;

/**
 * @author Konstantin Zaitsev
 * @date Jul 22, 2014
 */
public class YangTokenFormatter implements ITokenFormatter {

    private StringBuilder sb;
    private List<Token> tokens;
    private Token prevToken;

    // states
    private int currIndent = 0;
    private boolean wasNL;
    private boolean ovrNL;
    private boolean wasWS;
    // was new line separator '{' '}' ';'
    private int nlCount = 0;
    private boolean importScope = false;
    private StringBuilder importStatement = null;

    // preferences
    private int indent;
    private String lineSeparator;
    private Boolean formatString;
    private Boolean formatComment;
    private int maxLineLength;
    private Boolean compactImport;
    private boolean spaceForTabs;

    public YangTokenFormatter(YangFormattingPreferences preferences, int indentationLevel, String lineSeparator) {
        this.sb = new StringBuilder();
        this.spaceForTabs = preferences.isSpaceForTabs();
        this.indent = preferences.getIndentSize();
        this.formatComment = preferences.isFormatComment();
        this.formatString = preferences.isFormatStrings();
        this.compactImport = preferences.isCompactImport();
        this.maxLineLength = preferences.getMaxLineLength();
        this.lineSeparator = lineSeparator;
        this.currIndent = indentationLevel * this.indent;
        this.tokens = new ArrayList<>();
    }

    @Override
    public void process(Token token) {
        // ignore '\r' token
        if (isCarriageReturn(token)) {
            return;
        }

        if (isWS(token) && (isWS(prevToken) || isNewLine(prevToken))) {
            return;
        }

        if (isNewLine(token) && (isNewLine(prevToken) && nlCount > 1)) {
            return;
        }

        nlCount = isNewLine(token) ? (nlCount + 1) : 0;
        prevToken = token;
        tokens.add(token);

        // System.out.println(ignore + " '" + token.getText() + "'" + " - " +
        // YangLexer.tokenNames[token.getType()]);
    }

    /**
     * @param token token to inspect
     * @return <code>true</code> if token should be ignored from processing
     */
    private boolean checkForIgnore(Token token) {

        // ignore any repeat whitespace
        if (isWS(token) && (wasWS || wasNL)) {
            return true;
        }

        // ignore multiple new lines
        if (isNewLine(token) && (ovrNL || nlCount > 1)) {
            ovrNL = false;
            return true;
        }
        return false;
    }

    /**
     * @param token
     * @return <code>true</code> if token processed by import case
     */
    private boolean processImportCase(Token token) {
        if (!compactImport) {
            return false;
        }

        if (token.getType() == YangLexer.IMPORT_KEYWORD) {
            importScope = true;
            importStatement = new StringBuilder();
        }

        if (importScope) {
            if (token.getType() == RIGHT_BRACE) {
                importScope = false;
                printIndent();
                sb.append(importStatement.toString()).append(' ');
                wasWS = true;
                nlCount = 0;
                // add indent for right brace processing
                currIndent += indent;
                return false;
            } else if (!isNewLine(token) && !isWS(token)) {
                if (importStatement.length() > 0 && token.getType() != SEMICOLON) {
                    importStatement.append(' ');
                }
                importStatement.append(token.getText());
            }
            return true;
        }

        return importScope;
    }

    /**
     * Prints formatted text of token into string buffer.
     *
     * @param token current token
     */
    private void processToken(Token token) {
        int type = token.getType();

        if (type == STRING) {
            String text = token.getText();
            if (!formatString || text.length() < maxLineLength) {
                if (!(wasWS || token.getType() == LEFT_BRACE || token.getType() == SEMICOLON)) {
                    if (wasNL) {
                        printIndent();
                    }
                    printIndent();
                }
                sb.append(text);
            } else {
                printFormattedString(text);
            }
        } else if (type == YangLexer.END_BLOCK_COMMENT) {
            if (formatComment) {
                sb.append(lineSeparator);
                printIndent();
                String text = token.getText().substring(0, token.getText().length() - 2);
                text = text.replaceAll("(?m)^\\s+\\*", "");
                printFormattedString(text);
                sb.append(lineSeparator);
                printIndent();
                sb.append("*/");
            } else {
                sb.append(token.getText());
            }
        } else {
            if (!(wasWS || isNewLine(token) || isWS(token) || token.getType() == SEMICOLON
                    || token.getType() == LEFT_BRACE)) {
                printIndent();
            }
            if (!isNewLine(token)) {
                sb.append(token.getText());
            }
        }
        if (isNewLine(token)) {
            sb.append(lineSeparator);
        }
    }

    /**
     * Prints formatted string token.
     *
     * @param token string token
     */
    private void printFormattedString(String str) {
        String text = str;
        text = text.replaceAll("\\s+", " ");

        boolean firstLine = true;
        StringBuilder textSB = new StringBuilder();
        StringTokenizer st = new StringTokenizer(text);
        while (st.hasMoreTokens()) {
            if (textSB.length() > 0) {
                textSB.append(" ");
            }
            textSB.append(st.nextToken());
            if (textSB.length() > maxLineLength) {
                if (!firstLine || wasNL) {
                    printIndent();
                    printIndent();
                }
                sb.append(textSB.toString());
                if (st.hasMoreTokens()) {
                    sb.append(lineSeparator);
                }
                textSB = new StringBuilder();
                firstLine = false;
            }
        }
        if (textSB.length() > 0) {
            if (!firstLine || wasNL) {
                printIndent();
                printIndent();
            }
            sb.append(textSB.toString());
        }
    }

    /**
     * Updates the state before token processed.
     *
     * @param token current token
     */
    private void updatePreState(Token token) {
        int type = token.getType();

        // decrease indent if '}' token
        if (type == RIGHT_BRACE) {
            currIndent -= indent;
            if (currIndent < 0) {
                currIndent = 0;
            }
        }

        // add space in expression like 'st {'
        if (!wasWS && type == LEFT_BRACE) {
            sb.append(" ");
            wasWS = true;
        }
    }

    /**
     * Updates the state after token processed.
     *
     * @param token current token
     */
    private void updateState(Token token) {
        int type = token.getType();

        // increase indent if '{' token
        if (type == LEFT_BRACE) {
            currIndent += indent;
        }
        ovrNL = false;

        // added new line after '{', ';', '}' tokens
        if (type == LEFT_BRACE || type == SEMICOLON || type == RIGHT_BRACE) {
            sb.append(lineSeparator);
            ovrNL = true;
            // nlCount++;
        }

        wasNL = isNewLine(token) || ovrNL;
        wasWS = isWS(token);

        if (isNewLine(token)) {
            nlCount++;
        }

        if (!wasNL) {
            nlCount = 0;
        }
    }

    /**
     * Prints appropriate indent to string buffer.
     */
    private void printIndent() {
        char[] str = new char[currIndent];
        Arrays.fill(str, spaceForTabs ? ' ' : '\t');
        sb.append(str);
    }

    /**
     * @param token token to inspect
     * @return <code>true</code> if token is space or space separator
     */
    private boolean isWS(Token token) {
        if (token == null) {
            return false;
        }
        int type = token.getType();
        return (type == WS || type == S) && !token.getText().equals("\n") && !token.getText().equals("\r");
    }

    /**
     * @param token token to inspect
     * @return <code>true</code> if token is new line token.
     */
    private boolean isNewLine(Token token) {
        if (token == null) {
            return false;
        }
        int type = token.getType();
        return (type == WS || type == S) && token.getText().equals("\n");
    }

    /**
     * @param token token to inspect
     * @return <code>true</code> if token is '\r' token.
     */
    private boolean isCarriageReturn(Token token) {
        if (token == null) {
            return false;
        }
        int type = token.getType();
        return (type == WS || type == S) && token.getText().equals("\r");
    }

    /**
     * @param token token to inspect
     * @return <code>true</code> if token is '{' '}' ';' token.
     */
    private boolean isBlockSeparator(Token token) {
        if (token == null) {
            return false;
        }
        int type = token.getType();
        return type == LEFT_BRACE || type == SEMICOLON || type == RIGHT_BRACE;
    }

    @Override
    public String getFormattedContent() {
        cleanTokens();
        for (Token token : tokens) {
            boolean ignore = checkForIgnore(token);
            if (!ignore) {
                if (!processImportCase(token)) {
                    updatePreState(token);
                    processToken(token);
                    updateState(token);
                }
            }
        }
        return sb.toString();
    }

    /**
     * Cleans 'new line' tokens before block separators. We assume that we already clean all 'new
     * line' that occurred more 2 times.
     * <br>
     * New line token is not erased if it followed a comment. 
     */
    private void cleanTokens() {
        int idx = 0;
        while (idx < tokens.size() - 2) {
            if (isWS(tokens.get(idx)) && isNewLine(tokens.get(idx + 1))) {
                tokens.remove(tokens.get(idx));
                idx--;
            } else if (isNewLine(tokens.get(idx)) && isBlockSeparator(tokens.get(idx + 1)) && !isComment(idx - 1)) {
                tokens.remove(tokens.get(idx));
                idx--;
            } else {
                idx++;
            }
        }
    }

    /**
     * Returns true if token at idx is a line comment or is a block comment end.
     */
    private boolean isComment(int idx) {
        return idx > 0
                && (tokens.get(idx).getType() == LINE_COMMENT || tokens.get(idx).getType() == END_BLOCK_COMMENT);
    }
}