org.raml.parser.visitor.YamlDocumentSuggester.java Source code

Java tutorial

Introduction

Here is the source code for org.raml.parser.visitor.YamlDocumentSuggester.java

Source

/*
 * Copyright (c) MuleSoft, 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 org.raml.parser.visitor;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

import org.apache.commons.lang.StringUtils;
import org.raml.emitter.RamlEmitter;
import org.raml.parser.builder.NodeBuilder;
import org.raml.parser.builder.TupleBuilder;
import org.raml.parser.completion.DefaultSuggestion;
import org.raml.parser.completion.NodeContext;
import org.raml.parser.completion.Suggestion;
import org.raml.parser.loader.DefaultResourceLoader;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

public class YamlDocumentSuggester implements NodeHandler {

    private YamlDocumentBuilder builder;
    private int offset;
    private Stack<NodeContext> nodes;

    public YamlDocumentSuggester(YamlDocumentBuilder builder) {

        this.builder = builder;
        this.nodes = new Stack<NodeContext>();
    }

    public List<Suggestion> suggest(String topSection, String context) {
        return suggest(topSection, context, null);
    }

    public List<Suggestion> suggest(String topSection, String context, String bottomSection) {

        final List<Suggestion> result = new ArrayList<Suggestion>();

        topSection = topSection.trim();
        this.offset = topSection.length();
        if (offset == 0) {
            result.add(new DefaultSuggestion(RamlEmitter.VERSION, 0));
            return result;
        }
        Yaml yamlParser = new Yaml();
        NodeVisitor nodeVisitor = new NodeVisitor(this, new DefaultResourceLoader());
        MappingNode rootNode = (MappingNode) yamlParser.compose(new StringReader(topSection));

        nodeVisitor.visitDocument(rootNode);

        int contextColumn = calculateContextColumn(context);

        NodeBuilder<?> parentNodeBuilder = null;
        NodeContext nodeContext = nodes.peek();
        while (!nodes.isEmpty()) {
            nodeContext = popNode();
            parentNodeBuilder = (NodeBuilder) this.builder.getBuilderContext().pop();
            if (nodeContext.getParentIndentation() < contextColumn) {
                break;
            }
        }
        if (!isContextInValue(context)) {
            addKeySuggestions(context, result, parentNodeBuilder, nodeContext);
        } else {
            //todo add value suggestions
        }

        Collections.sort(result);
        return result;
    }

    private void addKeySuggestions(String context, List<Suggestion> result, NodeBuilder<?> parent,
            NodeContext nodeContext) {
        if (parent instanceof TupleBuilder) {
            List<String> existingKeys = nodeContext.getKeys();
            Collection<TupleBuilder<?, ?>> childrenTupleBuilders = ((TupleBuilder<?, ?>) parent)
                    .getChildrenTupleBuilders();
            for (TupleBuilder<?, ?> childTupleBuilder : childrenTupleBuilders) {
                List<Suggestion> suggestions = childTupleBuilder.getHandler().getSuggestions();
                String contextTrimmed = context.trim();
                for (Suggestion suggestion : suggestions) {
                    if (suggestion.getLabel().startsWith(contextTrimmed)
                            && !existingKeys.contains(suggestion.getLabel())) {
                        suggestion.setIndentation(nodeContext.getSiblingsIndentation());
                        result.add(suggestion);
                    }
                }
            }
        }
    }

    private boolean isContextInValue(String context) {
        return context.contains(":");
    }

    private int calculateContextColumn(String context) {
        int column = 0;
        while (column < context.length() && StringUtils.isWhitespace(context.charAt(column) + "")) {
            column++;
        }
        return column;
    }

    private void pushNode(Node node, MappingNode mappingNode) {
        pushNode(node.getStartMark().getColumn(), mappingNode);
    }

    private void pushNode(int column, MappingNode mappingNode) {
        nodes.push(new NodeContext(column, mappingNode));
    }

    private NodeContext popNode() {
        return nodes.pop();
    }

    @Override
    public boolean onMappingNodeStart(MappingNode mappingNode, TupleType tupleType) {
        return builder.onMappingNodeStart(mappingNode, tupleType);
    }

    @Override
    public void onMappingNodeEnd(MappingNode mappingNode, TupleType tupleType) {
        builder.onMappingNodeEnd(mappingNode, tupleType);
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean onSequenceStart(SequenceNode node, TupleType tupleType) {
        pushNode(node, null);
        return builder.onSequenceStart(node, tupleType);
    }

    @Override
    public void onSequenceEnd(SequenceNode node, TupleType tupleType) {

        popNode();
        builder.onSequenceEnd(node, tupleType);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void onScalar(ScalarNode node, TupleType tupleType) {
        try {
            builder.onScalar(node, tupleType);
        } catch (Exception e) {
            //ignore
        }
    }

    @Override
    public boolean onDocumentStart(MappingNode node) {
        if (node == null) {
            pushNode(0, null);
        } else {
            pushNode(node, node);
        }
        return builder.onDocumentStart(node);
    }

    @Override
    public void onDocumentEnd(MappingNode node) {

    }

    @Override
    public void onTupleEnd(NodeTuple nodeTuple) {
        Node valueNode = nodeTuple.getValueNode();
        if (validateOffset(valueNode)) {
            popNode();
            builder.onTupleEnd(nodeTuple);
        }
    }

    private boolean validateOffset(Node valueNode) {
        return valueNode != null && valueNode.getEndMark().getIndex() < offset;
    }

    @Override
    public boolean onTupleStart(NodeTuple nodeTuple) {
        try {
            builder.onTupleStart(nodeTuple);
            MappingNode mapping = nodeTuple.getValueNode().getNodeId() == NodeId.mapping
                    ? (MappingNode) nodeTuple.getValueNode()
                    : null;
            pushNode(nodeTuple.getKeyNode(), mapping);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    public void onSequenceElementStart(Node sequenceNode) {
        builder.onSequenceElementStart(sequenceNode);
    }

    @Override
    public void onSequenceElementEnd(Node sequenceNode) {
        builder.onSequenceElementEnd(sequenceNode);
    }

    @Override
    public void onCustomTagStart(Tag tag, Node originalValueNode, Node node) {
        builder.onCustomTagStart(tag, originalValueNode, node);
    }

    @Override
    public void onCustomTagEnd(Tag tag, Node originalValueNode, Node node) {
        builder.onCustomTagEnd(tag, originalValueNode, node);
    }

    @Override
    public void onCustomTagError(Tag tag, Node node, String message) {
        builder.onCustomTagError(tag, node, message);
    }
}