com.galenframework.parser.IndentationStructureParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of galen-core Show documentation
Show all versions of galen-core Show documentation
A library for layout testing of websites
/*******************************************************************************
* 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));
}
}