package de.ugoe.cs.swe.bnftools.ui.formatter; import java.util.ArrayList; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.parsetree.AbstractNode; import org.eclipse.xtext.parsetree.CompositeNode; import org.eclipse.xtext.parsetree.LeafNode; import org.eclipse.xtext.parsetree.NodeUtil; import de.ugoe.cs.swe.bnftools.ebnf.Atom; import de.ugoe.cs.swe.bnftools.ebnf.BnfEntry; import de.ugoe.cs.swe.bnftools.ebnf.DefinitionList; import de.ugoe.cs.swe.bnftools.ebnf.DeltaEntry; import de.ugoe.cs.swe.bnftools.ebnf.EtsiBnf; import de.ugoe.cs.swe.bnftools.ebnf.ExtRule; import de.ugoe.cs.swe.bnftools.ebnf.GlobalCombinator; import de.ugoe.cs.swe.bnftools.ebnf.GroupedSequence; import de.ugoe.cs.swe.bnftools.ebnf.HookCombinator; import de.ugoe.cs.swe.bnftools.ebnf.Import; import de.ugoe.cs.swe.bnftools.ebnf.ImportSection; import de.ugoe.cs.swe.bnftools.ebnf.MergeEntry; import de.ugoe.cs.swe.bnftools.ebnf.MergeRule; import de.ugoe.cs.swe.bnftools.ebnf.OptionalSequence; import de.ugoe.cs.swe.bnftools.ebnf.RepeatedSequence; import de.ugoe.cs.swe.bnftools.ebnf.Rule; import de.ugoe.cs.swe.bnftools.ebnf.RuleCombinator; import de.ugoe.cs.swe.bnftools.ebnf.RuleReference; import de.ugoe.cs.swe.bnftools.ebnf.SectionHeading; import de.ugoe.cs.swe.bnftools.ebnf.SingleDefinition; import de.ugoe.cs.swe.bnftools.ebnf.StringRule; import de.ugoe.cs.swe.bnftools.ebnf.Term; import de.ugoe.cs.swe.bnftools.visitor.EbnfVisitor; public class EbnfFormatterVisitor extends EbnfVisitor { private StringBuffer buf; private FormatterConfig config; private int bufferPositionFormattedTextNoWhitespaces = 0; private int bufferPositionOriginalText = 0; private int allCommentsPosition = 0; private ArrayList allComments = new ArrayList(); private boolean lastWasSectionHeading=false; private CompositeNode parserEtsiBnfNode; private String originalText; private int bufferPositionFormattedText; private String formattedText; private String formattedTextNoWhitespaces; private String originalTextNoWhitespaces; public EbnfFormatterVisitor(EObject rootNode, FormatterConfig config) { super(rootNode); this.config = config; buf = new StringBuffer(); } public EbnfFormatterVisitor(FormatterConfig config) { this.config = config; buf = new StringBuffer(); } public StringBuffer getBuf() { return buf; } private boolean isCommentNode(LeafNode node) { if ((node.getText().trim().startsWith("//") || node.getText().trim().startsWith("/*")) && (node.isHidden())) return true; return false; } private void collectAllComments(CompositeNode node) { for (int i=0; i < node.getChildren().size(); i++) { AbstractNode currentNode = node.getChildren().get(i); if (currentNode instanceof LeafNode) { LeafNode leafNode = (LeafNode) currentNode; if (isCommentNode(leafNode)) { allComments.add(leafNode); } } if (currentNode instanceof CompositeNode) { collectAllComments((CompositeNode) currentNode); } } } private boolean isSingleLineComment(String str) { if (str.startsWith("//")) return true; return false; } private boolean isMultiLineComment(String str) { if (str.startsWith("/*")) return true; return false; } private boolean isWhitespace(char ch) { if ((ch==' ') || (ch == '\t') || (ch == '\n') || (ch == '\r')) return true; return false; } private void skipWhitespacesOriginalText() { while (bufferPositionOriginalText < originalText.length() && isWhitespace(originalText.charAt(bufferPositionOriginalText))) { bufferPositionOriginalText++; } } private void skipWhitespacesFormattedText(StringBuffer result) { while (bufferPositionFormattedText < formattedText.length() && isWhitespace(formattedText.charAt(bufferPositionFormattedText))) { result.append(formattedText.substring(bufferPositionFormattedText, bufferPositionFormattedText+1)); bufferPositionFormattedText++; } } private boolean isSingleLineCommentNext(String str, int position) { if ((str.charAt(position) == '/') && (str.charAt(position) == '/')) return true; return false; } private boolean isMultiLineCommentNext(String str, int position) { if ((str.charAt(position) == '/') && (str.charAt(position) == '*')) return true; return false; } private boolean isCommentNext(String str, int position) { if (isSingleLineCommentNext(str, position) || isMultiLineCommentNext(str, position)) return true; else return false; } private String scanBackWhitespaces(String str, int position) { StringBuffer whiteSpaces = new StringBuffer(); int currentPosition = position; while (isWhitespace(str.charAt(currentPosition))) { whiteSpaces.append(str.charAt(currentPosition)); currentPosition--; } return whiteSpaces.toString(); } private String stripEndingNewline(String str) { int position = str.length() - 1; while ((str.charAt(position) == '\n') || (str.charAt(position) == '\r')) { position--; } return str.substring(0, position + 1); } private int scanBackNewlinesCount(String str, int position) { int newLinesCount = 0; int currentPosition = position; while ((str.charAt(currentPosition) == '\n') || (str.charAt(currentPosition) == '\r')) { if (str.charAt(currentPosition) == '\n') { if (str.charAt(currentPosition - 1) == '\r') { currentPosition -= 2; } else { currentPosition -= 1; } newLinesCount++; } else if (str.charAt(currentPosition) == '\r') { currentPosition -= 1; newLinesCount++; } } return newLinesCount; } private void weaveComments() { // if (true) { // StringBuffer result = new StringBuffer(); // result.append(buf.toString()); // buf = result; // return; // } bufferPositionOriginalText = 0; bufferPositionFormattedTextNoWhitespaces = 0; bufferPositionFormattedText = 0; StringBuffer result = new StringBuffer(); formattedTextNoWhitespaces = buf.toString().replaceAll("[ \t\n\r]", ""); formattedText = buf.toString(); while (bufferPositionFormattedTextNoWhitespaces <= formattedTextNoWhitespaces.length()) { skipWhitespacesOriginalText(); skipWhitespacesFormattedText(result); if (!(bufferPositionOriginalText < originalText.length())) break; char formattedPositionNoWhitespaces; if (bufferPositionFormattedTextNoWhitespaces == formattedTextNoWhitespaces.length()) { formattedPositionNoWhitespaces = ' '; } else { formattedPositionNoWhitespaces = formattedTextNoWhitespaces.charAt(bufferPositionFormattedTextNoWhitespaces); } char originalPosition = originalText.charAt(bufferPositionOriginalText); if (formattedPositionNoWhitespaces != originalPosition) { if (formattedPositionNoWhitespaces == ';') { // formatted text always outputs the optional semicolon, skip it if necessary bufferPositionFormattedTextNoWhitespaces++; bufferPositionFormattedText++; } else if (isCommentNext(originalText, bufferPositionOriginalText)) { LeafNode currentComment = allComments.get(allCommentsPosition); if (currentComment.getTotalOffset() == bufferPositionOriginalText) { if (isMultiLineComment(currentComment.getText())) { int newLinesCount = scanBackNewlinesCount(originalText, bufferPositionOriginalText-1); if (newLinesCount > 0) { if (scanBackNewlinesCount(result.toString(), result.toString().length()-1) == 0) { result.append("\n\n"); } result.append(currentComment.getText()); result.append("\n"); } else { String lastWhiteSpaces = scanBackWhitespaces(result.toString(), result.toString().length()-1); result.delete(result.toString().length() - lastWhiteSpaces.length(), result.toString().length()); result.append(" " + stripEndingNewline(currentComment.getText())); result.append(lastWhiteSpaces); } } else if (isSingleLineComment(currentComment.getText())) { int newLinesCount = scanBackNewlinesCount(originalText, bufferPositionOriginalText-1); String lastWhiteSpaces = scanBackWhitespaces(result.toString(), result.toString().length()-1); result.delete(result.toString().length() - lastWhiteSpaces.length(), result.toString().length()); if (newLinesCount > 0) { result.append("\n\n" + stripEndingNewline(currentComment.getText())); } else { result.append(" " + stripEndingNewline(currentComment.getText())); } result.append(lastWhiteSpaces); } bufferPositionOriginalText+=currentComment.getLength(); allCommentsPosition++; } } else { // disaster handling: return original unformatted text! buf = new StringBuffer(); buf.append(originalText); return; } } else { result.append(formattedText.substring(bufferPositionFormattedText, bufferPositionFormattedText+1)); bufferPositionOriginalText++; bufferPositionFormattedText++; bufferPositionFormattedTextNoWhitespaces++; } } buf = result; } private void newLine() { buf.append("\n"); } private void text(String str) { buf.append(str); } private void space() { buf.append(" "); } // ----------------------------------------------------------------------------- protected void visitBefore(EtsiBnf node) { parserEtsiBnfNode = NodeUtil.getNodeAdapter(node).getParserNode(); collectAllComments(parserEtsiBnfNode); originalText = NodeUtil.getNodeAdapter(node).getParserNode().serialize(); originalTextNoWhitespaces = originalText.replaceAll("[ \t\n\r]", ""); //System.out.println(allComments.toString()); text("grammar " + node.getName()); if (node.getType() != null) text(node.getType()); text(";"); newLine(); newLine(); } protected void visitAfter(EtsiBnf node) { weaveComments(); } protected void visitBefore(ImportSection node) { } protected void visitAfter(ImportSection node) { newLine(); } protected void visitBefore(BnfEntry node) { } protected void visitAfter(BnfEntry node) { } protected void visitBefore(DeltaEntry node) { } protected void visitAfter(DeltaEntry node) { } protected void visitBefore(MergeEntry node) { } protected void visitAfter(MergeEntry node) { } protected void visitBefore(Atom node) { } protected void visitAfter(Atom node) { } protected void visitBefore(Term node) { } protected void visitAfter(Term node) { if (!isLastElement()) space(); } protected void visitBefore(DefinitionList node) { } protected void visitAfter(DefinitionList node) { } protected void visitBefore(ExtRule node) { } protected void visitAfter(ExtRule node) { } protected void visitBefore(GlobalCombinator node) { } protected void visitAfter(GlobalCombinator node) { } protected void visitBefore(GroupedSequence node) { text("("); } protected void visitAfter(GroupedSequence node) { text(")"); } protected void visitBefore(HookCombinator node) { } protected void visitAfter(HookCombinator node) { } protected void visitBefore(Import node) { text("import \"" + node.getImportURI() + "\";"); newLine(); } protected void visitAfter(Import node) { } protected void visitBefore(MergeRule node) { } protected void visitAfter(MergeRule node) { } protected void visitBefore(OptionalSequence node) { text("["); } protected void visitAfter(OptionalSequence node) { text("]"); } protected void visitBefore(RepeatedSequence node) { text("{"); } protected void visitAfter(RepeatedSequence node) { text("}"); if (node.isMorethanonce()) text("+"); } protected void visitBefore(Rule node) { if (lastWasSectionHeading) newLine(); lastWasSectionHeading=false; if (node.getRulenumber() > 0) text(node.getRulenumber() + ". "); text(node.getName() + " ::= "); } protected void visitAfter(Rule node) { text(";"); newLine(); } protected void visitBefore(RuleCombinator node) { } protected void visitAfter(RuleCombinator node) { } protected void visitBefore(RuleReference node) { text(node.getRuleref().getName()); } protected void visitAfter(RuleReference node) { } protected void visitBefore(SectionHeading node) { if (!lastWasSectionHeading && !buf.substring(buf.length()-2).equals("\n\n")) newLine(); lastWasSectionHeading=true; // if (!lastWasSectionHeading || !buf.substring(buf.length()-2).equals("\n\n")) // if (!buf.substring(buf.length()-2).equals("\n\n")) // buf.append("\n"); text(node.getSectionHeader()); } protected void visitAfter(SectionHeading node) { } protected void visitBefore(SingleDefinition node) { } protected void visitAfter(SingleDefinition node) { if (!isLastElement()) text(" | "); } protected void visitBefore(StringRule node) { if (node.getLiteral() != null) text("\"" + node.getLiteral() + "\""); else if (node.getColon() != null) text("\"\"\""); } protected void visitAfter(StringRule node) { } }