All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xjs.serialization.parser.CommentedTokenParser Maven / Gradle / Ivy

There is a newer version: 0.36
Show newest version
package xjs.serialization.parser;

import xjs.comments.Comment;
import xjs.comments.CommentData;
import xjs.comments.CommentType;
import xjs.core.JsonObject;
import xjs.serialization.token.CommentToken;
import xjs.serialization.token.Token;
import xjs.serialization.token.TokenStream;
import xjs.serialization.token.TokenType;

/**
 * Specialized implementation of {@link TokenParser} designed to tolerate
 * commented JSON formats. This class provides utilities for storing and
 * applying comments to the output, as well as overrides to neatly mesh
 * this logic into the existing interface where possible.
 *
 * @see TokenParser
 */
public abstract class CommentedTokenParser extends TokenParser {
    protected CommentData commentBuffer;

    protected CommentedTokenParser(final TokenStream root) {
        super(root);
        this.commentBuffer = new CommentData();
    }

    @Override
    protected boolean consumeWhitespace(
            final Token t, final boolean nl) {
        if (!t.isMetadata()) {
            return false;
        }
        if (t.type() == TokenType.BREAK) {
            if (!nl) {
                return false;
            }
            this.flagLineAsSkipped();
        } else {
            this.appendComment((CommentToken) t);
        }
        return true;
    }

    @Override
    protected void flagLineAsSkipped() {
        if (!this.commentBuffer.isEmpty()) {
            this.commentBuffer.append(1);
        } else {
            this.linesSkipped++;
        }
    }

    @Override
    protected void setAbove() {
        this.setComment(CommentType.HEADER);
        super.setAbove();
    }

    @Override
    protected void setBetween() {
        this.setComment(CommentType.VALUE);
        super.setBetween();
    }

    @Override
    protected void setTrailing() {
        this.setComment(CommentType.INTERIOR);
        super.setTrailing();
    }

    /**
     * Splits any comments above an open root object (supported formats
     * only) into a root header and a first value header.
     *
     * @param root The root {@link JsonObject} where this data will be
     *             stored.
     */
    protected void readAboveOpenRoot(final JsonObject root) {
        this.readWhitespace();
        this.splitOpenHeader(root);
    }

    @Override
    protected void readAfter() {
        this.readLineWhitespace();
        this.setComment(CommentType.EOL);
    }

    @Override
    protected void readBottom() {
        this.readWhitespace(false);

        this.prependLinesSkippedToComment();
        this.setComment(CommentType.FOOTER);
        this.expectEndOfText();
    }

    /**
     * Appends the given token to the comment buffer.
     *
     * @param t Any token representing a comment.
     */
    protected void appendComment(final CommentToken t) {
        this.commentBuffer.append(new Comment(t));
    }

    /**
     * For each line skipped, appends a newline character to the
     * beginning of the comment buffer, resetting the
     * counter to 0.
     */
    protected void prependLinesSkippedToComment() {
        if (this.linesSkipped > 1) {
            this.commentBuffer.prepend(this.linesSkipped);
            this.linesSkipped = 0;
        }
    }

    /**
     * Applies the contents of the comment buffer to the formatting
     * output as the given type of comment.
     *
     * @param type The type of comment being set.
     */
    protected void setComment(final CommentType type) {
        final CommentData data = this.takeComment(type);
        if (!data.isEmpty()) {
            this.formatting.getComments().setData(type, data);
        }
    }

    /**
     * Returns the full text of the comment buffer, cropping it
     * according to the rules defined in {@link CommentType}.
     *
     * @param type The type of comment currently in the buffer.
     * @return The text of the comment buffer.
     */
    protected CommentData takeComment(final CommentType type) {
        if (this.commentBuffer.isEmpty()) {
            return new CommentData();
        }
        // line comments _must_ have newlines,
        // so they will be added later.
        if (this.commentBuffer.endsWithNewline()) {
            if (this.shouldTakeNl(type)) {
                this.trimComment();
                this.linesSkipped++;
            } else if (this.shouldTrimNl(type)) {
                this.trimComment();
            }
        }
        final CommentData taken = this.commentBuffer;
        this.commentBuffer = new CommentData();
        return taken;
    }

    // header comments always include a newline
    protected final boolean shouldTrimNl(final CommentType type) {
        return type == CommentType.HEADER;
    }

    // eol comments go to eol, but do not include the newline
    protected final boolean shouldTakeNl(final CommentType type) {
        return type == CommentType.EOL;
    }

    /**
     * Removes the last character (usually a newline character) from the
     * comment buffer.
     */
    protected final void trimComment() {
        this.commentBuffer.trimLastNewline();
    }

    /**
     * Splits the contents of the comment buffer into a header of the
     * root JSON object and one of the first member in the object.
     *
     * 

For example, assume the following comments: * *

     *     // first
     *     // second
     *
     *     // third
     *
     *     // fourth
     *     key: value
     * 
* *

This method pairs the first, second, and third comment with * the root object, and the fourth comment with the key-value pair. * * @param root The root {@link JsonObject} where the data will be * stored. */ protected void splitOpenHeader(final JsonObject root) { final CommentData header = this.commentBuffer.takeOpenHeader(); if (header != null) { root.getComments().setData(CommentType.HEADER, header); root.setLinesAbove(this.linesSkipped); this.linesSkipped = header.takeLastLinesSkipped() - 1; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy