/* * Copyright (C) 2010 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package lombok.ast.grammar; import java.util.List; import java.util.Stack; import com.google.common.collect.Lists; import lombok.ast.AstException; import lombok.ast.Node; import lombok.ast.Position; import lombok.ast.printer.TextFormatter; public class PositionCheckingFormatter extends TextFormatter { private Stack<Node> nodeStack = new Stack<Node>(); private List<AstException> problems = Lists.newArrayList(); private Source source; public PositionCheckingFormatter(Source source) { this.source = source; if ("\r\n".equals(System.getProperty("line.separator", "\n")) && source.getRawInput().contains("\r\n")) { setNewlineText("\r\n"); } } public List<AstException> getProblems() { List<AstException> p = Lists.newArrayList(problems); if (!nodeStack.isEmpty()) p.add(new AstException(nodeStack.peek(), "This node was never closed")); return p; } private void registerNode(Node node) { nodeStack.add(node); if (node == null) return; Position p = node.getPosition(); if (p.isUnplaced()) problems.add(new AstException(node, String.format( "this node is unplaced [%s]", node.getClass().getSimpleName()))); else { int delta = p.getStart() - getCurrentPosition(true); if (delta != 0) { int actualPos = getCurrentPosition(true); int repPos = p.getStart(); reportError(node, "start", actualPos, repPos); } } } @Override public void buildBlock(Node node) { super.buildBlock(node); registerNode(node); } @Override public void buildInline(Node node) { super.buildInline(node); registerNode(node); } @Override public void closeBlock() { super.closeBlock(); checkNodeClose(); } @Override public void closeInline() { super.closeInline(); checkNodeClose(); } private void checkNodeClose() { Node node = nodeStack.pop(); if (node == null) return; Position p = node.getPosition(); if (p.isUnplaced()) return; //opener already generated error. if (p.getStart() > p.getEnd()) problems.add(new AstException(node, "Node ends before it starts.")); int delta = p.getEnd() - Math.max(getCurrentPosition(false), p.getStart()); if (delta != 0) { int actualPos = getCurrentPosition(false); int repPos = p.getEnd(); reportError(node, "end", actualPos, repPos); } } private static final int RANGE = 12; private void reportError(Node node, String description, int actualPos, int repPos) { int delta = repPos - actualPos; String raw = source.getRawInput(); String actualPrefix = getCharsBeforePosition(actualPos, raw); String actualPostfix = getCharsAfterPosition(actualPos, raw); String reportedPrefix = getCharsBeforePosition(repPos, raw); String reportedPostfix = getCharsAfterPosition(repPos, raw); problems.add(new AstException(node, String.format( "this[%s] node's " + description + " is misreported with %+d\nactual(%d): \"%s%s\"\nreported(%d): \"%s%s\"\n%s", node.getClass().getSimpleName(), delta, actualPos, actualPrefix, actualPostfix, repPos, reportedPrefix, reportedPostfix, node))); } private String getCharsAfterPosition(int pos, String raw) { int max = raw.length(); if (pos < 0) pos = 0; return pos >= max ? "" : raw.substring(pos, Math.min(pos + RANGE, max)); } private String getCharsBeforePosition(int pos, String raw) { int max = raw.length(); return pos > RANGE ? raw.substring( Math.min(max, pos-RANGE), Math.min(max, pos)) : raw.substring(0, Math.min(max, pos)); } }