/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.build.doc;
import java.util.ArrayList;
import org.h2.bnf.Bnf;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule;
import org.h2.bnf.RuleFixed;
import org.h2.util.StringUtils;
/**
* A BNF visitor that generates HTML railroad diagrams.
*/
public class BnfRailroad implements BnfVisitor {
private static final boolean RAILROAD_DOTS = true;
private BnfSyntax syntaxVisitor;
private Bnf config;
private String html;
/**
* Generate the HTML for the given syntax.
*
* @param bnf the BNF parser
* @param syntaxLines the syntax
* @return the HTML
*/
public String getHtml(Bnf bnf, String syntaxLines) {
syntaxVisitor = new BnfSyntax();
this.config = bnf;
syntaxLines = StringUtils.replaceAll(syntaxLines, "\n ", " ");
String[] syntaxList = StringUtils.arraySplit(syntaxLines, '\n', true);
StringBuilder buff = new StringBuilder();
for (String s : syntaxList) {
bnf.visit(this, s);
html = StringUtils.replaceAll(html, "</code></td><td class=\"d\"><code class=\"c\">", " ");
if (buff.length() > 0) {
buff.append("<br />");
}
buff.append(html);
}
return buff.toString();
}
public void visitRuleElement(boolean keyword, String name, Rule link) {
String x;
if (keyword) {
x = StringUtils.xmlText(name.trim());
} else {
x = syntaxVisitor.getLink(config, name.trim());
}
html = "<code class=\"c\">" + x + "</code>";
}
public void visitRuleRepeat(boolean comma, Rule rule) {
StringBuilder buff = new StringBuilder();
if (RAILROAD_DOTS) {
buff.append("<code class=\"c\">");
if (comma) {
buff.append(", ");
}
buff.append("...</code>");
} else {
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\"><td class=\"te\"></td>");
buff.append("<td class=\"d\">");
rule.accept(this);
buff.append(html);
buff.append("</td><td class=\"ts\"></td></tr>");
buff.append("<tr class=\"railroad\"><td class=\"ls\"></td>");
buff.append("<td class=\"d\"> </td>");
buff.append("<td class=\"le\"></td></tr></table>");
}
html = buff.toString();
}
public void visitRuleFixed(int type) {
html = getHtmlText(type);
}
/**
* Get the HTML text for the given fixed rule.
*
* @param type the fixed rule type
* @return the HTML text
*/
static String getHtmlText(int type) {
switch(type) {
case RuleFixed.YMD:
return "2000-01-01";
case RuleFixed.HMS:
return "12:00:00";
case RuleFixed.NANOS:
return "000000000";
case RuleFixed.ANY_UNTIL_EOL:
case RuleFixed.ANY_EXCEPT_SINGLE_QUOTE:
case RuleFixed.ANY_EXCEPT_DOUBLE_QUOTE:
case RuleFixed.ANY_WORD:
case RuleFixed.ANY_EXCEPT_2_DOLLAR:
case RuleFixed.ANY_UNTIL_END: {
return "anything";
}
case RuleFixed.HEX_START:
return "0x";
case RuleFixed.CONCAT:
return "||";
case RuleFixed.AZ_UNDERSCORE:
return "A-Z | _";
case RuleFixed.AF:
return "A-F";
case RuleFixed.DIGIT:
return "0-9";
case RuleFixed.OPEN_BRACKET:
return "[";
case RuleFixed.CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
}
public void visitRuleList(boolean or, ArrayList<Rule> list) {
StringBuilder buff = new StringBuilder();
if (or) {
buff.append("<table class=\"railroad\">");
int i = 0;
for (Rule r : list) {
String a = i == 0 ? "t" : i == list.size() - 1 ? "l" : "k";
i++;
buff.append("<tr class=\"railroad\"><td class=\"" + a + "s\"></td><td class=\"d\">");
r.accept(this);
buff.append(html);
buff.append("</td><td class=\"" + a + "e\"></td></tr>");
}
buff.append("</table>");
} else {
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\">");
for (Rule r : list) {
buff.append("<td class=\"d\">");
r.accept(this);
buff.append(html);
buff.append("</td>");
}
buff.append("</tr></table>");
}
html = buff.toString();
}
public void visitRuleOptional(Rule rule) {
StringBuilder buff = new StringBuilder();
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\"><td class=\"ts\"></td><td class=\"d\"> </td><td class=\"te\"></td></tr>");
buff.append("<tr class=\"railroad\"><td class=\"ls\"></td><td class=\"d\">");
rule.accept(this);
buff.append(html);
buff.append("</td><td class=\"le\"></td></tr></table>");
html = buff.toString();
}
}