/* * 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.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.h2.bnf.Bnf; import org.h2.engine.Constants; import org.h2.server.web.PageParser; import org.h2.util.IOUtils; import org.h2.util.JdbcUtils; import org.h2.util.StringUtils; /** * This application generates sections of the documentation * by converting the built-in help section (INFORMATION_SCHEMA.HELP) * to cross linked html. */ public class GenerateDoc { private String inDir = "src/docsrc/html"; private String inHelp = "src/docsrc/help/help.csv"; private String outDir = "docs/html"; private Connection conn; private HashMap<String, Object> session = new HashMap<String, Object>(); private Bnf bnf; /** * This method is called when executing this application from the command * line. * * @param args the command line parameters */ public static void main(String... args) throws Exception { new GenerateDoc().run(args); } private void run(String... args) throws Exception { for (int i = 0; i < args.length; i++) { if (args[i].equals("-in")) { inDir = args[++i]; } else if (args[i].equals("-out")) { outDir = args[++i]; } } Class.forName("org.h2.Driver"); conn = DriverManager.getConnection("jdbc:h2:mem:"); new File(outDir).mkdirs(); new RailroadImages().run(outDir + "/images"); bnf = Bnf.getInstance(null); bnf.linkStatements(); session.put("version", Constants.getVersion()); session.put("versionDate", Constants.BUILD_DATE); session.put("stableVersion", Constants.getVersionStable()); session.put("stableVersionDate", Constants.BUILD_DATE_STABLE); // String help = "SELECT * FROM INFORMATION_SCHEMA.HELP WHERE SECTION"; String help = "SELECT ROWNUM ID, * FROM CSVREAD('" + inHelp + "', NULL, 'lineComment=#') WHERE SECTION "; map("commands", help + "LIKE 'Commands%' ORDER BY ID", true); map("commandsDML", help + "= 'Commands (DML)' ORDER BY ID", false); map("commandsDDL", help + "= 'Commands (DDL)' ORDER BY ID", false); map("commandsOther", help + "= 'Commands (Other)' ORDER BY ID", false); map("otherGrammar", help + "= 'Other Grammar' ORDER BY ID", true); map("functionsAggregate", help + "= 'Functions (Aggregate)' ORDER BY ID", false); map("functionsNumeric", help + "= 'Functions (Numeric)' ORDER BY ID", false); map("functionsString", help + "= 'Functions (String)' ORDER BY ID", false); map("functionsTimeDate", help + "= 'Functions (Time and Date)' ORDER BY ID", false); map("functionsSystem", help + "= 'Functions (System)' ORDER BY ID", false); map("functionsAll", help + "LIKE 'Functions%' ORDER BY SECTION, ID", true); map("dataTypes", help + "LIKE 'Data Types%' ORDER BY SECTION, ID", true); map("informationSchema", "SELECT TABLE_NAME TOPIC, GROUP_CONCAT(COLUMN_NAME " + "ORDER BY ORDINAL_POSITION SEPARATOR ', ') SYNTAX FROM INFORMATION_SCHEMA.COLUMNS " + "WHERE TABLE_SCHEMA='INFORMATION_SCHEMA' GROUP BY TABLE_NAME ORDER BY TABLE_NAME", false); processAll(""); conn.close(); } private void processAll(String dir) throws Exception { if (dir.endsWith(".svn")) { return; } File[] list = new File(inDir + "/" + dir).listFiles(); for (File file : list) { if (file.isDirectory()) { processAll(dir + file.getName()); } else { process(dir, file.getName()); } } } private void process(String dir, String fileName) throws Exception { String inFile = inDir + "/" + dir + "/" + fileName; String outFile = outDir + "/" + dir + "/" + fileName; new File(outFile).getParentFile().mkdirs(); FileOutputStream out = new FileOutputStream(outFile); FileInputStream in = new FileInputStream(inFile); byte[] bytes = IOUtils.readBytesAndClose(in, 0); if (fileName.endsWith(".html")) { String page = new String(bytes); page = PageParser.parse(page, session); bytes = page.getBytes(); } out.write(bytes); out.close(); } private void map(String key, String sql, boolean railroads) throws Exception { ResultSet rs = null; Statement stat = null; try { stat = conn.createStatement(); rs = stat.executeQuery(sql); ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>(); while (rs.next()) { HashMap<String, String> map = new HashMap<String, String>(); ResultSetMetaData meta = rs.getMetaData(); for (int i = 0; i < meta.getColumnCount(); i++) { String k = StringUtils.toLowerEnglish(meta.getColumnLabel(i + 1)); String value = rs.getString(i + 1); value = value.trim(); map.put(k, PageParser.escapeHtml(value)); } String topic = rs.getString("TOPIC"); String syntax = rs.getString("SYNTAX").trim(); if (railroads) { BnfRailroad r = new BnfRailroad(); String railroad = r.getHtml(bnf, syntax); map.put("railroad", railroad); } 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) { // text is enclosed in <p> .. </p> so this works. text = StringUtils.replaceAll(text, "<br /><br />", "</p><p>"); text = StringUtils.replaceAll(text, "<br />", " "); text = addCode(text); map.put("text", text); } String link = topic.toLowerCase(); link = StringUtils.replaceAll(link, " ", "_"); // link = StringUtils.replaceAll(link, "_", ""); link = StringUtils.replaceAll(link, "@", "_"); map.put("link", StringUtils.urlEncode(link)); list.add(map); } session.put(key, list); int div = 3; int part = (list.size() + div - 1) / div; for (int i = 0, start = 0; i < div; i++, start += part) { List<HashMap<String, String>> listThird = list.subList(start, Math.min(start + part, list.size())); session.put(key + "-" + i, listThird); } } finally { JdbcUtils.closeSilently(rs); JdbcUtils.closeSilently(stat); } } private static String addCode(String text) { text = StringUtils.replaceAll(text, """, "\""); StringBuilder buff = new StringBuilder(text.length()); int len = text.length(); boolean code = false, codeQuoted = false; for (int i = 0; i < len; i++) { char c = text.charAt(i); if (i < len - 1) { char next = text.charAt(i+1); if (!code && !codeQuoted) { if (Character.isUpperCase(c) && Character.isUpperCase(next)) { buff.append("<code>"); code = true; } else if (c == '\"' && (i == 0 || text.charAt(i - 1) != '\\')) { buff.append("<code>"); codeQuoted = true; continue; } } } if (code) { if (!Character.isLetterOrDigit(c) && "_.".indexOf(c) < 0) { buff.append("</code>"); code = false; } } else if (codeQuoted && c == '\"' && (i == 0 || text.charAt(i - 1) != '\\')) { buff.append("</code>"); codeQuoted = false; continue; } buff.append(c); } if (code) { buff.append("</code>"); } String s = buff.toString(); s = StringUtils.replaceAll(s, "</code>, <code>", ", "); s = StringUtils.replaceAll(s, ".</code>", "</code>."); s = StringUtils.replaceAll(s, ",</code>", "</code>."); s = StringUtils.replaceAll(s, " @<code>", " <code>@"); s = StringUtils.replaceAll(s, "</code> <code>", " "); s = StringUtils.replaceAll(s, "<code>SQL</code>", "SQL"); s = StringUtils.replaceAll(s, "<code>XML</code>", "XML"); s = StringUtils.replaceAll(s, "<code>URL</code>", "URL"); s = StringUtils.replaceAll(s, "<code>URLs</code>", "URLs"); s = StringUtils.replaceAll(s, "<code>HTML</code>", "HTML"); s = StringUtils.replaceAll(s, "<code>KB</code>", "KB"); s = StringUtils.replaceAll(s, "<code>MB</code>", "MB"); s = StringUtils.replaceAll(s, "<code>GB</code>", "GB"); return s; } }