Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/rxm.git

Migrated classes out of tree package.

Author Dave Jarvis <email>
Date 2015-03-24 19:36:56 GMT-0700
Commit f04471c01f3d076d066e0ae5110d30d6e7f82c10
Parent 02ded80
source/java/com/whitemagicsoftware/rxm/QueryBuilder.java
import com.whitemagicsoftware.rxm.grammar.QueryParser;
-import com.whitemagicsoftware.rxm.tree.xml.SelectClause;
-import com.whitemagicsoftware.rxm.tree.xml.FromClause;
-import com.whitemagicsoftware.rxm.tree.xml.JoinClause;
-import com.whitemagicsoftware.rxm.tree.xml.WhereClause;
+import com.whitemagicsoftware.rxm.xml.SelectClause;
+import com.whitemagicsoftware.rxm.xml.FromClause;
+import com.whitemagicsoftware.rxm.xml.JoinClause;
+import com.whitemagicsoftware.rxm.xml.WhereClause;
import org.antlr.v4.runtime.ANTLRInputStream;
source/java/com/whitemagicsoftware/rxm/tree/Token.java
-package com.whitemagicsoftware.rxm.tree;
-
-import org.antlr.v4.runtime.CommonToken;
-import static org.antlr.v4.runtime.Token.MIN_USER_TOKEN_TYPE;
-
-/**
- * Avoids having to duplicate common token functionality across the different
- * parser rule contexts.
- */
-public class Token extends CommonToken {
- public Token( String text ) {
- super( MIN_USER_TOKEN_TYPE, text );
- }
-
- public String toString() {
- return getText();
- }
-}
-
source/java/com/whitemagicsoftware/rxm/tree/xml/AttributeMapContext.java
-package com.whitemagicsoftware.rxm.tree.xml;
-
-import com.whitemagicsoftware.rxm.grammar.QueryParser;
-import com.whitemagicsoftware.rxm.tree.Token;
-import com.whitemagicsoftware.rxm.tree.Tree;
-
-import org.antlr.v4.runtime.ParserRuleContext;
-
-/**
- * Transforms <b><code>table &gt; @attribute</code></b> into an
- * <code>XMLATTRIBUTES</code> expression.
- */
-public class AttributeMapContext extends Context {
- private boolean hasPrecedingPayload = false;
- private boolean hasFollowingPayload = false;
-
- public AttributeMapContext( ParserRuleContext ctx ) {
- super( ctx );
- }
-
- /**
- * Opens the XMLATTRIBUTES expression.
- *
- * @return Token containing ",XMLATTRIBUTES(" ...
- */
- @Override
- public Token getStart() {
- String
- tableName = getParentTableText(),
- columnName = getColumnText(),
- attributeName = getAttributeText();
-
- return new Token(
- String.format( ",%s", hasPrecedingPayload() ?
- nextAttribute( tableName, columnName, attributeName ) :
- firstAttribute( tableName, columnName, attributeName )
- )
- );
- }
-
- private boolean hasPrecedingPayload() {
- return this.hasPrecedingPayload;
- }
-
- private boolean hasFollowingPayload() {
- return this.hasFollowingPayload;
- }
-
- /**
- * Closes the XMLATTRIBUTES expression, should no more attributes be
- * forthcoming.
- *
- * @return Token containing ")" or the empty string.
- */
- @Override
- public Token getStop() {
- return new Token( hasFollowingPayload() ? "" : ")" );
- }
-
- private String getAttributeText() {
- return attribute().getChild(1).getText();
- }
-
- /**
- * Returns the attribute (node) name.
- */
- private QueryParser.AttributeContext attribute() {
- return getAttributeMapContext().attribute();
- }
-
- /**
- * Returns the attribute map context that this class wraps.
- */
- private QueryParser.AttributeMapContext getAttributeMapContext() {
- return (QueryParser.AttributeMapContext)getParserRuleContext();
- }
-
- @Override
- protected QueryParser.ColumnContext column() {
- return getAttributeMapContext().column();
- }
-
- /**
- * Formats the start of the XMLATTRIBUTES call.
- *
- * @param table The table name assigned to the attribute.
- * @param column The column name assigned to the attribute.
- * @param node The document node name assigned to the attribute.
- *
- * @return The text used for SQL/XML XMLATTRIBUTES calls.
- */
- private String firstAttribute( String table, String column, String node ) {
- return "XMLATTRIBUTES(" + nextAttribute( table, column, node );
- }
-
- /**
- * Formats the next attribute of the XMLATTRIBUTES call. This is only
- * called when there are contiguous attributes listed in the <b>rxm</b>.
- *
- * @param table The table name assigned to the attribute.
- * @param column The column name assigned to the attribute.
- * @param node The document node name assigned to the attribute.
- *
- * @return The text used for SQL/XML XMLATTRIBUTES calls.
- */
- private String nextAttribute( String table, String column, String node ) {
- String result = "%s.%s%s";
-
- // If the column name and attribute name are the same, then the
- // "AS" clause is superfluous.
- if( column.equals( node ) ) {
- result = String.format( result, table, column, "" );
- }
- else {
- node = String.format( " AS \"%s\"", node );
- result = String.format( result, table, column, node );
- }
-
- return result;
- }
-}
-
source/java/com/whitemagicsoftware/rxm/tree/xml/Clause.java
-package com.whitemagicsoftware.rxm.tree.xml;
-
-import java.nio.CharBuffer;
-
-import com.whitemagicsoftware.rxm.grammar.QueryBaseListener;
-
-/**
- * Common behaviour for various clauses.
- */
-public class Clause extends QueryBaseListener {
- private static int INDENT = 2;
-
- private StringBuilder buffer = new StringBuilder( 2048 );
-
- /**
- * Default constructor.
- */
- public Clause() {
- }
-
- /**
- * Appends a given string to the buffer.
- *
- * @param s The string to append.
- * @return this to chain the append calls.
- */
- protected Clause append( String s ) {
- getBuffer().append( s );
- return this;
- }
-
- /**
- * Returns the buffer converted to a string. This is the result of
- * the transformed output and should only be called when the parsing
- * of the <b>rxm</b> source is complete.
- *
- * @return A non-null string, possibly empty.
- */
- public String toString() {
- return getBuffer().toString();
- }
-
- /**
- * Returns the buffer used for building the transformed output.
- *
- * @return A non-null string builder, possibly empty.
- */
- private StringBuilder getBuffer() {
- return this.buffer;
- }
-
- /**
- * Answers whether the transformed query should be indented with newlines.
- *
- * @return true Format the output (by default).
- */
- protected boolean beautify() {
- // Returns the System property value.
- return Boolean.getBoolean( "beautify" );
- }
-
- /**
- * When beautify is enabled, this returns a newline followed by
- * the default amount ofindentation; when disabled, this returns a single
- * space.
- *
- * @return A newline followed by spaces or a single space.
- */
- protected String getNewlineIndent() {
- return getNewline() + (beautify() ? getDefaultIndent() : " ");
- }
-
- /**
- * Creates a string of spaces that is spaces spaces long.
- *
- * @param spaces The number of spaces to add to the string.
- * @return A string with 'spaces' number of " " padding.
- */
- protected String getIndent( int spaces ) {
- return CharBuffer.allocate( spaces ).toString().replace( '\0', ' ' );
- }
-
- /**
- * Returns the system newline separator character sequence.
- *
- * @return CR/LF on Windows, LF on Unix, CR on MacOS, or " " if beautify
- * is set true.
- */
- protected String getNewline() {
- return beautify() ? System.lineSeparator() : " ";
- }
-
- /**
- * Returns the default amount of spaces.
- *
- * @return " " (by default)
- */
- protected String getDefaultIndent() {
- return getIndent( getIndentBy() );
- }
-
- /**
- * Returns the number of spaces to indent by.
- *
- * @return 2 (by default)
- */
- protected int getIndentBy() {
- return INDENT;
- }
-}
-
source/java/com/whitemagicsoftware/rxm/tree/xml/ColumnMapContext.java
-package com.whitemagicsoftware.rxm.tree.xml;
-
-import com.whitemagicsoftware.rxm.grammar.QueryParser;
-import com.whitemagicsoftware.rxm.tree.Token;
-import com.whitemagicsoftware.rxm.tree.Tree;
-
-import org.antlr.v4.runtime.ParserRuleContext;
-
-/**
- * Transforms <b><code>column &gt; path</code></b> into an
- * <code>XMLELEMENT</code> expression.
- */
-public class ColumnMapContext extends Context {
- /**
- * Default constructor (calls super).
- *
- * @param ctx The payload that associates the parser rule context with
- * the abstract syntax tree.
- */
- public ColumnMapContext( ParserRuleContext ctx ) {
- super( ctx );
- }
-
- /**
- * Opens the XMLELEMENT.
- *
- * @return ",XMLELEMENT(" ...*
- */
- @Override
- public Token getStart() {
- return new Token(
- String.format( ",%s,%s.%s",
- startElement( path() ),
- getParentTableText(),
- getColumnText()
- )
- );
- }
-
- @Override
- public Token getStop() {
- return new Token(
- String.format( "%s%s",
- stopElement( path() ),
- super.getStop().toString()
- )
- );
- }
-
- /**
- * Returns the path from the column map context, rather than using
- * the superclass's table map context.
- *
- * @return The path associated with the column map parser rule.
- */
- protected QueryParser.PathContext path() {
- return getColumnMapContext().path();
- }
-}
-
source/java/com/whitemagicsoftware/rxm/tree/xml/JoinClause.java
-package com.whitemagicsoftware.rxm.tree.xml;
-
-import com.whitemagicsoftware.rxm.tree.Tree;
-
-import com.whitemagicsoftware.rxm.grammar.QueryBaseListener;
-import com.whitemagicsoftware.rxm.grammar.QueryParser;
-
-import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.tree.TerminalNode;
-
-/**
- * Transforms <b><code>table.column +&gt; table.column</code></b> into a SQL
- * <code>INNER JOIN</code> expression; similarly, transforms the operand
- * <code>-&gt;</code> into <code>OUTER JOIN</code>.
- */
-public class JoinClause extends Clause {
- /**
- * Default constructor.
- */
- public JoinClause() {
- }
-
- @Override
- public void enterJoinMap( QueryParser.JoinMapContext ctx ) {
- QueryParser.TableColumnContext
- lhs = ctx.tableColumn( 0 ),
- rhs = ctx.tableColumn( 1 );
-
- String lhsTable = lhs.table().getText();
-
- append( String.format( "%s JOIN %s %s ON%s%s%s = %s%s%s",
- ctx.T_OUTER() == null ? "INNER" : "OUTER",
- lhsTable,
- lhsTable,
- getNewlineIndent(),
- lhsTable,
- lhs.column().getText(),
- rhs.table().getText(),
- rhs.column().getText(),
- getNewline() )
- );
- }
-}
-
source/java/com/whitemagicsoftware/rxm/tree/xml/RootContext.java
-package com.whitemagicsoftware.rxm.tree.xml;
-
-import com.whitemagicsoftware.rxm.grammar.QueryParser;
-import com.whitemagicsoftware.rxm.tree.Token;
-
-import org.antlr.v4.runtime.ParserRuleContext;
-
-/**
- * Transforms <b><code>root &gt; element</code></b> into the starting
- * <code>SELECT</code> statement.
- */
-public class RootContext extends Context {
- public RootContext( ParserRuleContext ctx ) {
- super( ctx );
- }
-
- /**
- * Transforms into SELECT, XMLROOT, and XMLELEMENT.
- *
- * @return "SELECT XMLROOT(" ... + ",XMLELEMENT(" ...
- */
- @Override
- public Token getStart() {
- QueryParser.ElementContext element = getElementContext();
- String e = element.getText();
-
- String s = String.format( "XMLROOT(%s", startElement( e ) );
- return new Token( s );
- }
-
- /**
- * Closes the XMLELEMENT and XMLROOT.
- */
- @Override
- public Token getStop() {
- return new Token( String.format( ")%s)", getDeclaration() ) );
- }
-
- /**
- * Returns the value for the XML declaration.
- *
- * @return The XML declaration string in SQL/XML.
- */
- protected String getDeclaration() {
- return String.format( ",VERSION '%.1f',STANDALONE %s",
- getVersion(), isStandalone() ? "YES" : "NO" );
- }
-
- /**
- * Returns the XML version number, used for the <code>VERSION</code> value.
- *
- * @return 1.0f by default.
- */
- protected float getVersion() {
- return 1.0f;
- }
-
- /**
- * Indicates the value for <code>STANDALONE</code>.
- *
- * @return <code>true</code> STANDALONE is set <code>YES</code>;
- * <code>false</code> STANDALONE is set <code>NO</code>.
- */
- protected boolean isStandalone() {
- return true;
- }
-
- /**
- * Casts the payload into the proper context, then extracts the element.
- *
- * @return The element associated with the root parser rule.
- */
- protected QueryParser.ElementContext getElementContext() {
- return ((QueryParser.RootContext)getParserRuleContext()).element();
- }
-}
-
source/java/com/whitemagicsoftware/rxm/tree/xml/SelectClause.java
-package com.whitemagicsoftware.rxm.tree.xml;
-
-import com.whitemagicsoftware.rxm.grammar.QueryBaseListener;
-import com.whitemagicsoftware.rxm.grammar.QueryParser;
-import com.whitemagicsoftware.rxm.tree.Tree;
-
-import org.antlr.v4.runtime.ParserRuleContext;
-
-public class SelectClause extends Clause {
- private Tree<ParserRuleContext> tree;
-
- /**
- * Default constructor.
- */
- public SelectClause() {
- }
-
- /**
- * Adds the root context node to the top-most part of the tree. This
- * initializes the context tree to the root context node.
- *
- * @param ctx The payload to transform.
- */
- @Override
- public void enterRoot( QueryParser.RootContext ctx ) {
- setTree( createTree( new RootContext( ctx ) ) );
- }
-
- /**
- * Invoked when parsing <b><code>table &gt; path</code></b>. Adds a new
- * leaf to the context tree, then changes the context tree to the newly
- * added leaf.
- *
- * @param ctx The payload to transform.
- */
- @Override
- public void enterTableMap( QueryParser.TableMapContext ctx ) {
- setTree( addLeaf( new TableMapContext( ctx ) ) );
- }
-
- /**
- * Invoked when parsing <b><code>column &gt; path</code></b>. Adds a new
- * leaf to the context tree.
- *
- * @param ctx The payload to transform.
- */
- @Override
- public void enterColumnMap(
- QueryParser.ColumnMapContext ctx ) {
- addLeaf( new ColumnMapContext( ctx ) );
- }
-
- /**
- * Invoked when parsing <b><code>column &gt; attribute</code></b>. Adds
- * a new leaf to the context tree.
- *
- * @param ctx The payload to transform.
- */
- @Override
- public void enterAttributeMap(
- QueryParser.AttributeMapContext ctx ) {
- addLeaf( new AttributeMapContext( ctx ) );
- }
-
- /**
- * Invoked when parsing <b><code>^</code></b>. Changes context tree to
- * its parent. If the parent doesn't exist, this will fail silently.
- *
- * @param ctx Indicates that context should switch, has no payload.
- */
- @Override
- public void enterPop( QueryParser.PopContext ctx ) {
- Tree<ParserRuleContext> parent = getTree().getParent();
-
- if( parent != null ) {
- setTree( parent );
- }
- }
-
- /**
- * Called after the last mapping statement is found. This builds the
- * SELECT statement using the tree created while walking the <b>rxm</b>
- * source.
- *
- * @param ctx Unused.
- */
- @Override
- public void exitStatements( QueryParser.StatementsContext ctx ) {
- append( getTree().getRoot().toString() );
- }
-
- /**
- * Adds the given parser rule context to the current context tree.
- *
- * @param ctx The parser rule context to add as a leaf to the context tree.
- * @return The leaf (tree) that was added.
- */
- private Tree<ParserRuleContext> addLeaf( ParserRuleContext ctx ) {
- return getTree().addLeaf( createTree( ctx ) );
- }
-
- /**
- * Changes where the next mapped items will be attached.
- */
- private void setTree( Tree<ParserRuleContext> tree ) {
- this.tree = tree;
- }
-
- /**
- * Returns where to attach upcoming map items.
- */
- private Tree<ParserRuleContext> getTree() {
- return this.tree;
- }
-
- /**
- * Creates a new tree with the given parser rule context. This is required
- * so that the *Context classes are aware of their relative position in the
- * hierarchy. As a consequence of that knowledge, the objects have access
- * to the nearest table name, whether siblings exist, etc.
- *
- * @param ctx The context to wrap in a payload that is added to a tree.
- *
- * @return A new tree with a single data element and no branches.
- */
- private Tree<ParserRuleContext> createTree( ParserRuleContext ctx ) {
- return new SelectTree( ctx );
- }
-}
-
source/java/com/whitemagicsoftware/rxm/tree/xml/TableMapContext.java
-package com.whitemagicsoftware.rxm.tree.xml;
-
-import com.whitemagicsoftware.rxm.grammar.QueryParser;
-import com.whitemagicsoftware.rxm.tree.Token;
-
-import org.antlr.v4.runtime.ParserRuleContext;
-
-/**
- * Transforms <b><code>table &gt; path</code></b> into an
- * <code>XMLELEMENT</code> expression.
- */
-public class TableMapContext extends Context {
- public TableMapContext( ParserRuleContext ctx ) {
- super( ctx );
- }
-
- /**
- * Opens the XMLELEMENT.
- *
- * @return ",XMLELEMENT(" ...
- */
- @Override
- public Token getStart() {
- ParserRuleContext p = getParserRuleContext();
- QueryParser.TableMapContext ctx = (QueryParser.TableMapContext)p;
- String tableName = ctx.table(1).getText();
-
- return new Token( String.format( ",%s", startElement( tableName ) ) );
- }
-}
-
source/java/com/whitemagicsoftware/rxm/tree/xml/WhereClause.java
-package com.whitemagicsoftware.rxm.tree.xml;
-
-import com.whitemagicsoftware.rxm.grammar.QueryBaseListener;
-import com.whitemagicsoftware.rxm.grammar.QueryParser;
-
-import org.antlr.v4.runtime.tree.TerminalNode;
-
-/**
- */
-public class WhereClause extends Clause {
- /**
- * Default constructor.
- */
- public WhereClause() {
- }
-
- /**
- * Appends "<code>WHERE </code>" to the output.
- *
- * @param ctx The WHERE expression context (unused).
- */
- public void enterWhere( QueryParser.WhereContext ctx ) {
- append( "WHERE" ).append( getNewlineIndent() );
- }
-
- /**
- * Appends "<code> AND </code>" to the output.
- *
- * @param ctx The AND expression context (unused).
- */
- public void exitExprAnd( QueryParser.ExprAndContext ctx ) {
- append( getNewlineIndent() ).append( "AND " );
- }
-
- /**
- * Appends "<code> OR </code>" to the output.
- *
- * @param ctx The OR expression context (unused).
- */
- public void exitExprOr( QueryParser.ExprOrContext ctx ) {
- append( getNewlineIndent() ).append( "OR " );
- }
-
- /**
- * Appends "<code>(</code>" to the output.
- *
- * @param ctx The parenthesis expression context (unused).
- */
- public void enterExprParen( QueryParser.ExprParenContext ctx ) {
- append( "(" );
- }
-
- /**
- * Appends "<code>)</code>" to the output.
- *
- * @param ctx The parenthesis expression context (unused).
- */
- public void exitExprParen( QueryParser.ExprParenContext ctx ) {
- append( ")" );
- }
-
- /**
- * Transforms an <b>rxm</b> equality comparator into ANSI SQL.
- * Note: There is probably a cleaner way to implement this monstrosity.
- *
- * @param ctx The equality comparison context.
- */
- public void enterExprCompEqual( QueryParser.ExprCompEqualContext ctx ) {
- QueryParser.TableColumnContext tableColumn = ctx.tableColumn();
- QueryParser.ExprSetContext set = ctx.exprSet();
-
- // If the terminal node is null, then the equality is T_INEQ.
- TerminalNode equals = ctx.getToken( QueryParser.T_EQ, 0 );
-
- String entity = tableColumn.getText();
- String equalTo = (equals == null) ? "NOT " : "";
-
- if( set == null ) {
- // Presume = or <> by default (changes to IS or IS NOT as needed).
- String comparator = ctx.getChild(1).getText();
- QueryParser.LiteralContext literal = ctx.exprValue().literal();
-
- if( literal == null ) {
- // No literal value to examine means it is probably a parameter.
- append( ctx.getText() );
- }
- else {
- // Default is to presume the value literal is not the 'null' token.
- String rhs = literal.getText();
-
- // If the "null" literal terminal node is not null, then null has
- // been found and so the value must be set to NULL and the
- // comparators changed from symbols to ANSI SQL keywords.
- if( literal.T_NULL() != null ) {
- rhs = "NULL";
- comparator = " IS " + equalTo;
- }
-
- append( String.format( "%s%s%s", entity, comparator, rhs ) );
- }
- }
- else {
- // Transform to ANSI SQL set notation.
- append( String.format( "%s %sIN (%s)",
- entity,
- equalTo,
- set.exprList().getText() ) );
- }
- }
-
- /**
- * Appends a relational expression (less than, greater than, etc.) to the
- * output.
- *
- * @param ctx The relational expression context.
- */
- public void enterExprCompRel( QueryParser.ExprCompRelContext ctx ) {
- append( ctx.getText() );
- }
-}
-
source/java/com/whitemagicsoftware/rxm/xml/AttributeMapContext.java
+package com.whitemagicsoftware.rxm.xml;
+
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+import com.whitemagicsoftware.rxm.tree.Tree;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+/**
+ * Transforms <b><code>table &gt; @attribute</code></b> into an
+ * <code>XMLATTRIBUTES</code> expression.
+ */
+public class AttributeMapContext extends Context {
+ private boolean hasPrecedingPayload = false;
+ private boolean hasFollowingPayload = false;
+
+ public AttributeMapContext( ParserRuleContext ctx ) {
+ super( ctx );
+ }
+
+ /**
+ * Opens the XMLATTRIBUTES expression.
+ *
+ * @return Token containing ",XMLATTRIBUTES(" ...
+ */
+ @Override
+ public Token getStart() {
+ String
+ tableName = getParentTableText(),
+ columnName = getColumnText(),
+ attributeName = getAttributeText();
+
+ return new Token(
+ String.format( ",%s", hasPrecedingPayload() ?
+ nextAttribute( tableName, columnName, attributeName ) :
+ firstAttribute( tableName, columnName, attributeName )
+ )
+ );
+ }
+
+ private boolean hasPrecedingPayload() {
+ return this.hasPrecedingPayload;
+ }
+
+ private boolean hasFollowingPayload() {
+ return this.hasFollowingPayload;
+ }
+
+ /**
+ * Closes the XMLATTRIBUTES expression, should no more attributes be
+ * forthcoming.
+ *
+ * @return Token containing ")" or the empty string.
+ */
+ @Override
+ public Token getStop() {
+ return new Token( hasFollowingPayload() ? "" : ")" );
+ }
+
+ private String getAttributeText() {
+ return attribute().getChild(1).getText();
+ }
+
+ /**
+ * Returns the attribute (node) name.
+ */
+ private QueryParser.AttributeContext attribute() {
+ return getAttributeMapContext().attribute();
+ }
+
+ /**
+ * Returns the attribute map context that this class wraps.
+ */
+ private QueryParser.AttributeMapContext getAttributeMapContext() {
+ return (QueryParser.AttributeMapContext)getParserRuleContext();
+ }
+
+ @Override
+ protected QueryParser.ColumnContext column() {
+ return getAttributeMapContext().column();
+ }
+
+ /**
+ * Formats the start of the XMLATTRIBUTES call.
+ *
+ * @param table The table name assigned to the attribute.
+ * @param column The column name assigned to the attribute.
+ * @param node The document node name assigned to the attribute.
+ *
+ * @return The text used for SQL/XML XMLATTRIBUTES calls.
+ */
+ private String firstAttribute( String table, String column, String node ) {
+ return "XMLATTRIBUTES(" + nextAttribute( table, column, node );
+ }
+
+ /**
+ * Formats the next attribute of the XMLATTRIBUTES call. This is only
+ * called when there are contiguous attributes listed in the <b>rxm</b>.
+ *
+ * @param table The table name assigned to the attribute.
+ * @param column The column name assigned to the attribute.
+ * @param node The document node name assigned to the attribute.
+ *
+ * @return The text used for SQL/XML XMLATTRIBUTES calls.
+ */
+ private String nextAttribute( String table, String column, String node ) {
+ String result = "%s.%s%s";
+
+ // If the column name and attribute name are the same, then the
+ // "AS" clause is superfluous.
+ if( column.equals( node ) ) {
+ result = String.format( result, table, column, "" );
+ }
+ else {
+ node = String.format( " AS \"%s\"", node );
+ result = String.format( result, table, column, node );
+ }
+
+ return result;
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/Clause.java
+package com.whitemagicsoftware.rxm.xml;
+
+import java.nio.CharBuffer;
+
+import com.whitemagicsoftware.rxm.grammar.QueryBaseListener;
+
+/**
+ * Common behaviour for various clauses.
+ */
+public class Clause extends QueryBaseListener {
+ private static int INDENT = 2;
+
+ private StringBuilder buffer = new StringBuilder( 2048 );
+
+ /**
+ * Default constructor.
+ */
+ public Clause() {
+ }
+
+ /**
+ * Appends a given string to the buffer.
+ *
+ * @param s The string to append.
+ * @return this to chain the append calls.
+ */
+ protected Clause append( String s ) {
+ getBuffer().append( s );
+ return this;
+ }
+
+ /**
+ * Returns the buffer converted to a string. This is the result of
+ * the transformed output and should only be called when the parsing
+ * of the <b>rxm</b> source is complete.
+ *
+ * @return A non-null string, possibly empty.
+ */
+ public String toString() {
+ return getBuffer().toString();
+ }
+
+ /**
+ * Returns the buffer used for building the transformed output.
+ *
+ * @return A non-null string builder, possibly empty.
+ */
+ private StringBuilder getBuffer() {
+ return this.buffer;
+ }
+
+ /**
+ * Answers whether the transformed query should be indented with newlines.
+ *
+ * @return true Format the output (by default).
+ */
+ protected boolean beautify() {
+ // Returns the System property value.
+ return Boolean.getBoolean( "beautify" );
+ }
+
+ /**
+ * When beautify is enabled, this returns a newline followed by
+ * the default amount ofindentation; when disabled, this returns a single
+ * space.
+ *
+ * @return A newline followed by spaces or a single space.
+ */
+ protected String getNewlineIndent() {
+ return getNewline() + (beautify() ? getDefaultIndent() : " ");
+ }
+
+ /**
+ * Creates a string of spaces that is spaces spaces long.
+ *
+ * @param spaces The number of spaces to add to the string.
+ * @return A string with 'spaces' number of " " padding.
+ */
+ protected String getIndent( int spaces ) {
+ return CharBuffer.allocate( spaces ).toString().replace( '\0', ' ' );
+ }
+
+ /**
+ * Returns the system newline separator character sequence.
+ *
+ * @return CR/LF on Windows, LF on Unix, CR on MacOS, or " " if beautify
+ * is set true.
+ */
+ protected String getNewline() {
+ return beautify() ? System.lineSeparator() : " ";
+ }
+
+ /**
+ * Returns the default amount of spaces.
+ *
+ * @return " " (by default)
+ */
+ protected String getDefaultIndent() {
+ return getIndent( getIndentBy() );
+ }
+
+ /**
+ * Returns the number of spaces to indent by.
+ *
+ * @return 2 (by default)
+ */
+ protected int getIndentBy() {
+ return INDENT;
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/ColumnMapContext.java
+package com.whitemagicsoftware.rxm.xml;
+
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+import com.whitemagicsoftware.rxm.tree.Tree;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+/**
+ * Transforms <b><code>column &gt; path</code></b> into an
+ * <code>XMLELEMENT</code> expression.
+ */
+public class ColumnMapContext extends Context {
+ /**
+ * Default constructor (calls super).
+ *
+ * @param ctx The payload that associates the parser rule context with
+ * the abstract syntax tree.
+ */
+ public ColumnMapContext( ParserRuleContext ctx ) {
+ super( ctx );
+ }
+
+ /**
+ * Opens the XMLELEMENT.
+ *
+ * @return ",XMLELEMENT(" ...*
+ */
+ @Override
+ public Token getStart() {
+ return new Token(
+ String.format( ",%s,%s.%s",
+ startElement( path() ),
+ getParentTableText(),
+ getColumnText()
+ )
+ );
+ }
+
+ @Override
+ public Token getStop() {
+ return new Token(
+ String.format( "%s%s",
+ stopElement( path() ),
+ super.getStop().toString()
+ )
+ );
+ }
+
+ /**
+ * Returns the path from the column map context, rather than using
+ * the superclass's table map context.
+ *
+ * @return The path associated with the column map parser rule.
+ */
+ protected QueryParser.PathContext path() {
+ return getColumnMapContext().path();
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/Context.java
+package com.whitemagicsoftware.rxm.xml;
+
+import java.util.List;
+
+import com.whitemagicsoftware.rxm.tree.Tree;
+
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+/**
+ * Represents a wrapper class for ParserRuleContext.
+ */
+public abstract class Context extends ParserRuleContext {
+ private ParserRuleContext parserRuleContext;
+
+ /**
+ * Constructs a wrapper class that provides the ParserRuleContext and
+ * tree for the *Context classes.
+ *
+ * @param payload The data that contains the ParserRuleContext.
+ */
+ protected Context( ParserRuleContext payload ) {
+ setParserRuleContext( payload );
+ }
+
+ /**
+ * Opens the current transformation expression. Subclasses must override
+ * this to provide the correct opening SQL/XML expression. It could be
+ * made abstract, but is a useful convenience method.
+ *
+ * @return "("
+ */
+ @Override
+ public Token getStart() {
+ return new Token( "(" );
+ }
+
+ /**
+ * Closes the current transformation expression. Subclasses may override
+ * this to provide a custom closing SQL/XML expression.
+ *
+ * @return ")"
+ */
+ @Override
+ public Token getStop() {
+ return new Token( ")" );
+ }
+
+ /**
+ * Formats the start of the XMLELEMENT call.
+ *
+ * @param ctx The name assigned to the element.
+ *
+ * @return The text used for SQL/XML XMLELEMENT calls.
+ */
+ protected String startElement( QueryParser.PathContext ctx ) {
+ QueryParser.ElementContext element = ctx.element();
+
+ return element == null ?
+ startElement( ctx.elementPath() ) :
+ startElement( element );
+ }
+
+ protected String startElement( QueryParser.ElementPathContext ctx ) {
+ List<QueryParser.ElementContext> elements = ctx.element();
+ StringBuilder sb = new StringBuilder( 2048 );
+ String sep = "";
+
+ for( QueryParser.ElementContext element : elements ) {
+ sb.append( sep );
+ sb.append( startElement( element ) );
+ sep = ",";
+ }
+
+ return sb.toString();
+ }
+
+ protected String startElement( QueryParser.ElementContext ctx ) {
+ return startElement( ctx.getText() );
+ }
+
+ /**
+ * Formats the start of the XMLELEMENT call.
+ *
+ * @param name The name assigned to the element.
+ *
+ * @return The text used for SQL/XML XMLELEMENT calls.
+ */
+ protected String startElement( String name ) {
+ return String.format( "XMLELEMENT(NAME \"%s\"", name );
+ }
+
+ protected String stopElement( QueryParser.PathContext ctx ) {
+ QueryParser.ElementContext element = ctx.element();
+
+ return element == null ? stopElement( ctx.elementPath() ) : "";
+ }
+
+ protected String stopElement( QueryParser.ElementPathContext ctx ) {
+ List<QueryParser.ElementContext> elements = ctx.element();
+ StringBuilder sb = new StringBuilder( 2048 );
+ String sep = "";
+
+ for( QueryParser.ElementContext element : elements ) {
+ sb.append( sep );
+ sep = ")";
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * This will return column name without the preceding period.
+ *
+ * @return The column name, without a leading period.
+ */
+ protected String getColumnText() {
+ // The first child (index 0) is the leading period.
+ // The second child (index 1) is the column name.
+ return column().getChild(1).getText();
+ }
+
+ protected String getParentTableText() {
+ return getPayload().getText();
+ }
+
+ /**
+ * Returns the column name, if any. This should not be called
+ * if the parser rule context doesn't know about columns.
+ *
+ * @return A column instance, but could throw a class cast exception.
+ */
+ protected QueryParser.ColumnContext column() {
+ return getColumnMapContext().column();
+ }
+
+ /**
+ * Returns the table associated with the payload.
+ *
+ * @return The table from the table map context.
+ */
+ protected QueryParser.TableContext table() {
+ return getTableMapContext().table(0);
+ }
+
+ /**
+ * Casts the payload into the proper context, then extracts the column.
+ *
+ * @return The column associated with the column map parser rule.
+ */
+ protected QueryParser.ColumnMapContext getColumnMapContext() {
+ return (QueryParser.ColumnMapContext)getParserRuleContext();
+ }
+
+ /**
+ * Returns the nearest table context to this parser rule context.
+ *
+ * @return The wrapped parser rule context's table map context.
+ */
+ protected QueryParser.TableMapContext getTableMapContext() {
+ return (QueryParser.TableMapContext)getParserRuleContext();
+ }
+
+ private void setParserRuleContext( ParserRuleContext ctx ) {
+ this.parserRuleContext = ctx;
+ }
+
+ /**
+ * Returns the payload used to associate the parser rule context with
+ * a tree.
+ *
+ * @return The payload containing a parser rule context and tree.
+ */
+ protected ParserRuleContext getParserRuleContext() {
+ return this.parserRuleContext;
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/FromClause.java
+package com.whitemagicsoftware.rxm.xml;
+
+import com.whitemagicsoftware.rxm.QueryBuilder;
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+
+/**
+ * Transforms the first <b><code>table &gt; table</code></b> into a SQL
+ * <code>FROM</code> expression.
+ */
+public class FromClause extends Clause {
+ private QueryBuilder builder;
+
+ /**
+ * Default constructor.
+ *
+ * @param builder Required to stop listening to events once the first
+ * table is obtained.
+ */
+ public FromClause( QueryBuilder builder ) {
+ this.builder = builder;
+ }
+
+ /**
+ * Returns true if the from clause is ready for writing.
+ *
+ * @return true Stop listening for events.
+ */
+ public boolean complete() {
+ return toString().length() > 0;
+ }
+
+ @Override
+ public void enterTableMap( QueryParser.TableMapContext ctx ) {
+ String tableName = ctx.table(0).getText();
+
+ append(
+ String.format( "FROM%s %s %s%s",
+ getNewlineIndent(),
+ tableName,
+ tableName,
+ getNewline()
+ )
+ );
+
+ getBuilder().rescindEvents( this );
+ }
+
+ private QueryBuilder getBuilder() {
+ return this.builder;
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/JoinClause.java
+package com.whitemagicsoftware.rxm.xml;
+
+import com.whitemagicsoftware.rxm.tree.Tree;
+
+import com.whitemagicsoftware.rxm.grammar.QueryBaseListener;
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+/**
+ * Transforms <b><code>table.column +&gt; table.column</code></b> into a SQL
+ * <code>INNER JOIN</code> expression; similarly, transforms the operand
+ * <code>-&gt;</code> into <code>OUTER JOIN</code>.
+ */
+public class JoinClause extends Clause {
+ /**
+ * Default constructor.
+ */
+ public JoinClause() {
+ }
+
+ @Override
+ public void enterJoinMap( QueryParser.JoinMapContext ctx ) {
+ QueryParser.TableColumnContext
+ lhs = ctx.tableColumn( 0 ),
+ rhs = ctx.tableColumn( 1 );
+
+ String lhsTable = lhs.table().getText();
+
+ append( String.format( "%s JOIN %s %s ON%s%s%s = %s%s%s",
+ ctx.T_OUTER() == null ? "INNER" : "OUTER",
+ lhsTable,
+ lhsTable,
+ getNewlineIndent(),
+ lhsTable,
+ lhs.column().getText(),
+ rhs.table().getText(),
+ rhs.column().getText(),
+ getNewline() )
+ );
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/RootContext.java
+package com.whitemagicsoftware.rxm.xml;
+
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+/**
+ * Transforms <b><code>root &gt; element</code></b> into the starting
+ * <code>SELECT</code> statement.
+ */
+public class RootContext extends Context {
+ public RootContext( ParserRuleContext ctx ) {
+ super( ctx );
+ }
+
+ /**
+ * Transforms into SELECT, XMLROOT, and XMLELEMENT.
+ *
+ * @return "SELECT XMLROOT(" ... + ",XMLELEMENT(" ...
+ */
+ @Override
+ public Token getStart() {
+ QueryParser.ElementContext element = getElementContext();
+ String e = element.getText();
+
+ String s = String.format( "XMLROOT(%s", startElement( e ) );
+ return new Token( s );
+ }
+
+ /**
+ * Closes the XMLELEMENT and XMLROOT.
+ */
+ @Override
+ public Token getStop() {
+ return new Token( String.format( ")%s)", getDeclaration() ) );
+ }
+
+ /**
+ * Returns the value for the XML declaration.
+ *
+ * @return The XML declaration string in SQL/XML.
+ */
+ protected String getDeclaration() {
+ return String.format( ",VERSION '%.1f',STANDALONE %s",
+ getVersion(), isStandalone() ? "YES" : "NO" );
+ }
+
+ /**
+ * Returns the XML version number, used for the <code>VERSION</code> value.
+ *
+ * @return 1.0f by default.
+ */
+ protected float getVersion() {
+ return 1.0f;
+ }
+
+ /**
+ * Indicates the value for <code>STANDALONE</code>.
+ *
+ * @return <code>true</code> STANDALONE is set <code>YES</code>;
+ * <code>false</code> STANDALONE is set <code>NO</code>.
+ */
+ protected boolean isStandalone() {
+ return true;
+ }
+
+ /**
+ * Casts the payload into the proper context, then extracts the element.
+ *
+ * @return The element associated with the root parser rule.
+ */
+ protected QueryParser.ElementContext getElementContext() {
+ return ((QueryParser.RootContext)getParserRuleContext()).element();
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/SelectClause.java
+package com.whitemagicsoftware.rxm.xml;
+
+import com.whitemagicsoftware.rxm.grammar.QueryBaseListener;
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+import com.whitemagicsoftware.rxm.tree.Tree;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+public class SelectClause extends Clause {
+ private Tree<ParserRuleContext> tree;
+
+ /**
+ * Default constructor.
+ */
+ public SelectClause() {
+ }
+
+ /**
+ * Adds the root context node to the top-most part of the tree. This
+ * initializes the context tree to the root context node.
+ *
+ * @param ctx The payload to transform.
+ */
+ @Override
+ public void enterRoot( QueryParser.RootContext ctx ) {
+ setTree( createTree( new RootContext( ctx ) ) );
+ }
+
+ /**
+ * Invoked when parsing <b><code>table &gt; path</code></b>. Adds a new
+ * leaf to the context tree, then changes the context tree to the newly
+ * added leaf.
+ *
+ * @param ctx The payload to transform.
+ */
+ @Override
+ public void enterTableMap( QueryParser.TableMapContext ctx ) {
+ setTree( addLeaf( new TableMapContext( ctx ) ) );
+ }
+
+ /**
+ * Invoked when parsing <b><code>column &gt; path</code></b>. Adds a new
+ * leaf to the context tree.
+ *
+ * @param ctx The payload to transform.
+ */
+ @Override
+ public void enterColumnMap(
+ QueryParser.ColumnMapContext ctx ) {
+ addLeaf( new ColumnMapContext( ctx ) );
+ }
+
+ /**
+ * Invoked when parsing <b><code>column &gt; attribute</code></b>. Adds
+ * a new leaf to the context tree.
+ *
+ * @param ctx The payload to transform.
+ */
+ @Override
+ public void enterAttributeMap(
+ QueryParser.AttributeMapContext ctx ) {
+ addLeaf( new AttributeMapContext( ctx ) );
+ }
+
+ /**
+ * Invoked when parsing <b><code>^</code></b>. Changes context tree to
+ * its parent. If the parent doesn't exist, this will fail silently.
+ *
+ * @param ctx Indicates that context should switch, has no payload.
+ */
+ @Override
+ public void enterPop( QueryParser.PopContext ctx ) {
+ Tree<ParserRuleContext> parent = getTree().getParent();
+
+ if( parent != null ) {
+ setTree( parent );
+ }
+ }
+
+ /**
+ * Called after the last mapping statement is found. This builds the
+ * SELECT statement using the tree created while walking the <b>rxm</b>
+ * source.
+ *
+ * @param ctx Unused.
+ */
+ @Override
+ public void exitStatements( QueryParser.StatementsContext ctx ) {
+ append( getTree().getRoot().toString() );
+ }
+
+ /**
+ * Adds the given parser rule context to the current context tree.
+ *
+ * @param ctx The parser rule context to add as a leaf to the context tree.
+ * @return The leaf (tree) that was added.
+ */
+ private Tree<ParserRuleContext> addLeaf( ParserRuleContext ctx ) {
+ return getTree().addLeaf( createTree( ctx ) );
+ }
+
+ /**
+ * Changes where the next mapped items will be attached.
+ */
+ private void setTree( Tree<ParserRuleContext> tree ) {
+ this.tree = tree;
+ }
+
+ /**
+ * Returns where to attach upcoming map items.
+ */
+ private Tree<ParserRuleContext> getTree() {
+ return this.tree;
+ }
+
+ /**
+ * Creates a new tree with the given parser rule context. This is required
+ * so that the *Context classes are aware of their relative position in the
+ * hierarchy. As a consequence of that knowledge, the objects have access
+ * to the nearest table name, whether siblings exist, etc.
+ *
+ * @param ctx The context to wrap in a payload that is added to a tree.
+ *
+ * @return A new tree with a single data element and no branches.
+ */
+ private Tree<ParserRuleContext> createTree( ParserRuleContext ctx ) {
+ return new SelectTree( ctx );
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/SelectTree.java
+package com.whitemagicsoftware.rxm.xml;
+
+import java.nio.CharBuffer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.whitemagicsoftware.rxm.tree.Tree;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.Token;
+
+/**
+ * This creates a tree capable of generating a SQL/XML SELECT clause.
+ */
+public class SelectTree extends Tree<ParserRuleContext> {
+ public static final int INDENT = 2;
+
+ /**
+ * Constructs a tree capable of transforming itself into a SQL/XML
+ * SELECT clause.
+ *
+ * @param payload The data associated with this tree leaf (must not
+ * be null).
+ */
+ public SelectTree( ParserRuleContext payload ) {
+ super( payload );
+ }
+
+ public String toString() {
+ return toString( this, 0 );
+ }
+
+ /*
+ * This converts all payload instances in the tree to their transformed
+ * equivalent using a depth-first traversal.
+ *
+ * @param tree The tree to transform.
+ * @param depth Indicates number of spaces for indentation.
+ *
+ * @return The fully transformed text.
+ */
+ protected String toString( Tree<ParserRuleContext> tree, int depth ) {
+ StringBuilder sb = new StringBuilder( 2048 );
+ String indent = "", newline = "";
+
+ if( beautify() ) {
+ indent = getIndent( depth );
+ newline = getNewline();
+ }
+
+ sb.append( indent ).append( start( tree ) );
+
+ for( Tree<ParserRuleContext> branch : tree.getBranches() ) {
+ // Recurse for all the branches at this level of the tree.
+ sb.append( newline ).append( toString( branch, depth + getIndentBy() ) );
+ }
+
+ sb.append( newline ).append( indent );
+
+ return sb.append( stop( tree ) ).toString();
+ }
+
+ /**
+ * Returns a list of contiguous payloads that are of the same class as
+ * the given payload.
+ */
+ private List<ParserRuleContext> getConsecutiveRules( ParserRuleContext p ) {
+ List<ParserRuleContext> result = new ArrayList<ParserRuleContext>();
+
+ for( Tree<ParserRuleContext> branch : getBranches() ) {
+ if( branch.isPayloadClass( p.getClass() ) ) {
+ result.add( branch.getPayload() );
+ }
+ else {
+ // No more adjacent payloads.
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Answers whether the payload is preceded by at least one sibling. The
+ * siblings must not be split by any other type of playload.
+ *
+ * @param payload The payload with a class that is used as a reference for
+ * finding contiguous payloads.
+ *
+ * @return true There is at least one sibling of the same payload type
+ * that is before the given payload.
+ */
+ @Override
+ public boolean hasPrecedingPayload( ParserRuleContext payload ) {
+ return getConsecutiveRules( payload ).indexOf( payload ) > 0;
+ }
+
+ /**
+ * Answers whether the payload is followed by at least one sibling. The
+ * siblings must not be split by any other type of playload.
+ *
+ * @param payload The payload with a class that is used as a reference for
+ * finding contiguous payloads.
+ *
+ * @return true There is at least one sibling of the same payload type
+ * that is after the given payload.
+ */
+ @Override
+ public boolean hasFollowingPayload( ParserRuleContext payload ) {
+ List<ParserRuleContext> siblings = getConsecutiveRules( payload );
+ int index = siblings.indexOf( payload );
+
+ return index > -1 && (index + 1 < siblings.size());
+ }
+
+ /**
+ * Returns the transformed syntax for beginning the payload item.
+ *
+ * @param tree The tree that has a payload that contains a token to
+ * transform into a string.
+ * @return A non-null string, possibly empty.
+ */
+ protected String start( Tree<ParserRuleContext> tree ) {
+ return start( tree.getPayload() );
+ }
+
+ /**
+ * Helper method for returning the start string.
+ *
+ * @param payload The payload that contains a token to transform into
+ * a string.
+ * @return The start string for the parsed item; possibly empty, never null.
+ */
+ protected String start( ParserRuleContext payload ) {
+ return start( payload.getStart() );
+ }
+
+ /**
+ * Helper method for returning the start string.
+ *
+ * @param token The token to transform into a string.
+ * @return The start string for the parsed item; possibly empty, never null.
+ */
+ protected String start( Token token ) {
+ return token.toString();
+ }
+
+ /**
+ * Returns the transformed syntax for ending the payload item.
+ *
+ * @param tree The tree that has a payload that contains a token to
+ * transform into a string.
+ * @return A non-null string, possibly empty.
+ */
+ protected String stop( Tree<ParserRuleContext> tree ) {
+ return stop( tree.getPayload() );
+ }
+
+ /**
+ * Helper method for returning the stop string.
+ *
+ * @param payload The payload that contains a token to transform into
+ * a string.
+ * @return The stop string for the parsed item; possibly empty, never null.
+ */
+ protected String stop( ParserRuleContext payload ) {
+ return stop( payload.getStop() );
+ }
+
+ /**
+ * Helper method for returning the start string.
+ *
+ * @param token The token to transform into a string.
+ * @return The start string for the parsed item; possibly empty, never null.
+ */
+ protected String stop( Token token ) {
+ return token.toString();
+ }
+
+ protected String getNewline() {
+ return System.lineSeparator();
+ }
+
+ /**
+ * Answers whether the transformed query should be indented with newlines.
+ *
+ * @return true Format the output (by default).
+ */
+ protected boolean beautify() {
+ // Returns the System property value.
+ return Boolean.getBoolean( "beautify" );
+ }
+
+ /**
+ * Used by subclasses to beautify the leading SQL clause (e.g., SELECT,
+ * FROM, WHERE).
+ *
+ * @param spaces The number of spaces to include in the result.
+ * @return A non-null string with 'spaces' amount of space.
+ */
+ protected String getSpace( int spaces ) {
+ return beautify() ? getNewline() + getIndent( spaces ) : " ";
+ }
+
+ /**
+ * Helper method to return the default space amount.
+ *
+ * @return A non-null string with the default amount of space.
+ */
+ protected String getSpace() {
+ return getSpace( getIndentBy() );
+ }
+
+ /**
+ * Creates a string of spaces that is spaces spaces long.
+ *
+ * @param spaces The number of spaces to add to the string.
+ * @return A string with 'spaces' number of " " padding.
+ */
+ protected String getIndent( int spaces ) {
+ return CharBuffer.allocate( spaces ).toString().replace( '\0', ' ' );
+ }
+
+ /**
+ * Returns the default amount of spaces.
+ *
+ * @return " " (by default)
+ */
+ protected String getDefaultIndent() {
+ return getIndent( getIndentBy() );
+ }
+
+ /**
+ * Returns the number of spaces to indent by.
+ *
+ * @return 2 (by default)
+ */
+ protected int getIndentBy() {
+ return INDENT;
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/TableMapContext.java
+package com.whitemagicsoftware.rxm.xml;
+
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+/**
+ * Transforms <b><code>table &gt; path</code></b> into an
+ * <code>XMLELEMENT</code> expression.
+ */
+public class TableMapContext extends Context {
+ public TableMapContext( ParserRuleContext ctx ) {
+ super( ctx );
+ }
+
+ /**
+ * Opens the XMLELEMENT.
+ *
+ * @return ",XMLELEMENT(" ...
+ */
+ @Override
+ public Token getStart() {
+ ParserRuleContext p = getParserRuleContext();
+ QueryParser.TableMapContext ctx = (QueryParser.TableMapContext)p;
+ String tableName = ctx.table(1).getText();
+
+ return new Token( String.format( ",%s", startElement( tableName ) ) );
+ }
+}
+
source/java/com/whitemagicsoftware/rxm/xml/WhereClause.java
+package com.whitemagicsoftware.rxm.xml;
+
+import com.whitemagicsoftware.rxm.grammar.QueryBaseListener;
+import com.whitemagicsoftware.rxm.grammar.QueryParser;
+
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+/**
+ */
+public class WhereClause extends Clause {
+ /**
+ * Default constructor.
+ */
+ public WhereClause() {
+ }
+
+ /**
+ * Appends "<code>WHERE </code>" to the output.
+ *
+ * @param ctx The WHERE expression context (unused).
+ */
+ public void enterWhere( QueryParser.WhereContext ctx ) {
+ append( "WHERE" ).append( getNewlineIndent() );
+ }
+
+ /**
+ * Appends "<code> AND </code>" to the output.
+ *
+ * @param ctx The AND expression context (unused).
+ */
+ public void exitExprAnd( QueryParser.ExprAndContext ctx ) {
+ append( getNewlineIndent() ).append( "AND " );
+ }
+
+ /**
+ * Appends "<code> OR </code>" to the output.
+ *
+ * @param ctx The OR expression context (unused).
+ */
+ public void exitExprOr( QueryParser.ExprOrContext ctx ) {
+ append( getNewlineIndent() ).append( "OR " );
+ }
+
+ /**
+ * Appends "<code>(</code>" to the output.
+ *
+ * @param ctx The parenthesis expression context (unused).
+ */
+ public void enterExprParen( QueryParser.ExprParenContext ctx ) {
+ append( "(" );
+ }
+
+ /**
+ * Appends "<code>)</code>" to the output.
+ *
+ * @param ctx The parenthesis expression context (unused).
+ */
+ public void exitExprParen( QueryParser.ExprParenContext ctx ) {
+ append( ")" );
+ }
+
+ /**
+ * Transforms an <b>rxm</b> equality comparator into ANSI SQL.
+ * Note: There is probably a cleaner way to implement this monstrosity.
+ *
+ * @param ctx The equality comparison context.
+ */
+ public void enterExprCompEqual( QueryParser.ExprCompEqualContext ctx ) {
+ QueryParser.TableColumnContext tableColumn = ctx.tableColumn();
+ QueryParser.ExprSetContext set = ctx.exprSet();
+
+ // If the terminal node is null, then the equality is T_INEQ.
+ TerminalNode equals = ctx.getToken( QueryParser.T_EQ, 0 );
+
+ String entity = tableColumn.getText();
+ String equalTo = (equals == null) ? "NOT " : "";
+
+ if( set == null ) {
+ // Presume = or <> by default (changes to IS or IS NOT as needed).
+ String comparator = ctx.getChild(1).getText();
+ QueryParser.LiteralContext literal = ctx.exprValue().literal();
+
+ if( literal == null ) {
+ // No literal value to examine means it is probably a parameter.
+ append( ctx.getText() );
+ }
+ else {
+ // Default is to presume the value literal is not the 'null' token.
+ String rhs = literal.getText();
+
+ // If the "null" literal terminal node is not null, then null has
+ // been found and so the value must be set to NULL and the
+ // comparators changed from symbols to ANSI SQL keywords.
+ if( literal.T_NULL() != null ) {
+ rhs = "NULL";
+ comparator = " IS " + equalTo;
+ }
+
+ append( String.format( "%s%s%s", entity, comparator, rhs ) );
+ }
+ }
+ else {
+ // Transform to ANSI SQL set notation.
+ append( String.format( "%s %sIN (%s)",
+ entity,
+ equalTo,
+ set.exprList().getText() ) );
+ }
+ }
+
+ /**
+ * Appends a relational expression (less than, greater than, etc.) to the
+ * output.
+ *
+ * @param ctx The relational expression context.
+ */
+ public void enterExprCompRel( QueryParser.ExprCompRelContext ctx ) {
+ append( ctx.getText() );
+ }
+}
+
Delta 1170 lines added, 719 lines removed, 451-line increase