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

com.galenframework.parser.IndentationStructureParser Maven / Gradle / Ivy

There is a newer version: 2.4.4
Show newest version
/*******************************************************************************
* Copyright 2017 Ivan Shubin http://galenframework.com
* 
* 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.galenframework.parser;


import com.galenframework.specs.Place;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Stack;

public class IndentationStructureParser {
    public static final String COMMENT_SYMBOL = "#";
    private static final char SPACE = ' ';
    private static final char TAB = '\t';
    private static final int TAB_SIZE = 4;

    public List parse(String contentWithTabs) throws IOException {
        return parse(new ByteArrayInputStream(contentWithTabs.getBytes()), "");
    }


    private static class IndentationNode {
        private final StructNode parent;
        private final Integer indentation;
        private final StructNode structNode;
        private int childIndentation = 0;
        public IndentationNode(Integer indentation, StructNode structNode, StructNode parent) {
            this.indentation = indentation;
            this.structNode = structNode;
            this.parent = parent;
        }

        @Override
        public String toString() {
            return structNode.getName() + " #" + indentation;
        }
    }

    public List parse(InputStream stream, String source) throws IOException {
        Stack nodeStack = new Stack<>();


        StructNode rootNode = new StructNode();
        nodeStack.push(new IndentationNode(-1, rootNode, null));

        List lines = IOUtils.readLines(stream);

        int lineNumber = 0;
        for (String line : lines) {
            lineNumber++;
            if (isProcessable(line)) {
                processPlace(nodeStack, line, lineNumber, source);
            }
        }

        return rootNode.getChildNodes();
    }

    private void processPlace(Stack stack, String text, int lineNumber, String source) {
        StructNode newStructNode = new StructNode(text.trim());
        newStructNode.setPlace(new Place(source, lineNumber));

        int calculatedIndentation = calculateIndentation(text, lineNumber);

        while (calculatedIndentation <= stack.peek().indentation && stack.peek().parent != null) {
            stack.pop();
        }


        StructNode parent = stack.peek().structNode;

        if (parent.getChildNodes() != null && parent.getChildNodes().size() > 0
                && calculatedIndentation != stack.peek().childIndentation) {
            throw new SyntaxException(new Place(source, lineNumber), "Inconsistent indentation");
        }


        parent.addChildNode(newStructNode);
        stack.peek().childIndentation = calculatedIndentation;

        stack.push(new IndentationNode(calculatedIndentation, newStructNode, parent));
    }

    private int calculateIndentation(String line, int lineNumber) {
        int indentation = 0;
        char symbol;
        for (int i = 0; i < line.length(); i++) {
            symbol = line.charAt(i);
            if (symbol == SPACE) {
                indentation += 1;
            } else if (symbol == TAB) {
                indentation += TAB_SIZE;
            } else {
                return indentation;
            }
        }

        throw new SyntaxException(new Place(line, lineNumber), "This line does not have any text");
    }

    private boolean isProcessable(String line) {
        String trimmed = line.trim();
        return !(trimmed.isEmpty() || trimmed.startsWith(COMMENT_SYMBOL));
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy