You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ca...@apache.org on 2018/02/09 16:29:25 UTC

svn commit: r1823678 - in /jackrabbit/oak/trunk: ./ oak-doc-railroad-macro/ oak-doc-railroad-macro/src/ oak-doc-railroad-macro/src/main/ oak-doc-railroad-macro/src/main/java/ oak-doc-railroad-macro/src/main/java/org/ oak-doc-railroad-macro/src/main/jav...

Author: catholicon
Date: Fri Feb  9 16:29:25 2018
New Revision: 1823678

URL: http://svn.apache.org/viewvc?rev=1823678&view=rev
Log:
OAK-5051: Document XPath (and SQL-2) syntax as supported by Oak

Added a doxia macro that can render railroads (heavily inspired by h2
project's railroads).

Also, update grammar-xpath.md to utilize it.

TODO: Currently, h2-Bnf didn't like Xpath a lot (from keywords pov). So,
we're using a work-around escaping. Maybe h2-Bnf should be pluggable to
accept a list of keywords too.

Added:
    jackrabbit/oak/trunk/oak-doc-railroad-macro/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/pom.xml   (with props)
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/RailroadMacro.java   (with props)
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfRailroad.java   (with props)
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfSyntax.java   (with props)
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/Railroad.java   (with props)
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/grammar/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/grammar/test.csv
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/java/
    jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/java/RailroadTest.java
      - copied, changed from r1823677, jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md.vm   (contents, props changed)
      - copied, changed from r1823677, jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md
    jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/
    jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/sql2.csv
    jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/xpath.csv
    jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/
    jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-d.png   (with props)
    jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ke.png   (with props)
    jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ks.png   (with props)
    jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-le.png   (with props)
    jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ls.png   (with props)
    jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-te.png   (with props)
    jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ts.png   (with props)
Removed:
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md
Modified:
    jackrabbit/oak/trunk/oak-doc/pom.xml
    jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css
    jackrabbit/oak/trunk/pom.xml

Added: jackrabbit/oak/trunk/oak-doc-railroad-macro/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc-railroad-macro/pom.xml?rev=1823678&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc-railroad-macro/pom.xml (added)
+++ jackrabbit/oak/trunk/oak-doc-railroad-macro/pom.xml Fri Feb  9 16:29:25 2018
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd ">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.jackrabbit</groupId>
+        <artifactId>oak-parent</artifactId>
+        <version>1.10-SNAPSHOT</version>
+        <relativePath>../oak-parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>oak-doc-railroad-macro</artifactId>
+    <name>Oak Doxia Macro for Grammar Railroads</name>
+    <url>http://jackrabbit.apache.org/oak/docs/</url>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.plexus</groupId>
+                <artifactId>plexus-maven-plugin</artifactId>
+                <version>1.3.8</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>descriptor</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.plexus</groupId>
+                <artifactId>plexus-component-metadata</artifactId>
+                <version>1.5.6</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-metadata</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <version>${h2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.doxia</groupId>
+            <artifactId>doxia-core</artifactId>
+            <version>1.7</version>
+        </dependency>
+    </dependencies>
+</project>

Propchange: jackrabbit/oak/trunk/oak-doc-railroad-macro/pom.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/RailroadMacro.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/RailroadMacro.java?rev=1823678&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/RailroadMacro.java (added)
+++ jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/RailroadMacro.java Fri Feb  9 16:29:25 2018
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.doc.doxia;
+
+import org.apache.jackrabbit.oak.doc.doxia.jcr.Railroad;
+import org.apache.maven.doxia.macro.AbstractMacro;
+import org.apache.maven.doxia.macro.Macro;
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.component.annotations.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * RailroadMacro macro that prints out the content of a file or a URL.
+ */
+@Component( role = Macro.class, hint = "railroad" )
+public class RailroadMacro
+        extends AbstractMacro
+{
+    private final Map<String, Railroad> railroadCache = new HashMap<String, Railroad>();
+
+
+    /** {@inheritDoc} */
+    public void execute( Sink sink, MacroRequest request ) throws MacroExecutionException {
+        try {
+            String fileName = (String) request.getParameter( "file" );
+            required("file", fileName);
+            getLog().debug("fileName: " + fileName);
+
+            String topic = (String) request.getParameter("topic");
+            required("topic", topic);
+            getLog().debug("topic: " + topic);
+
+            boolean setAnchor = true;
+            String setAnchorParam = (String) request.getParameter( "setAnchor" );
+            if ( setAnchorParam != null && !"".equals( setAnchorParam ) ) {
+                setAnchor = Boolean.valueOf( setAnchorParam ).booleanValue();
+            }
+            getLog().debug("Set Anchor: " + setAnchor);
+
+            boolean renderLink = false;
+            String renderLinkParam = (String) request.getParameter( "renderLink" );
+            if ( renderLinkParam != null && !"".equals( renderLinkParam ) ) {
+                renderLink = Boolean.valueOf( renderLinkParam ).booleanValue();
+            }
+            getLog().debug("Render Link: " + renderLink);
+
+            Railroad railroad = getRailroad(fileName);
+
+            if (renderLink) {
+                sink.link(railroad.getLink("#" + topic));
+                sink.text(topic);
+                sink.link_();
+            } else {
+                if (setAnchor) {
+                    sink.rawText("<h2>");
+                    sink.anchor(railroad.getLink(topic));
+                    sink.anchor_();
+                    sink.text(topic);
+                    sink.rawText("</h2>");
+                }
+                String str = railroad.render(topic);
+                if (str == null) {
+                    throw new MacroExecutionException("NO RAILROAD FOR " + topic + " in " + fileName);
+                } else {
+                    sink.rawText(str);
+                }
+            }
+        } catch (Exception e) {
+            getLog().error("Error creating railroad: " + e.getMessage());
+            throw new MacroExecutionException("Error creating railroad: " + e.getMessage(), e);
+        }
+    }
+
+    private Railroad getRailroad(String fileName) throws Exception {
+        Railroad railroad = railroadCache.get(fileName);
+
+        if (railroad == null) {
+            getLog().info("Creating railroad for " + fileName);
+            railroad = new Railroad(fileName);
+            railroadCache.put(fileName, railroad);
+        }
+
+        return railroad;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/RailroadMacro.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfRailroad.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfRailroad.java?rev=1823678&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfRailroad.java (added)
+++ jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfRailroad.java Fri Feb  9 16:29:25 2018
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.doc.doxia.jcr;
+
+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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A BNF visitor that generates HTML railroad diagrams.
+ */
+public class BnfRailroad implements BnfVisitor {
+
+    private static final boolean RAILROAD_DOTS = true;
+
+    private static final Map<String, String> XPATH_KEYWORD_TO_ESCAPE = new HashMap();
+    static {
+        XPATH_KEYWORD_TO_ESCAPE.put("|", "@PIPE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("element", "@ELEMENT@");
+        XPATH_KEYWORD_TO_ESCAPE.put("/jcr:root", "@JCR_ROOT@");
+        XPATH_KEYWORD_TO_ESCAPE.put("jcr:contains", "@CONTAINS@");
+        XPATH_KEYWORD_TO_ESCAPE.put("jcr:like", "@LIKE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("jcr:score", "@SCORE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fn:not", "@FN_NOT@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fn:string-length", "@FN_STRING_LENGTH@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fn:name", "@FN_NAME@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fn:local-name", "@FN_LOCAL_NAME@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fn:lower-case", "@FN_LOWER_CASE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fn:upper-case", "@FN_UPPER_CASE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fn:coalesce", "@FN_COALESCE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("rep:excerpt", "@EXCERPT@");
+        XPATH_KEYWORD_TO_ESCAPE.put("rep:native", "@NATIVE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("rep:similar", "@SIMILAR@");
+        XPATH_KEYWORD_TO_ESCAPE.put("rep:spellcheck", "@SPELLCHECK@");
+        XPATH_KEYWORD_TO_ESCAPE.put("rep:suggest", "@SUGGEST@");
+        XPATH_KEYWORD_TO_ESCAPE.put("rep:facet", "@FACET@");
+        XPATH_KEYWORD_TO_ESCAPE.put("text()", "@TEXT@");
+        XPATH_KEYWORD_TO_ESCAPE.put("xs:dateTime", "@XS_DATE_TIME@");
+        XPATH_KEYWORD_TO_ESCAPE.put("true", "@TRUE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("false", "@FALSE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("ascending", "@ASCENDING@");
+        XPATH_KEYWORD_TO_ESCAPE.put("descending", "@DESCENDING@");
+        XPATH_KEYWORD_TO_ESCAPE.put("or", "@OR@");
+        XPATH_KEYWORD_TO_ESCAPE.put("and", "@AND@");
+        XPATH_KEYWORD_TO_ESCAPE.put("not", "@NOT@");
+        XPATH_KEYWORD_TO_ESCAPE.put("explain", "@EXPLAIN@");
+        XPATH_KEYWORD_TO_ESCAPE.put("measure", "@MEASURE@");
+        XPATH_KEYWORD_TO_ESCAPE.put("order by", "@ORDER_BY@");
+        XPATH_KEYWORD_TO_ESCAPE.put("option", "@OPTION@");
+        XPATH_KEYWORD_TO_ESCAPE.put("traversal", "@TRAVERSAL@");
+        XPATH_KEYWORD_TO_ESCAPE.put("ok", "@OK@");
+        XPATH_KEYWORD_TO_ESCAPE.put("warn", "@WARN@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fail", "@FAIL@");
+        XPATH_KEYWORD_TO_ESCAPE.put("default", "@DEFAULT@");
+        XPATH_KEYWORD_TO_ESCAPE.put("index", "@INDEX@");
+        XPATH_KEYWORD_TO_ESCAPE.put("tag", "@TAG@");
+        XPATH_KEYWORD_TO_ESCAPE.put("fulltextSearchExpression", "@FULLTEXT_EXPRESSION@");
+    }
+
+    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) {
+            for (Map.Entry<String, String> entry : XPATH_KEYWORD_TO_ESCAPE.entrySet()) {
+                s = StringUtils.replaceAll(s, "'" + entry.getKey() + "'", entry.getValue());
+            }
+
+            bnf.visit(this, s);
+            html = StringUtils.replaceAll(html, "</code></td>" +
+                    "<td class=\"d\"><code class=\"c\">", " ");
+
+            for (Map.Entry<String, String> entry : XPATH_KEYWORD_TO_ESCAPE.entrySet()) {
+                html = StringUtils.replaceAll(html, entry.getValue(), entry.getKey());
+            }
+
+            if (buff.length() > 0) {
+                buff.append("<br />");
+            }
+            buff.append(html);
+        }
+        return buff.toString();
+    }
+
+    @Override
+    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>";
+    }
+
+    @Override
+    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();
+    }
+
+    @Override
+    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);
+        }
+    }
+
+    @Override
+    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();
+    }
+
+    @Override
+    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();
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfRailroad.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfSyntax.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfSyntax.java?rev=1823678&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfSyntax.java (added)
+++ jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfSyntax.java Fri Feb  9 16:29:25 2018
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.doc.doxia.jcr;
+
+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;
+
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/**
+ * A BNF visitor that generates BNF in HTML form.
+ */
+public class BnfSyntax implements BnfVisitor {
+
+    private String html;
+
+    /**
+     * Get the HTML syntax for the given syntax.
+     *
+     * @param bnf the BNF
+     * @param syntaxLines the syntax
+     * @return the 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;
+    }
+
+    /**
+     * Get the HTML link to the given token.
+     *
+     * @param bnf the BNF
+     * @param token the token
+     * @return the HTML link
+     */
+    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;
+        }
+
+        if (found.getRule() instanceof RuleFixed) {
+            found.getRule().accept(this);
+            return html;
+        }
+        String link = found.getTopic().toLowerCase().replace(' ', '_');
+        link = "#" + StringUtils.urlEncode(link);
+        return "<a href=\"" + link + "\">" + token + "</a>";
+    }
+
+    @Override
+    public void visitRuleElement(boolean keyword, String name, Rule link) {
+        // not used
+    }
+
+    @Override
+    public void visitRuleFixed(int type) {
+        html = BnfRailroad.getHtmlText(type);
+    }
+
+    @Override
+    public void visitRuleList(boolean or, ArrayList<Rule> list) {
+        // not used
+    }
+
+    @Override
+    public void visitRuleOptional(Rule rule) {
+        // not used
+    }
+
+    @Override
+    public void visitRuleRepeat(boolean comma, Rule rule) {
+        // not used
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/BnfSyntax.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/Railroad.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/Railroad.java?rev=1823678&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/Railroad.java (added)
+++ jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/Railroad.java Fri Feb  9 16:29:25 2018
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.doc.doxia.jcr;
+
+import org.h2.bnf.Bnf;
+import org.h2.server.web.PageParser;
+import org.h2.tools.Csv;
+import org.h2.util.IOUtils;
+import org.h2.util.StringUtils;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * JCR 2.0 / SQL-2 railroad generator.
+ */
+public class Railroad {
+
+    private final Map<String, String> map;
+
+    public Railroad(String fileName) throws Exception {
+        Bnf bnf = Bnf.getInstance(getReader(fileName));
+        Csv csv = new Csv();
+        csv.setLineCommentCharacter('#');
+        ResultSet rs = csv.read(getReader(fileName), null);
+
+        map = map(bnf, rs);
+    }
+
+    public String render(String topic) {
+        return map.get(topic);
+    }
+
+    public static String getLink(String topic) {
+        String link = topic.toLowerCase();
+        link = link.replace(' ', '_');
+        // link = StringUtils.replaceAll(link, "_", "");
+        link = link.replace('@', '_');
+
+        return link;
+    }
+
+    private Map<String, String> map(Bnf bnf, ResultSet rs) throws Exception {
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        try {
+            while (rs.next()) {
+                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();
+
+                BnfRailroad r = new BnfRailroad();
+                String railroad = r.getHtml(bnf, syntax);
+                map.put(topic, railroad);
+            }
+        } finally {
+            IOUtils.closeSilently(rs);
+        }
+
+        return map;
+    }
+
+    private static Reader getReader(String fileName) throws IOException {
+        return new FileReader(new File(fileName));
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/main/java/org/apache/jackrabbit/oak/doc/doxia/jcr/Railroad.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/grammar/test.csv
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/grammar/test.csv?rev=1823678&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/grammar/test.csv (added)
+++ jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/grammar/test.csv Fri Feb  9 16:29:25 2018
@@ -0,0 +1,154 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"SECTION","TOPIC","SYNTAX","TEXT"
+
+"Grammar","Query","
+'/jcr:root' { filter [ ... ] }
+    [ ( column [ | ... ] ) ]
+    [ 'order by' { ordering [ , ... ] } ]
+    [ queryOptions ]
+","
+"
+
+"Grammar","Filter","
+/ directChildNodeFilter
+    | // descendantNodeFilter
+    | *
+    | 'element'( [ * | nodeName ] [, nodeType] )
+    | 'text()'
+    | '[' constraint ']'
+    | ( filter '|' unionFilter [...] )
+","
+"
+
+"Grammar","Column","
+'rep:excerpt'( [. | [ relativePath / ] @propertyName ]  )
+    | 'rep:spellcheck'()
+    | 'rep:suggest'( [.] )
+    | 'rep:facet' ( [ relativePath / ] @propertyName )
+","
+"
+
+"Grammar","Constraint","
+andCondition [ { 'or' andCondition } [...] ]
+","
+"
+
+"Grammar","And Condition","
+condition [ { 'and' condition } [...] ]
+","
+"
+
+"Grammar","Condition","
+comparison
+    inComparison
+    | [ 'fn:not' | 'not' ] (constraint)
+    | ( constraint )
+    | 'jcr:contains'( [ { propertyName | . } , ] 'fulltextSearchExpression' )
+    | 'jcr:like'( dynamicOperand , staticOperand )
+    | 'rep:similar' ( propertyName , staticOperand )
+    | 'rep:native' ( languageName , staticOperand )
+    | 'rep:spellcheck' ( staticOperand )
+    | 'rep:suggest' ( staticOperand )
+","
+"
+
+"Grammar","Comparison","
+dynamicOperand
+    { = | <> | != | < | <= | > | >= }
+    staticOperand
+","
+"
+
+"Grammar","Fulltext Search Expression","
+' anythingExceptSingleQuote ' | $ bindVariableName
+","
+"
+
+"Grammar","Static Operand","
+textLiteral
+    | $ bindVariableName
+    | [ + | - ] numberLiteral
+    | 'true' [ () ]
+    | 'false' [ () ]
+    | 'xs:dateTime' ( literal )
+","
+"
+
+"Grammar","Ordering","
+dynamicOperand [ 'ascending' | 'descending' ]
+","
+"
+
+"Grammar","Dynamic Operand","
+[ relativePath / ] { @propertyName | * }
+    | 'fn:string-length' ( dynamicOperand  )
+    | { 'fn:name' | 'fn:local-name' } ( [ . ] )
+    | 'jcr:score' ( )
+    | { 'fn:lower-case' | 'fn:upper-case' } ( dynamicOperand )
+    | 'fn:coalesce' ( firstDynamicOperand, secondDynamicOperand )
+","
+"
+
+"Grammar","Path","
+{ simpleName / [ ... ] }
+","
+"
+
+"Grammar","Literal","
+' anythingExceptSingleQuote '
+    | "" anythingExceptDoubleQuote ""
+    | numberLiteral
+","
+"
+
+"Grammar","Number Literal","
+[ + | - ] { { number [ . number ] } | { . number } } [ E [ + | - ] expNumber [...] ] ]
+","
+"
+
+"Grammar","Number","
+0-9 [...]
+","
+"
+
+"Grammar","Name","
+simpleName
+","
+"
+
+"Grammar","Type","
+simpleType
+","
+"
+
+"Grammar","Options","
+'option'( {
+    'traversal' { 'ok' | 'warn' | 'fail' | 'default' } |
+    'index' 'tag' tagName
+    } [ , ... ] )
+","
+"
+
+"Grammar","Explain","
+'explain' ['measure'] { query }
+","
+"
+
+"Grammar","Measure","
+'measure' { query }
+","
+"
\ No newline at end of file

Copied: jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/java/RailroadTest.java (from r1823677, jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/java/RailroadTest.java?p2=jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/java/RailroadTest.java&p1=jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css&r1=1823677&r2=1823678&rev=1823678&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css (original)
+++ jackrabbit/oak/trunk/oak-doc-railroad-macro/src/test/java/RailroadTest.java Fri Feb  9 16:29:25 2018
@@ -14,27 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-tt {
-	white-space: nowrap;
-	border: 1px solid #eaeaea;
-	background-color: #f5f5f5;
-}
-pre {
-	font-size: 12px;
-}
 
-/* fluido skin moves to small-screen-statically-positioned top-nav below 980px */
-@media(min-width:980px) {
-    div[id="bodyColumn"] h1 > a[name],
-    div[id="bodyColumn"] h2 > a[name],
-    div[id="bodyColumn"] h3 > a[name],
-    div[id="bodyColumn"] h4 > a[name] {
-        visibility: hidden;
-        content: "";
-        display: block;
+import org.apache.jackrabbit.oak.doc.doxia.jcr.Railroad;
+import org.junit.Test;
 
-        /* 42px is how layout showed as top-nav height.*/
-        margin: -42px 0 0 0;
-        height: 42px;
+public class RailroadTest {
+    @Test
+    public void main() throws Exception {
+        String fileName = "src/test/grammar/test.csv";
+        System.out.println(new Railroad(fileName).render("Query"));
     }
-}
\ No newline at end of file
+}

Modified: jackrabbit/oak/trunk/oak-doc/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/pom.xml?rev=1823678&r1=1823677&r2=1823678&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-doc/pom.xml Fri Feb  9 16:29:25 2018
@@ -42,6 +42,13 @@
           <skip>false</skip>
           <relativizeDecorationLinks>false</relativizeDecorationLinks>
         </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>oak-doc-railroad-macro</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>

Copied: jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md.vm (from r1823677, jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md.vm?p2=jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md.vm&p1=jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md&r1=1823677&r2=1823678&rev=1823678&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md.vm Fri Feb  9 16:29:25 2018
@@ -14,32 +14,40 @@
    See the License for the specific language governing permissions and
    limitations under the License.
   -->
+#set($h1 = '#')
+#set($h2 = '##')
+#set($h3 = '###')
+#set($h4 = '####')
+
+#set($xpath-grammar = ${project.basedir} + '/src/site/resources/grammar/xpath.csv')
+
+#macro( railroad_link $topic )
+<!-- MACRO{railroad|file=$xpath-grammar|topic=$topic|renderLink=true} -->
+#end
+
+#macro( render $topic )
+<!-- MACRO{railroad|file=$xpath-grammar|topic=$topic} -->
+#end
+
+$h2 Oak XPath Query Grammar - ${project.name}
+
+* #railroad_link( 'Query' )
+* #railroad_link( 'Filter' )
+* #railroad_link( 'Column' )
+* #railroad_link( 'Constraint' )
+* #railroad_link( 'And Condition' )
+* #railroad_link( 'Condition' )
+* #railroad_link( 'Comparison' )
+* #railroad_link( 'Static Operand' )
+* #railroad_link( 'Ordering' )
+* #railroad_link( 'Dynamic Operand' )
+* #railroad_link( 'Options' )
+* #railroad_link( 'Explain' )
+* #railroad_link( 'Measure' )
 
-## Oak XPath Query Grammar
+---
 
-* [Query](#query)
-* [Filter](#filter)
-* [Column](#column)
-* [Constraint](#constraint)
-* [And Condition](#andCondition)
-* [Condition](#condition)
-* [Comparison](#comparison)
-* [Static Operand](#staticOperand)
-* [Ordering](#ordering)
-* [Dynamic Operand](#dynamicOperand)
-* [Options](#options)
-* [Explain](#explain)
-* [Measure](#measure)
-
-<hr />
-<h3 id="query">Query</h3>
-
-<h4>
-/jcr:root { <a href="#filter">filter</a> [ ... ] } }
-<br/> [ ( <a href="#column">column</a> [ | ... ] } ) ]
-<br/> [ order by { <a href="#ordering">ordering</a> [ , ... ] } ]
-<br/> [ <a href="#options">queryOptions</a> ]
-</h4>
+#render( 'Query' )
 
 The "/jcr:root" means the root node. 
 It is recommended that all XPath queries start with this term.
@@ -70,18 +78,9 @@ Get all nodes below /etc that have the p
     /jcr:root/etc//*[@type='report'] option(traversal fail)
 
 
-<hr />
-<h3 id="filter">Filter</h3>
+---
 
-<h4>
-/ <a href="#filter">directChildNodeFilter</a>
-<br/> | // <a href="#filter">descendantNodeFilter</a>
-<br/> | *
-<br/> | element( [ * | nodeName ] [, nodeType] )
-<br/> | text()
-<br/> | '[' <a href="#constraint">constraint</a> ']'
-<br/> | ( filter '|' unionFilter [...] )
-</h4>
+#render( 'Filter' )
 
 A single slash means filtering on a specific child node,
 while two slashes means filtering on a descendant node.
@@ -130,15 +129,9 @@ The paths '/libs' and '/etc' should be s
     /jcr:root/(libs | etc)//*[@jcr:uuid and @jcr:mimeType = 'text/css']
 
 
-<hr />
-<h3 id="column">Column</h3>
+---
 
-<h4>
-rep:excerpt( [. | [ relativePath / ] @propertyName ]  )
-<br/> | rep:spellcheck()
-<br/> | rep:suggest( [.] )
-<br/> | rep:facet ( [ relativePath / ] @propertyName )
-</h4>
+#render( 'Column' )
 
 "rep:excerpt": include the excerpt in the result.
 Since Oak version 1.8.1, optionally a property name can be specified.
@@ -164,12 +157,9 @@ Examples:
     /jcr:root/content//*[jcr:contains(@jcr:title, 'oak')]/(rep:facet(@tags))
 
 
-<hr />
-<h3 id="constraint">Constraint</h3>
+---
 
-<h4>
-<a href="#andCondition">andCondition</a> [ { or <a href="#andCondition">andCondition</a> } [...] ]
-</h4>
+#render( 'Constraint' )
 
 "or" conditions of the form "@x = 1 or @x = 2" can use the same index.
 
@@ -185,12 +175,9 @@ or don't have the property set:
     /jcr:root/content/dam/*[@hidden='hidden-folder' or not(@hidden)]
 
 
-<hr />
-<h3 id="andCondition">And Condition</h3>
+---
 
-<h4>
-<a href="#condition">condition</a> [ { and <a href="#condition">condition</a> } [...] ]
-</h4>
+#render( 'And Condition' )
 
 A special case (not found in relational databases) is
 "and" conditions of the form "@x = 1 and @x = 2".
@@ -202,21 +189,9 @@ Examples:
     /jcr:root/home//element(*, rep:Authorizable)[@rep:principalName != 'Joe' and @rep:principalName != 'Steve']
 
 
-<hr />
-<h3 id="condition">Condition</h3>
+---
 
-<h4>
-<a href="#comparison">comparison</a>
-<br/> <a href="#inComparison">inComparison</a>
-<br/> | [ [ fn:not | not ] (<a href="#constraint">constraint</a>)
-<br/> | ( <a href="#constraint">constraint</a> )
-<br/> | jcr:contains( [ { property | . } , ] fulltextSearchExpression )
-<br/> | jcr:like( dynamicOperand , staticOperand )
-<br/> | rep:similar ( propertyName , staticOperand )
-<br/> | rep:native ( language , staticOperand )
-<br/> | rep:spellcheck ( staticOperand )
-<br/> | rep:suggest ( staticOperand )
-</h4>
+#render( 'Condition' )
 
 "fn:not": this is used for both "is not null", and for nested conditions.
 For example "fn:not(@status)" means nodes where this property is not set.
@@ -249,14 +224,9 @@ Examples:
     /jcr:root/content//element(*, cq:Page)[@offTime > xs:dateTime('2020-12-01T20:00:00.000') or @onTime > xs:dateTime('2020-12-01T20:00:00.000')]
 
 
-<hr />
-<h3 id="comparison">Comparison</h3>
+---
 
-<h4>
-<a href="#dynamicOperand">dynamicOperand</a> 
-{ = | &lt;&gt; | != | &lt; | &lt;= | &gt; | &gt;= } 
-<a href="#staticOperand">staticOperand</a>
-</h4>
+#render( 'Comparison' )
 
 Comparison using &lt;, &gt;, &gt;=, and &lt;= can use an index if the property in the index is ordered.
 
@@ -266,17 +236,9 @@ Examples:
     @offTime > xs:dateTime('2020-12-01T20:00:00.000')
 
 
-<hr />
-<h3 id="staticOperand">Static Operand</h3>
+---
 
-<h4>
-textLiteral
-<br/> | $ bindVariableName
-<br/> | [ + | - ] numberLiteral
-<br/> | true [ () ]
-<br/> | false [ () ]
-<br/> | xs:dateTime ( literal )
-</h4>
+#render( 'Static Operand' )
 
 A string (text) literal starts and ends with a single quote. 
 Two single quotes can be used to create a single quote inside a string.
@@ -290,12 +252,9 @@ Examples:
     -30.3
     xs:dateTime('2020-12-01T20:00:00.000')
 
-<hr />
-<h3 id="ordering">Ordering</h3>
+---
 
-<h4>
-<a href="#dynamicOperand">dynamicOperand</a> [ ascending | descending ]
-</h4>
+#render( 'Ordering' )
 
 Ordering by an indexed property will use that index if possible.
 If there is no index that can be used for the given sort order,
@@ -312,17 +271,9 @@ Examples:
     order by @jcr:created descending
 
 
-<hr />
-<h3 id="dynamicOperand">Dynamic Operand</h3>
+---
 
-<h4>
-[ relativePath / ] { @propertyName | * }
-<br/>  | fn:string-length ( dynamicOperand  )
-<br/>  | { fn:name | fn:local-name } ( [ . ] )
-<br/>  | jcr:score ( )
-<br/>  | { fn:lower-case | fn:upper-case } ( dynamicOperand )
-<br/>  | fn:coalesce ( dynamicOperand1, dynamicOperand2 )
-</h4>
+#render( 'Dynamic Operand' )
 
 The "*" stands for any property.
 
@@ -345,15 +296,9 @@ Examples:
     fn:coalesce(@lastName, @name)
 
 
-<hr />
-<h3 id="options">Options</h3>
+---
 
-<h4>
-option( { 
-<br/>&nbsp;&nbsp; traversal { ok | warn | fail | default } | 
-<br/>&nbsp;&nbsp; index tag tagName 
-<br/> } [ , ... ] )
-</h4>
+#render( 'Options' )
 
 "traversal": by default, queries without index will log a warning,
 except if the configuration option `QueryEngineSettings.failTraversal` is changed
@@ -372,12 +317,9 @@ Examples:
     option(traversal fail)
 
 
-<hr />
-<h3 id="explain">Explain Query</h3>
+---
 
-<h4>
-explain [measure] { <a href="#query">query</a> }
-</h4>
+#render( 'Explain' )
 
 Does not run the query, but only computes and returns the query plan.
 With "explain measure", the expected cost is calculated as well.
@@ -396,12 +338,9 @@ Result:
 This means the property index named "uuid" is used for this query.
 The expected cost (roughly the number of uncached I/O operations) is 2.
 
-<hr />
-<h3 id="measure">Measure</h3>
+---
 
-<h4>
-measure { <a href="#query">query</a> }
-</h4>
+#render( 'Measure' )
 
 Runs the query, but instead of returning the result, returns the number of rows traversed.
 The query result has two columns, one called 'selector' and one called 'scanCount'.

Propchange: jackrabbit/oak/trunk/oak-doc/src/site/markdown/query/grammar-xpath.md.vm
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css?rev=1823678&r1=1823677&r2=1823678&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/resources/css/site.css Fri Feb  9 16:29:25 2018
@@ -37,4 +37,111 @@ pre {
         margin: -42px 0 0 0;
         height: 42px;
     }
-}
\ No newline at end of file
+}
+
+/* begin grammar railroad styling*/
+.railroad {
+    border: 0px;
+    padding: 0px;
+    margin: 0px;
+    border-collapse: collapse;
+    vertical-align: top;
+}
+
+.c {
+    padding: 1px 3px;
+    margin: 0px 0px;
+    border: 2px solid;
+    -moz-border-radius: 0.4em;
+    -webkit-border-radius: 0.4em;
+    -khtml-border-radius: 0.4em;
+    border-radius: 0.4em;
+    background-color: #fff;
+    color: black;
+    font-size: 14px;
+}
+
+.ts {
+    border: 0px;
+    padding: 0px;
+    margin: 0px;
+    border-collapse: collapse;
+    vertical-align: top;
+    width: 16px;
+    height: 24px;
+    background-image: url(../img/railroad/div-ts.png);
+    background-size: 16px 512px;
+}
+
+.ls {
+    border: 0px;
+    padding: 0px;
+    margin: 0px;
+    border-collapse: collapse;
+    vertical-align: top;
+    width: 16px;
+    height: 24px;
+    background-image: url(../img/railroad/div-ls.png);
+    background-size: 16px 512px;
+}
+
+.ks {
+    border: 0px;
+    padding: 0px;
+    margin: 0px;
+    border-collapse: collapse;
+    vertical-align: top;
+    width: 16px;
+    height: 24px;
+    background-image: url(../img/railroad/div-ks.png);
+    background-size: 16px 512px;
+}
+
+.te {
+    border: 0px;
+    padding: 0px;
+    margin: 0px;
+    border-collapse: collapse;
+    vertical-align: top;
+    width: 16px;
+    height: 24px;
+    background-image: url(../img/railroad/div-te.png);
+    background-size: 16px 512px;
+}
+
+.le {
+    border: 0px;
+    padding: 0px;
+    margin: 0px;
+    border-collapse: collapse;
+    vertical-align: top;
+    width: 16px;
+    height: 24px;
+    background-image: url(../img/railroad/div-le.png);
+    background-size: 16px 512px;
+}
+
+.ke {
+    border: 0px;
+    padding: 0px;
+    margin: 0px;
+    border-collapse: collapse;
+    vertical-align: top;
+    width: 16px;
+    height: 24px;
+    background-image: url(../img/railroad/div-ke.png);
+    background-size: 16px 512px;
+}
+
+.d {
+    border: 0px;
+    padding: 0px;
+    margin: 0px;
+    border-collapse: collapse;
+    vertical-align: top;
+    min-width: 16px;
+    height: 24px;
+    background-image: url(../img/railroad/div-d.png);
+    background-size: 1024px 512px;
+}
+/* end grammar railroad styling*/
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/sql2.csv
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/sql2.csv?rev=1823678&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/sql2.csv (added)
+++ jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/sql2.csv Fri Feb  9 16:29:25 2018
@@ -0,0 +1,99 @@
+# Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
+# and the EPL 1.0 (http://h2database.com/html/license.html).
+# Initial Developer: H2 Group)
+
+"SECTION","TOPIC","SYNTAX","TEXT"
+"Grammar","Query","
+SELECT { * | { column [ , ... ] } }  FROM { selector [ join ... ] }
+[ WHERE constraint ] [ ORDER BY { ordering [ , ... ] } ]
+","
+"
+
+"Grammar","Column","
+{ [ selectorName . ] propertyName [ AS columnName ] } | { selectorName . * }
+","
+"
+
+"Grammar","Selector","
+nodeTypeName [ AS selectorName ]
+","
+"
+
+"Grammar","Join","
+{ INNER | { LEFT | RIGHT } OUTER } JOIN rightSelector ON
+{ selectorName . propertyName = joinSelectorName . joinPropertyName }
+    | { ISSAMENODE( selectorName , joinSelectorName [ , selectorPathName ] ) }
+    | { ISCHILDNODE( childSelectorName , parentSelectorName ) }
+    | { ISDESCENDANTNODE( descendantSelectorName , ancestorSelectorName ) }
+","
+"
+
+"Grammar","Constraint","
+andCondition [ { OR andCondition } [...] ]
+","
+"
+
+"Grammar","And Condition","
+condition [ { AND condition } [...] ]
+","
+"
+
+"Grammar","Condition","
+comparison | NOT constraint | ( constraint )
+    | [ selectorName . ] propertyName IS [ NOT ] NULL
+    | CONTAINS( { { [ selectorName . ] propertyName } | { selectorName . * } } , fulltextSearchExpression )
+    | { ISSAMENODE | ISCHILDNODE | ISDESCENDANTNODE } (  [ selectorName , ] PathName )
+","
+"
+
+"Grammar","Comparison","
+dynamicOperand { = | <> | < | <= | > | >= | LIKE } staticOperand
+","
+"
+
+"Grammar","Fulltext Search Expression","
+' anythingExceptSingleQuote ' | $ bindVariableName
+","
+"
+
+"Grammar","Static Operand","
+literal
+    | $ bindVariableName
+    | CAST ( literal AS { STRING | BINARY | DATE | LONG | DOUBLE | DECIMAL | BOOLEAN | NAME | PATH | REFERENCE | WEAKREFERENCE | URI } )
+","
+"
+
+"Grammar","Literal","
+' anythingExceptSingleQuote '
+    | "" anythingExceptDoubleQuote ""
+    | numberLiteral
+","
+"
+
+"Grammar","Number Literal","
+[ + | - ] { { number [ . number ] } | { . number } } [ E [ + | - ] expNumber [...] ] ]
+","
+"
+
+"Grammar","Number","
+0-9 [...]
+","
+"
+
+"Grammar","Dynamic Operand","
+[ selectorName . ] propertyName
+    | LENGTH( [ selectorName . ] propertyName  )
+    | { NAME | LOCALNAME | SCORE } ( [ selectorName ] )
+    | { LOWER | UPPER } ( dynamicOperand )
+","
+"
+
+"Grammar","Ordering","
+simpleName [ ASC | DESC ]
+","
+"
+
+"Grammar","Name","
+simpleName | '[' quotedName ']'
+","
+"
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/xpath.csv
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/xpath.csv?rev=1823678&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/xpath.csv (added)
+++ jackrabbit/oak/trunk/oak-doc/src/site/resources/grammar/xpath.csv Fri Feb  9 16:29:25 2018
@@ -0,0 +1,149 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"SECTION","TOPIC","SYNTAX","TEXT"
+
+"Grammar","Query","
+'/jcr:root' { filter [ ... ] }
+    [ ( column [ | ... ] ) ]
+    [ 'order by' { ordering [ , ... ] } ]
+    [ queryOptions ]
+","
+"
+
+"Grammar","Filter","
+/ directChildNodeFilter
+    | // descendantNodeFilter
+    | *
+    | 'element'( [ * | nodeName ] [, nodeType] )
+    | 'text()'
+    | '[' constraint ']'
+    | ( filter '|' unionFilter [...] )
+","
+"
+
+"Grammar","Column","
+'rep:excerpt'( [. | [ relativePath / ] @propertyName ]  )
+    | 'rep:spellcheck'()
+    | 'rep:suggest'( [.] )
+    | 'rep:facet' ( [ relativePath / ] @propertyName )
+","
+"
+
+"Grammar","Constraint","
+andCondition [ { 'or' andCondition } [...] ]
+","
+"
+
+"Grammar","And Condition","
+condition [ { 'and' condition } [...] ]
+","
+"
+
+"Grammar","Condition","
+comparison
+    inComparison
+    | [ 'fn:not' | 'not' ] (constraint)
+    | ( constraint )
+    | 'jcr:contains'( [ { propertyName | . } , ] 'fulltextSearchExpression' )
+    | 'jcr:like'( dynamicOperand , staticOperand )
+    | 'rep:similar' ( propertyName , staticOperand )
+    | 'rep:native' ( languageName , staticOperand )
+    | 'rep:spellcheck' ( staticOperand )
+    | 'rep:suggest' ( staticOperand )
+","
+"
+
+"Grammar","Comparison","
+dynamicOperand
+    { = | <> | != | < | <= | > | >= }
+    staticOperand
+","
+"
+
+"Grammar","Static Operand","
+textLiteral
+    | $ bindVariableName
+    | [ + | - ] numberLiteral
+    | 'true' [ () ]
+    | 'false' [ () ]
+    | 'xs:dateTime' ( literal )
+","
+"
+
+"Grammar","Ordering","
+dynamicOperand [ 'ascending' | 'descending' ]
+","
+"
+
+"Grammar","Dynamic Operand","
+[ relativePath / ] { @propertyName | * }
+    | 'fn:string-length' ( dynamicOperand  )
+    | { 'fn:name' | 'fn:local-name' } ( [ . ] )
+    | 'jcr:score' ( )
+    | { 'fn:lower-case' | 'fn:upper-case' } ( dynamicOperand )
+    | 'fn:coalesce' ( firstDynamicOperand, secondDynamicOperand )
+","
+"
+
+"Grammar","Path","
+{ simpleName / [ ... ] }
+","
+"
+
+"Grammar","Literal","
+' anythingExceptSingleQuote '
+    | "" anythingExceptDoubleQuote ""
+    | numberLiteral
+","
+"
+
+"Grammar","Number Literal","
+[ + | - ] { { number [ . number ] } | { . number } } [ E [ + | - ] expNumber [...] ] ]
+","
+"
+
+"Grammar","Number","
+0-9 [...]
+","
+"
+
+"Grammar","Name","
+simpleName
+","
+"
+
+"Grammar","Type","
+simpleType
+","
+"
+
+"Grammar","Options","
+'option'( {
+    'traversal' { 'ok' | 'warn' | 'fail' | 'default' } |
+    'index' 'tag' tagName
+    } [ , ... ] )
+","
+"
+
+"Grammar","Explain","
+'explain' ['measure'] { query }
+","
+"
+
+"Grammar","Measure","
+'measure' { query }
+","
+"
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-d.png
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-d.png?rev=1823678&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-d.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ke.png
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ke.png?rev=1823678&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ke.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ks.png
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ks.png?rev=1823678&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ks.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-le.png
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-le.png?rev=1823678&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-le.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ls.png
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ls.png?rev=1823678&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ls.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-te.png
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-te.png?rev=1823678&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-te.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ts.png
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ts.png?rev=1823678&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jackrabbit/oak/trunk/oak-doc/src/site/resources/img/railroad/div-ts.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Modified: jackrabbit/oak/trunk/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/pom.xml?rev=1823678&r1=1823677&r2=1823678&view=diff
==============================================================================
--- jackrabbit/oak/trunk/pom.xml (original)
+++ jackrabbit/oak/trunk/pom.xml Fri Feb  9 16:29:25 2018
@@ -117,6 +117,7 @@
       <id>doc</id>
       <modules>
         <module>oak-doc</module>
+        <module>oak-doc-railroad-macro</module>
       </modules>
     </profile>
     <profile>