提交 3acbf0c2 authored 作者: Thomas Mueller's avatar Thomas Mueller

The BNF parser now uses the visitor pattern.

上级 d60981be
......@@ -18,7 +18,8 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>The methods of the CloseListener are added to the Trigger interface.
<ul><li>The BNF parser now uses the visitor pattern.
</li><li>The methods of the CloseListener are added to the Trigger interface.
The interface CloseListener is removed.
</li><li>Converting a UUID to bytes was incorrect. Because of that, updatable result sets on
tables with UUID primary key did not work.
......
......@@ -14,9 +14,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.StringTokenizer;
import org.h2.server.web.DbContextRule;
import org.h2.tools.Csv;
import org.h2.util.New;
......@@ -29,8 +27,6 @@ import org.h2.util.Utils;
*/
public class Bnf {
private final Random random = new Random();
/**
* The rule map. The key is lowercase, and all spaces
* are replaces with underscore.
......@@ -45,10 +41,6 @@ public class Bnf {
private ArrayList<RuleHead> statements;
private String currentTopic;
Bnf() {
random.setSeed(1);
}
/**
* Create an instance using the grammar specified in the CSV file.
*
......@@ -80,10 +72,6 @@ public class Bnf {
return head;
}
public Random getRandom() {
return random;
}
private void parse(Reader csv) throws SQLException, IOException {
Rule functions = null;
statements = New.arrayList();
......@@ -132,54 +120,13 @@ public class Bnf {
addFixedRule("@close_bracket@", RuleFixed.CLOSE_BRACKET);
}
/**
* Get the HTML railroad for a given syntax.
*
* @param bnf the syntax
* @return the HTML formatted railroad
*/
public String getRailroadHtml(String bnf) {
bnf = StringUtils.replaceAll(bnf, "\n ", " ");
String[] syntaxList = StringUtils.arraySplit(bnf, '\n', true);
StringBuilder buff = new StringBuilder();
for (String s : syntaxList) {
this.syntax = s;
tokens = tokenize();
index = 0;
Rule rule = parseRule();
rule.setLinks(ruleMap);
String html = rule.getHtmlRailroad(this, false);
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();
}
/**
* Get the HTML documentation for a given syntax.
*
* @param bnf the BNF syntax
* @return the HTML formatted text
*/
public String getSyntaxHtml(String bnf) {
bnf = StringUtils.replaceAll(bnf, "\n ", "\n");
StringTokenizer tokenizer = getTokenizer(bnf);
StringBuilder buff = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
String s = tokenizer.nextToken();
if (s.length() == 1 || StringUtils.toUpperEnglish(s).equals(s)) {
buff.append(StringUtils.xmlText(s));
continue;
}
buff.append(getLink(s));
}
String s = buff.toString();
// ensure it works within XHTML comments
s = StringUtils.replaceAll(s, "--", "&#45;-");
return s;
public void visit(BnfVisitor visitor, String s) {
this.syntax = s;
tokens = tokenize();
index = 0;
Rule rule = parseRule();
rule.setLinks(ruleMap);
rule.accept(visitor);
}
/**
......@@ -188,7 +135,7 @@ public class Bnf {
* @param token the token
* @return the rule map key
*/
static String getRuleMapKey(String token) {
public static String getRuleMapKey(String token) {
StringBuilder buff = new StringBuilder();
for (char ch : token.toCharArray()) {
if (Character.isUpperCase(ch)) {
......@@ -201,39 +148,13 @@ public class Bnf {
}
/**
* Get the HTML link for the given token, or the token itself if no link
* exists.
* Get the rule head for the given title.
*
* @param token the token
* @return the HTML link
* @param title
* @return the rule head, or null
*/
String getLink(String token) {
RuleHead found = null;
String key = getRuleMapKey(token);
for (int i = 0; i < token.length(); i++) {
String test = StringUtils.toLowerEnglish(key.substring(i));
RuleHead r = ruleMap.get(test);
if (r != null) {
found = r;
break;
}
}
if (found == null) {
return token;
}
String page = "grammar.html";
if (found.getSection().startsWith("Data Types")) {
page = "datatypes.html";
} else if (found.getSection().startsWith("Functions")) {
page = "functions.html";
} else if (token.equals("@func@")) {
return "<a href=\"functions.html\">Function</a>";
} else if (found.getRule() instanceof RuleFixed) {
return found.getRule().getHtmlRailroad(this, false);
}
String link = found.getTopic().toLowerCase().replace(' ', '_');
link = page + "#" + StringUtils.urlEncode(link);
return "<a href=\"" + link + "\">" + token + "</a>";
public RuleHead getRuleHead(String title) {
return ruleMap.get(title);
}
private Rule parseRule() {
......@@ -396,7 +317,13 @@ public class Bnf {
return statements;
}
private StringTokenizer getTokenizer(String s) {
/**
* Get the tokenizer for the given syntax.
*
* @param s the syntax
* @return the tokenizer
*/
public StringTokenizer getTokenizer(String s) {
return new StringTokenizer(s, " [](){}|.,\r\n<>:-+*/=<\">!'$", true);
}
......
/*
* Copyright 2004-2010 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.bnf;
import java.util.ArrayList;
/**
* The visitor interface for BNF rules.
*/
public interface BnfVisitor {
/**
* Visit a rule element.
*
* @param keyword whether this is a keyword
* @param name the element name
* @param link the linked rule if it's not a keyword
*/
void visitRuleElement(boolean keyword, String name, Rule link);
/**
* Visit a repeat rule.
*
* @param comma whether the comma is repeated as well
* @param rule the element to repeat
*/
void visitRuleRepeat(boolean comma, Rule rule);
/**
* Visit a fixed rule.
*
* @param type the type
*/
void visitRuleFixed(int type);
/**
* Visit a rule list.
*
* @param or true for OR, false for AND
* @param list the rules
*/
void visitRuleList(boolean or, ArrayList<Rule> list);
/**
* Visit an optional rule.
*
* @param rule the rule
*/
void visitRuleOptional(Rule rule);
}
......@@ -20,22 +20,6 @@ public interface Rule {
*/
String name();
/**
* Get a random entry.
*
* @param config the configuration
* @param level the call level
* @return the entry
*/
String random(Bnf config, int level);
/**
* Get the last entry.
*
* @return the last entry
*/
Rule last();
/**
* Update cross references.
*
......@@ -63,12 +47,10 @@ public interface Rule {
boolean matchRemove(Sentence sentence);
/**
* Get the HTML railroad.
* Call the visit method in the given visitor.
*
* @param config the configuration
* @param topLevel true if line break are permitted
* @return the railroad
* @param visitor the visitor
*/
String getHtmlRailroad(Bnf config, boolean topLevel);
void accept(BnfVisitor visitor);
}
......@@ -33,34 +33,14 @@ public class RuleElement implements Rule {
return name;
}
public String getHtmlRailroad(Bnf config, boolean topLevel) {
String x;
if (keyword) {
x = StringUtils.xmlText(name.trim());
} else {
x = config.getLink(name.trim());
}
return "<code class=\"c\">" + x + "</code>";
}
public String random(Bnf config, int level) {
if (keyword) {
return name.length() > 1 ? " " + name + " " : name;
}
if (link != null) {
return link.random(config, level + 1);
}
throw new AssertionError(name);
public void accept(BnfVisitor visitor) {
visitor.visitRuleElement(keyword, name, link);
}
public String name() {
return name;
}
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) {
if (link != null) {
link.setLinks(ruleMap);
......
......@@ -7,21 +7,20 @@
package org.h2.bnf;
import java.util.HashMap;
import java.util.Random;
/**
* Represents a hard coded terminal rule in a BNF object.
*/
public class RuleFixed implements Rule {
static final int YMD = 0, HMS = 1, NANOS = 2;
static final int ANY_EXCEPT_SINGLE_QUOTE = 3;
static final int ANY_EXCEPT_DOUBLE_QUOTE = 4;
static final int ANY_UNTIL_EOL = 5;
static final int ANY_UNTIL_END = 6;
static final int ANY_WORD = 7;
static final int ANY_EXCEPT_2_DOLLAR = 8;
static final int HEX_START = 10, CONCAT = 11, AZ_UNDERSCORE = 12, AF = 13, DIGIT = 14;
static final int OPEN_BRACKET = 15, CLOSE_BRACKET = 16;
public static final int YMD = 0, HMS = 1, NANOS = 2;
public static final int ANY_EXCEPT_SINGLE_QUOTE = 3;
public static final int ANY_EXCEPT_DOUBLE_QUOTE = 4;
public static final int ANY_UNTIL_EOL = 5;
public static final int ANY_UNTIL_END = 6;
public static final int ANY_WORD = 7;
public static final int ANY_EXCEPT_2_DOLLAR = 8;
public static final int HEX_START = 10, CONCAT = 11, AZ_UNDERSCORE = 12, AF = 13, DIGIT = 14;
public static final int OPEN_BRACKET = 15, CLOSE_BRACKET = 16;
private final int type;
......@@ -64,94 +63,14 @@ public class RuleFixed implements Rule {
}
}
public String getHtmlRailroad(Bnf config, boolean topLevel) {
return getHtmlText();
}
public String getHtmlText() {
switch(type) {
case YMD:
return "2000-01-01";
case HMS:
return "12:00:00";
case NANOS:
return "000000000";
case ANY_UNTIL_EOL:
case ANY_EXCEPT_SINGLE_QUOTE:
case ANY_EXCEPT_DOUBLE_QUOTE:
case ANY_WORD:
case ANY_EXCEPT_2_DOLLAR:
case ANY_UNTIL_END: {
return "anything";
}
case HEX_START:
return "0x";
case CONCAT:
return "||";
case AZ_UNDERSCORE:
return "A-Z | _";
case AF:
return "A-F";
case DIGIT:
return "0-9";
case OPEN_BRACKET:
return "[";
case CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
}
public String random(Bnf config, int level) {
Random r = config.getRandom();
switch (type) {
case YMD:
return (1800 + r.nextInt(200)) + "-" + (1 + r.nextInt(12)) + "-" + (1 + r.nextInt(31));
case HMS:
return (r.nextInt(24)) + "-" + (r.nextInt(60)) + "-" + (r.nextInt(60));
case NANOS:
return "" + (r.nextInt(100000) + r.nextInt(10000));
case ANY_UNTIL_EOL:
case ANY_EXCEPT_SINGLE_QUOTE:
case ANY_EXCEPT_DOUBLE_QUOTE:
case ANY_WORD:
case ANY_EXCEPT_2_DOLLAR:
case ANY_UNTIL_END: {
StringBuilder buff = new StringBuilder();
int len = r.nextBoolean() ? 1 : r.nextInt(5);
for (int i = 0; i < len; i++) {
buff.append((char) ('A' + r.nextInt('C' - 'A')));
}
return buff.toString();
}
case HEX_START:
return "0x";
case CONCAT:
return "||";
case AZ_UNDERSCORE:
return "" + (char) ('A' + r.nextInt('C' - 'A'));
case AF:
return "" + (char) ('A' + r.nextInt('F' - 'A'));
case DIGIT:
return "" + (char) ('0' + r.nextInt(10));
case OPEN_BRACKET:
return "[";
case CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
public void accept(BnfVisitor visitor) {
visitor.visitRuleFixed(type);
}
public String name() {
return "type="+type;
}
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) {
// nothing to do
}
......
......@@ -53,71 +53,14 @@ public class RuleList implements Rule {
return buff.toString();
}
public String getHtmlRailroad(Bnf config, boolean topLevel) {
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\">");
buff.append(r.getHtmlRailroad(config, false));
buff.append("</td><td class=\"" + a + "e\"></td></tr>");
}
buff.append("</table>");
} else {
if (!topLevel) {
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\">");
}
for (Rule r : list) {
if (!topLevel) {
buff.append("<td class=\"d\">");
}
buff.append(r.getHtmlRailroad(config, false));
if (!topLevel) {
buff.append("</td>");
}
}
if (!topLevel) {
buff.append("</tr></table>");
}
}
return buff.toString();
}
public String random(Bnf config, int level) {
if (or) {
if (level > 10) {
if (level > 1000) {
// better than stack overflow
throw new AssertionError();
}
return get(0).random(config, level);
}
int idx = config.getRandom().nextInt(list.size());
return get(idx).random(config, level + 1);
}
StringBuilder buff = new StringBuilder();
for (Rule r : list) {
buff.append(r.random(config, level+1));
}
return buff.toString();
}
private Rule get(int idx) {
return list.get(idx);
public void accept(BnfVisitor visitor) {
visitor.visitRuleList(or, list);
}
public String name() {
return null;
}
public Rule last() {
return get(list.size() - 1);
}
public void setLinks(HashMap<String, RuleHead> ruleMap) {
if (!mapSet) {
for (Rule r : list) {
......
......@@ -23,31 +23,14 @@ public class RuleOptional implements Rule {
return "[" + rule.toString() + "]";
}
public String getHtmlRailroad(Bnf config, boolean topLevel) {
StringBuilder buff = new StringBuilder();
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\"><td class=\"ts\"></td><td class=\"d\">&nbsp;</td><td class=\"te\"></td></tr>");
buff.append("<tr class=\"railroad\"><td class=\"ls\"></td><td class=\"d\">");
buff.append(rule.getHtmlRailroad(config, false));
buff.append("</td><td class=\"le\"></td></tr></table>");
return buff.toString();
public void accept(BnfVisitor visitor) {
visitor.visitRuleOptional(rule);
}
public String name() {
return null;
}
public String random(Bnf config, int level) {
if (level > 10 ? config.getRandom().nextInt(level) == 1 : config.getRandom().nextInt(4) == 1) {
return rule.random(config, level + 1);
}
return "";
}
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) {
if (!mapSet) {
rule.setLinks(ruleMap);
......
......@@ -13,8 +13,6 @@ import java.util.HashMap;
*/
public class RuleRepeat implements Rule {
private static final boolean RAILROAD_DOTS = true;
private final Rule rule;
private final boolean comma;
......@@ -27,43 +25,18 @@ public class RuleRepeat implements Rule {
return "...";
}
public String getHtmlRailroad(Bnf config, boolean topLevel) {
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\">");
buff.append(rule.getHtmlRailroad(config, false));
buff.append("</td><td class=\"ts\"></td></tr>");
buff.append("<tr class=\"railroad\"><td class=\"ls\"></td>");
buff.append("<td class=\"d\">&nbsp;</td>");
buff.append("<td class=\"le\"></td></tr></table>");
}
return buff.toString();
public void accept(BnfVisitor visitor) {
visitor.visitRuleRepeat(comma, rule);
}
public String name() {
return rule.name();
}
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) {
// rule.setLinks(ruleMap);
}
public String random(Bnf config, int level) {
return rule.random(config, level);
}
public boolean matchRemove(Sentence sentence) {
if (sentence.shouldStop()) {
return false;
......
......@@ -9,8 +9,7 @@ package org.h2.server.web;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.h2.bnf.Bnf;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule;
import org.h2.bnf.RuleHead;
import org.h2.bnf.Sentence;
......@@ -61,14 +60,6 @@ public class DbContextRule implements Rule {
return null;
}
public String random(Bnf config, int level) {
return null;
}
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) {
// nothing to do
}
......@@ -477,8 +468,8 @@ public class DbContextRule implements Rule {
return best;
}
public String getHtmlRailroad(Bnf config, boolean topLevel) {
return null;
public void accept(BnfVisitor visitor) {
// nothing to do
}
}
......@@ -288,6 +288,7 @@ java org.h2.test.TestAll timer
/*
Row.getMemorySize
FileStore.sync, Database.sync() (CHECKPOINT SYNC)
H2 Console: use reflection for @INFO; maybe compiler for loop
document in performance section:
PreparedStatement prep = conn.prepareStatement(
......
/*
* Copyright 2004-2010 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.test.synth;
import java.util.ArrayList;
import java.util.Random;
import org.h2.bnf.Bnf;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule;
import org.h2.bnf.RuleFixed;
import org.h2.bnf.RuleHead;
import org.h2.util.New;
/**
* A BNF visitor that generates a random SQL statement.
*/
public class BnfRandom implements BnfVisitor {
private static final boolean SHOW_SYNTAX = false;
private final Bnf config;
private final Random random = new Random();
private final ArrayList<RuleHead> statements = New.arrayList();
private int level;
private String sql;
BnfRandom() throws Exception {
config = Bnf.getInstance(null);
config.linkStatements();
ArrayList<RuleHead> all = config.getStatements();
// go backwards so we can append at the end
for (int i = all.size() - 1; i >= 0; i--) {
RuleHead r = all.get(i);
String topic = r.getTopic().toLowerCase();
int weight = 0;
if (topic.equals("select")) {
weight = 10;
} else if (topic.equals("create table")) {
weight = 20;
} else if (topic.equals("insert")) {
weight = 5;
} else if (topic.startsWith("update")) {
weight = 3;
} else if (topic.startsWith("delete")) {
weight = 3;
} else if (topic.startsWith("drop")) {
weight = 2;
}
if (SHOW_SYNTAX) {
System.out.println(r.getTopic());
}
for (int j = 0; j < weight; j++) {
statements.add(r);
}
}
}
public String getRandomSQL() {
int sid = random.nextInt(statements.size());
RuleHead r = statements.get(sid);
level = 0;
r.getRule().accept(this);
sql = sql.trim();
if (sql.length() > 0) {
if (sql.indexOf("TRACE_LEVEL_") < 0 && sql.indexOf("COLLATION") < 0
&& sql.indexOf("SCRIPT ") < 0 && sql.indexOf("CSVWRITE") < 0
&& sql.indexOf("BACKUP") < 0 && sql.indexOf("DB_CLOSE_DELAY") < 0) {
if (SHOW_SYNTAX) {
System.out.println(" " + sql);
}
return sql;
}
}
return null;
}
public void visitRuleElement(boolean keyword, String name, Rule link) {
if (keyword) {
sql = name.length() > 1 ? " " + name + " " : name;
} else if (link != null) {
level++;
link.accept(this);
level--;
} else {
throw new AssertionError(name);
}
}
public void visitRuleFixed(int type) {
sql = getRandomFixed(type);
}
private String getRandomFixed(int type) {
Random r = random;
switch (type) {
case RuleFixed.YMD:
return (1800 + r.nextInt(200)) + "-" + (1 + r.nextInt(12)) + "-" + (1 + r.nextInt(31));
case RuleFixed.HMS:
return (r.nextInt(24)) + "-" + (r.nextInt(60)) + "-" + (r.nextInt(60));
case RuleFixed.NANOS:
return "" + (r.nextInt(100000) + r.nextInt(10000));
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: {
StringBuilder buff = new StringBuilder();
int len = r.nextBoolean() ? 1 : r.nextInt(5);
for (int i = 0; i < len; i++) {
buff.append((char) ('A' + r.nextInt('C' - 'A')));
}
return buff.toString();
}
case RuleFixed.HEX_START:
return "0x";
case RuleFixed.CONCAT:
return "||";
case RuleFixed.AZ_UNDERSCORE:
return "" + (char) ('A' + r.nextInt('C' - 'A'));
case RuleFixed.AF:
return "" + (char) ('A' + r.nextInt('F' - 'A'));
case RuleFixed.DIGIT:
return "" + (char) ('0' + r.nextInt(10));
case RuleFixed.OPEN_BRACKET:
return "[";
case RuleFixed.CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
}
public void visitRuleList(boolean or, ArrayList<Rule> list) {
if (or) {
if (level > 10) {
if (level > 1000) {
// better than stack overflow
throw new AssertionError();
}
list.get(0).accept(this);
return;
}
int idx = random.nextInt(list.size());
level++;
list.get(idx).accept(this);
level--;
return;
}
StringBuilder buff = new StringBuilder();
level++;
for (Rule r : list) {
r.accept(this);
buff.append(sql);
}
level--;
sql = buff.toString();
}
public void visitRuleOptional(Rule rule) {
if (level > 10 ? random.nextInt(level) == 1 : random.nextInt(4) == 1) {
level++;
rule.accept(this);
level--;
return;
}
sql = "";
}
public void visitRuleRepeat(boolean comma, Rule rule) {
rule.accept(this);
}
public void setSeed(int seed) {
random.setSeed(seed);
}
public int getStatementCount() {
return statements.size();
}
}
......@@ -9,10 +9,6 @@ package org.h2.test.synth;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import org.h2.bnf.Bnf;
import org.h2.bnf.RuleHead;
import org.h2.constant.SysProperties;
import org.h2.store.fs.FileSystemMemory;
import org.h2.test.TestAll;
......@@ -26,11 +22,9 @@ import org.h2.util.MathUtils;
public class TestRandomSQL extends TestBase {
private int dbId;
private boolean showSQL;
private ArrayList<RuleHead> statements;
private int seed;
private boolean exitOnError = true;
private Bnf bnfSyntax;
private BnfRandom bnfRandom;
private int success, total;
/**
......@@ -99,40 +93,12 @@ public class TestRandomSQL extends TestBase {
public TestBase init(TestAll conf) throws Exception {
super.init(conf);
bnfSyntax = Bnf.getInstance(null);
bnfSyntax.linkStatements();
statements = bnfSyntax.getStatements();
// go backwards so we can append at the end
for (int i = statements.size() - 1; i >= 0; i--) {
RuleHead r = statements.get(i);
String topic = r.getTopic();
int weight = 0;
if (topic.equals("select")) {
weight = 10;
} else if (topic.equals("createtable")) {
weight = 20;
} else if (topic.equals("insert")) {
weight = 5;
} else if (topic.startsWith("update")) {
weight = 3;
} else if (topic.startsWith("delete")) {
weight = 3;
} else if (topic.startsWith("drop")) {
weight = 2;
}
if (showSQL) {
System.out.println(r.getTopic());
}
for (int j = 0; j < weight; j++) {
statements.add(r);
}
}
bnfRandom = new BnfRandom();
return this;
}
private void testWithSeed(Bnf bnf) throws SQLException {
bnf.getRandom().setSeed(seed);
private void testWithSeed() throws SQLException {
bnfRandom.setSeed(seed);
Connection conn = null;
try {
conn = connect();
......@@ -141,28 +107,20 @@ public class TestRandomSQL extends TestBase {
conn = connect();
}
Statement stat = conn.createStatement();
for (int i = 0; i < statements.size(); i++) {
int sid = bnf.getRandom().nextInt(statements.size());
RuleHead r = statements.get(sid);
String rand = r.getRule().random(bnf, 0).trim();
if (rand.length() > 0) {
for (int i = 0; i < bnfRandom.getStatementCount(); i++) {
String sql = bnfRandom.getRandomSQL();
if (sql != null) {
try {
Thread.yield();
if (rand.indexOf("TRACE_LEVEL_") < 0 && rand.indexOf("COLLATION") < 0
&& rand.indexOf("SCRIPT ") < 0 && rand.indexOf("CSVWRITE") < 0
&& rand.indexOf("BACKUP") < 0 && rand.indexOf("DB_CLOSE_DELAY") < 0) {
if (showSQL) {
System.out.println(i + " " + rand);
}
total++;
if (total % 100 == 0) {
printTime("total: " + total + " success: " + (100 * success / total) + "%");
}
stat.execute(rand);
success++;
total++;
if (total % 100 == 0) {
printTime("total: " + total + " success: " + (100 * success / total) + "%");
}
stat.execute(sql);
success++;
} catch (SQLException e) {
processException(rand, e);
processException(sql, e);
}
}
}
......@@ -184,7 +142,7 @@ public class TestRandomSQL extends TestBase {
} catch (SQLException e) {
processException("deleteDb", e);
}
testWithSeed(bnfSyntax);
testWithSeed();
} finally {
System.setProperty(SysProperties.H2_SCRIPT_DIRECTORY, old);
}
......@@ -201,7 +159,6 @@ public class TestRandomSQL extends TestBase {
}
int len = getSize(2, 6);
exitOnError = false;
showSQL = false;
for (int a = 0; a < len; a++) {
int s = MathUtils.randomInt(Integer.MAX_VALUE);
testCase(s);
......
/*
* Copyright 2004-2010 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\">&nbsp;</td>");
buff.append("<td class=\"le\"></td></tr></table>");
}
html = buff.toString();
}
public void visitRuleFixed(int type) {
html = getHtmlText(type);
}
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\">&nbsp;</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();
}
}
/*
* Copyright 2004-2010 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 java.util.StringTokenizer;
import org.h2.bnf.Bnf;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule;
import org.h2.bnf.RuleFixed;
import org.h2.bnf.RuleHead;
import org.h2.util.StringUtils;
/**
* A BNF visitor that generates BNF in HTML form.
*/
public class BnfSyntax implements BnfVisitor {
private String html;
public String getHtml(Bnf bnf, String syntaxLines) {
syntaxLines = StringUtils.replaceAll(syntaxLines, "\n ", "\n");
StringTokenizer tokenizer = bnf.getTokenizer(syntaxLines);
StringBuilder buff = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
String s = tokenizer.nextToken();
if (s.length() == 1 || StringUtils.toUpperEnglish(s).equals(s)) {
buff.append(StringUtils.xmlText(s));
continue;
}
buff.append(getLink(bnf, s));
}
String s = buff.toString();
// ensure it works within XHTML comments
s = StringUtils.replaceAll(s, "--", "&#45;-");
return s;
}
String getLink(Bnf bnf, String token) {
RuleHead found = null;
String key = Bnf.getRuleMapKey(token);
for (int i = 0; i < token.length(); i++) {
String test = StringUtils.toLowerEnglish(key.substring(i));
RuleHead r = bnf.getRuleHead(test);
if (r != null) {
found = r;
break;
}
}
if (found == null) {
return token;
}
String page = "grammar.html";
if (found.getSection().startsWith("Data Types")) {
page = "datatypes.html";
} else if (found.getSection().startsWith("Functions")) {
page = "functions.html";
} else if (token.equals("@func@")) {
return "<a href=\"functions.html\">Function</a>";
} else if (found.getRule() instanceof RuleFixed) {
found.getRule().accept(this);
return html;
}
String link = found.getTopic().toLowerCase().replace(' ', '_');
link = page + "#" + StringUtils.urlEncode(link);
return "<a href=\"" + link + "\">" + token + "</a>";
}
public void visitRuleElement(boolean keyword, String name, Rule link) {
// not used
}
public void visitRuleFixed(int type) {
html = BnfRailroad.getHtmlText(type);
}
public void visitRuleList(boolean or, ArrayList<Rule> list) {
// not used
}
public void visitRuleOptional(Rule rule) {
// not used
}
public void visitRuleRepeat(boolean comma, Rule rule) {
// not used
}
}
......@@ -137,11 +137,13 @@ public class GenerateDoc {
String topic = rs.getString("TOPIC");
String syntax = rs.getString("SYNTAX").trim();
if (railroads) {
String railroad = bnf.getRailroadHtml(syntax);
BnfRailroad r = new BnfRailroad();
String railroad = r.getHtml(bnf, syntax);
map.put("railroad", railroad);
}
syntax = bnf.getSyntaxHtml(syntax);
map.put("syntax", syntax);
BnfSyntax visitor = new BnfSyntax();
String syntaxHtml = visitor.getHtml(bnf, syntax);
map.put("syntax", syntaxHtml);
// remove newlines in the regular text
String text = map.get("text");
......
......@@ -18,6 +18,8 @@ import java.util.HashMap;
import java.util.List;
import org.h2.bnf.Bnf;
import org.h2.build.BuildBase;
import org.h2.build.doc.BnfRailroad;
import org.h2.build.doc.BnfSyntax;
import org.h2.build.doc.RailroadImages;
import org.h2.server.web.PageParser;
import org.h2.tools.Csv;
......@@ -88,12 +90,13 @@ public class Railroads {
String topic = rs.getString("TOPIC");
String syntax = rs.getString("SYNTAX").trim();
if (railroads) {
String railroad = bnf.getRailroadHtml(syntax);
BnfRailroad r = new BnfRailroad();
String railroad = r.getHtml(bnf, syntax);
map.put("railroad", railroad);
}
syntax = bnf.getSyntaxHtml(syntax);
map.put("syntax", syntax);
BnfSyntax visitor = new BnfSyntax();
String syntaxHtml = visitor.getHtml(bnf, syntax);
map.put("syntax", syntaxHtml);
// remove newlines in the regular text
String text = map.get("text");
if (text != null) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论