com.squarespace.template.TreeEmitter.java Source code

Java tutorial

Introduction

Here is the source code for com.squarespace.template.TreeEmitter.java

Source

/**
 * Copyright (c) 2015 SQUARESPACE, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.squarespace.template;

import static com.squarespace.template.ReprEmitter.emitNames;

import java.util.List;

import org.apache.commons.lang3.StringEscapeUtils;

import com.squarespace.template.Instructions.AlternatesWithInst;
import com.squarespace.template.Instructions.BindVarInst;
import com.squarespace.template.Instructions.CommentInst;
import com.squarespace.template.Instructions.IfInst;
import com.squarespace.template.Instructions.PredicateInst;
import com.squarespace.template.Instructions.RepeatedInst;
import com.squarespace.template.Instructions.SectionInst;
import com.squarespace.template.Instructions.TextInst;
import com.squarespace.template.Instructions.VariableInst;

/**
 * Emits a string representation of an instruction tree, for debugging
 * purposes.
 */
public class TreeEmitter {

    private static final int INCR = 2;

    private TreeEmitter() {
    }

    public static String get(Instruction inst) {
        StringBuilder buf = new StringBuilder();
        emit(inst, 0, buf);
        return buf.toString();
    }

    public static void emit(Instruction inst, int depth, StringBuilder buf) {
        if (inst == null) {
            return;
        }
        InstructionType type = inst.getType();
        emitHeader(type, inst, depth, buf);
        switch (type) {
        case ALTERNATES_WITH:
        case IF:
        case OR_PREDICATE:
        case PREDICATE:
        case SECTION:
            emitConsequent((BlockInstruction) inst, depth, buf);
            emitAlternative((BlockInstruction) inst, depth, buf);
            break;

        case REPEATED:
            emitConsequent((BlockInstruction) inst, depth, buf);
            emitAlternatesWith((RepeatedInst) inst, depth, buf);
            emitAlternative((BlockInstruction) inst, depth, buf);
            break;

        case ROOT:
            emitBlock(((BlockInstruction) inst).getConsequent(), depth, buf);
            break;

        default:
            break;
        }
    }

    private static void emitHeader(InstructionType type, Instruction inst, int depth, StringBuilder buf) {
        if (type.equals(InstructionType.ROOT)) {
            return;
        }

        indent(depth, buf);
        buf.append(type.toString());
        buf.append(" {").append(inst.getLineNumber()).append(',').append(inst.getCharOffset()).append("} ");
        switch (type) {

        case BINDVAR:
            BindVarInst bindvar = (BindVarInst) inst;
            buf.append(bindvar.getName()).append(" = ");
            emitNames(bindvar.getVariable(), buf);
            break;

        case COMMENT:
            CommentInst comment = (CommentInst) inst;
            emitEscapedString(comment.getView(), buf);
            break;

        case IF:
            ReprEmitter.emitIfExpression((IfInst) inst, buf);
            break;

        case OR_PREDICATE:
        case PREDICATE:
            PredicateInst predicateInst = (PredicateInst) inst;
            Predicate predicate = predicateInst.getPredicate();
            if (predicate != null) {
                buf.append(predicate);
                Arguments args = predicateInst.getArguments();
                if (!args.isEmpty()) {
                    buf.append(' ');
                    emitArgs(args, buf);
                }
            }
            break;

        case REPEATED:
            RepeatedInst repeated = (RepeatedInst) inst;
            emitNames(repeated.getVariable(), buf);
            break;

        case SECTION:
            SectionInst section = (SectionInst) inst;
            emitNames(section.getVariable(), buf);
            break;

        case TEXT:
            TextInst text = (TextInst) inst;
            emitEscapedString(text.getView(), buf);
            break;

        case VARIABLE:
            VariableInst variable = (VariableInst) inst;
            emitNames(variable.getVariable(), buf);
            for (FormatterCall formatterCall : variable.getFormatters()) {
                buf.append('\n');
                indent(depth + INCR, buf);
                buf.append("| ");
                buf.append(formatterCall.getFormatter().identifier());
                Arguments args = formatterCall.getArguments();
                if (!args.isEmpty()) {
                    buf.append(' ');
                    emitArgs(args, buf);
                }
            }
            break;

        default:
            break;
        }
        buf.append('\n');
    }

    private static void emitArgs(Arguments args, StringBuilder buf) {
        List<String> rawArgs = args.getArgs();
        if (!rawArgs.isEmpty()) {
            buf.append("delim='");
            buf.append(StringEscapeUtils.escapeJava("" + args.getDelimiter()));
            buf.append("' parsed=").append(args.getArgs());
        }
    }

    private static void emitEscapedString(StringView view, StringBuilder buf) {
        String raw = view.toString();
        int length = raw.length();
        int maxLen = Math.min(40, length);
        buf.append("(len=").append(length).append(") ");
        buf.append('"').append(StringEscapeUtils.escapeJava(raw.substring(0, maxLen)));
        if (maxLen != raw.length()) {
            buf.append(" ...");
        }
        buf.append('"');
    }

    private static void emitConsequent(BlockInstruction inst, int depth, StringBuilder buf) {
        Block block = inst.getConsequent();
        if (block != null) {
            emitBlock(block, depth + INCR, buf);
        }
    }

    private static void emitAlternatesWith(RepeatedInst inst, int depth, StringBuilder buf) {
        AlternatesWithInst alternatesWith = inst.getAlternatesWith();
        if (alternatesWith != null) {
            emit(alternatesWith, depth + INCR, buf);
        }
    }

    private static void emitAlternative(BlockInstruction inst, int depth, StringBuilder buf) {
        Instruction alternative = inst.getAlternative();
        if (alternative != null) {
            emit(alternative, depth, buf);
        }
    }

    private static void emitBlock(Block block, int depth, StringBuilder buf) {
        emit(block.getInstructions(), depth, buf);
    }

    private static void emit(List<Instruction> instructions, int depth, StringBuilder buf) {
        int size = instructions.size();
        for (int i = 0; i < size; i++) {
            emit(instructions.get(i), depth, buf);
        }
    }

    private static void indent(int depth, StringBuilder buf) {
        for (int i = 0; i < depth; i++) {
            buf.append(' ');
        }
    }
}