Visitor.java

package com.mackenziehigh.autumn.util.json.parser;

import autumn.lang.Record;
import autumn.util.F;
import com.mackenziehigh.snowflake.ITreeNode;
import java.math.BigDecimal;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;

/**
 * An instance of this class can be used to convert a JSON string to an Autumn object tree.
 *
 * @author Mackenzie High
 */
public final class Visitor
        extends AbstractVisitor
{
    /**
     * This stack is used to hold intermediate values during the conversion process.
     */
    public final Stack<Object> stack = new Stack<Object>();

    /**
     * This map maps a set containing the keys of a record to the record itself.
     * The records are used as prototypes for creating new records.
     */
    public final Map<Set<String>, Record> prototypes = new HashMap<Set<String>, Record>();

    /**
     * {@inheritDoc}
     */
    @Override
    public void visitUnknown(final ITreeNode node)
    {
        for (ITreeNode child : node.children())
        {
            visit(child);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit_null(final ITreeNode node)
    {
        stack.push(null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit_boolean(final ITreeNode node)
    {
        if (node.text().equalsIgnoreCase("true"))
        {
            stack.push(Boolean.TRUE);
        }
        else
        {
            stack.push(Boolean.FALSE);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit_number(final ITreeNode node)
    {
        final String text = node.text().trim();

        final BigDecimal number = F.parseBigDecimal(text);

        stack.push(number);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit_string(final ITreeNode node)
    {
        String text = node.text().trim();

        // TODO: Fix Grammar - capturing WS

        text = text.substring(1, text.length() - 1);

        stack.push(text);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit_pair(final ITreeNode node)
    {
        visitUnknown(node);

        final Object value = stack.pop();

        final Object key = stack.pop();

        final SimpleImmutableEntry entry = new SimpleImmutableEntry(key, value);

        stack.push(entry);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit_object(final ITreeNode node)
    {
        final int size = stack.size();

        visitUnknown(node);

        final Map<Object, Object> builder = new HashMap<Object, Object>();

        while (stack.size() > size)
        {
            final Entry entry = (Entry) stack.pop();

            builder.put(entry.getKey().toString(), entry.getValue());
        }

        if (prototypes.containsKey(builder.keySet()))
        {
            final Record prototype = prototypes.get(builder.keySet());

            final Record record = F.set(prototype, builder);

            stack.push(record);
        }
        else
        {
            stack.push(F.unmodifiable(builder));
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit_array(final ITreeNode node)
    {
        final int size = stack.size();

        visitUnknown(node);

        final List builder = new LinkedList();

        while (stack.size() > size)
        {
            builder.add(0, stack.pop());
        }

        stack.push(F.unmodifiable(builder));
    }
}