| # Overview | ||
| -Relational eXpression Map (**rxm**) is a terse database query language that | ||
| -decouples structured document formats from SQL statements. | ||
| +Relational eXpression Map (**rxm**) is a database- and output-agnostic query | ||
| +language for generating structured documents. | ||
| # Requirements |
| #!/bin/bash | ||
| -java -Drxm_beautify=true -jar build/libs/rxm.jar $1 | ||
| -#java -jar build/libs/rxm.jar $1 | ||
| +java -Dbeautify=true -jar build/libs/rxm.jar $1 | ||
| expression : exprEquality | exprLogicalAnd ; | ||
| -exprLogicalAnd: exprLogicalOr (T_LOGIC_AND exprLogicalOr)* ; | ||
| -exprLogicalOr : exprRelational (T_LOGIC_OR exprRelational)* ; | ||
| +exprLogicalAnd: exprLogicalOr (exprAnd exprLogicalOr)* ; | ||
| +exprLogicalOr : exprRelational (exprOr exprRelational)* ; | ||
| exprRelational: exprEquality | exprCompRel ; | ||
| exprEquality : exprParen | exprCompEqual ; | ||
| exprParen : T_EXPR_OPEN expression T_EXPR_CLOSE ; | ||
| + | ||
| +exprAnd : T_LOGIC_AND ; | ||
| +exprOr : T_LOGIC_OR ; | ||
| exprCompRel : tableColumn (T_LT | T_GT | T_LTE | T_GTE) exprValue ; | ||
| -exprCompEqual : tableColumn (T_EQ | T_INEQ) exprValueOrSet ; | ||
| +exprCompEqual : tableColumn (T_EQ | T_INEQ) (exprValue | exprSet) ; | ||
| exprValue : exprParameter | literal ; | ||
| -exprValueOrSet: exprValue | exprSet ; | ||
| -exprSet : T_EXPR_SET_OPEN exprList T_EXPR_SET_CLOSE ; | ||
| -exprList : exprValue (T_COMMA exprValue)* ; | ||
| exprParameter : T_EXPR_PARAMETER T_ID ; | ||
| literal : T_NULL | T_STRING | T_FLOAT ; | ||
| + | ||
| +exprSet : T_EXPR_SET_OPEN exprList T_EXPR_SET_CLOSE ; | ||
| +exprList : exprValue (T_COMMA exprValue)* ; | ||
| import com.whitemagicsoftware.rxm.tree.xml.SelectClauseTree; | ||
| import com.whitemagicsoftware.rxm.tree.xml.JoinClauseList; | ||
| -import com.whitemagicsoftware.rxm.tree.xml.WhereClauseList; | ||
| +import com.whitemagicsoftware.rxm.tree.xml.WhereClause; | ||
| import org.antlr.v4.runtime.ANTLRInputStream; | ||
| private Tree<Payload> joinClauseList; | ||
| - /** List of where clause expressions (tree used as flat hierarchy). */ | ||
| - private Tree<Payload> whereClauseList; | ||
| + private WhereClause whereClause = new WhereClause(); | ||
| /** | ||
| } | ||
| + @Override | ||
| public synchronized void enterWhere( QueryParser.WhereContext ctx ) { | ||
| - addWhereClause( ctx ); | ||
| + getWhereClause().enterWhere( ctx ); | ||
| + } | ||
| + | ||
| + @Override | ||
| + public void exitExprOr( QueryParser.ExprOrContext ctx ) { | ||
| + getWhereClause().exitExprOr( ctx ); | ||
| + } | ||
| + | ||
| + @Override | ||
| + public void exitExprAnd( QueryParser.ExprAndContext ctx ) { | ||
| + getWhereClause().exitExprAnd( ctx ); | ||
| + } | ||
| + | ||
| + @Override | ||
| + public synchronized void enterExprParen( QueryParser.ExprParenContext ctx ) { | ||
| + getWhereClause().enterExprParen( ctx ); | ||
| + } | ||
| + | ||
| + @Override | ||
| + public synchronized void exitExprParen( QueryParser.ExprParenContext ctx ) { | ||
| + getWhereClause().exitExprParen( ctx ); | ||
| + } | ||
| + | ||
| + @Override | ||
| + public synchronized void enterExprCompRel( | ||
| + QueryParser.ExprCompRelContext ctx ) { | ||
| + getWhereClause().enterExprCompRel( ctx ); | ||
| + } | ||
| + | ||
| + @Override | ||
| + public synchronized void enterExprCompEqual( | ||
| + QueryParser.ExprCompEqualContext ctx ) { | ||
| + getWhereClause().enterExprCompEqual( ctx ); | ||
| } | ||
| String from = getFromClause(); | ||
| String join = getJoinClause(); | ||
| - String where = getWhereClause(); | ||
| - System.out.printf( "%s%s%s%s", | ||
| - select, from, join, where ); | ||
| + String where = getWhereClause().toString(); | ||
| + | ||
| + System.out.printf( "%s%s%s%s", select, from, join, where ); | ||
| } | ||
| */ | ||
| private String getFromClause() { | ||
| - return "FROM person person"; | ||
| + return "FROM person person "; | ||
| } | ||
| * | ||
| * @return The WHERE portion of a SQL statement. | ||
| - */ | ||
| - private String getWhereClause() { | ||
| - return getWhereClauseList().toString(); | ||
| - } | ||
| - | ||
| - /** | ||
| - * Adds a new leaf, representing a JOIN clause, to the from clause list. | ||
| - * | ||
| - * @param ctx The INNER/OUTER JOIN expression to add to the list. | ||
| */ | ||
| - private void addWhereClause( ParserRuleContext ctx ) { | ||
| - getWhereClauseList().addLeaf( createWhereClauseList( ctx ) ); | ||
| + private WhereClause getWhereClause() { | ||
| + return this.whereClause; | ||
| } | ||
| private Tree<Payload> createJoinClauseList( ParserRuleContext ctx ) { | ||
| return new JoinClauseList<Payload>( new Payload( ctx ) ); | ||
| - } | ||
| - | ||
| - /** | ||
| - * Changes where the next mapped items will be attached. | ||
| - */ | ||
| - private void setWhereClauseList( Tree<Payload> whereClauseList ) { | ||
| - this.whereClauseList = whereClauseList; | ||
| - } | ||
| - | ||
| - /** | ||
| - * Lazily-initializes the from clause list. | ||
| - * | ||
| - * @return The list that will contain the query's FROM clause. | ||
| - */ | ||
| - private Tree<Payload> getWhereClauseList() { | ||
| - Tree<Payload> list = this.whereClauseList; | ||
| - | ||
| - if( list == null ) { | ||
| - setWhereClauseList( list = createWhereClauseList( null ) ); | ||
| - } | ||
| - | ||
| - return list; | ||
| - } | ||
| - | ||
| - /** | ||
| - * Creates a new WhereClauseList instance containing the given parser | ||
| - * rule context. The resulting tree should only have leaves added | ||
| - * to the root, never branching beyond a flat hierarchy (one-level deep). | ||
| - * | ||
| - * @return A new WhereClauseList instance, never null, containing a payload | ||
| - * with the parser rule context. | ||
| - */ | ||
| - private Tree<Payload> createWhereClauseList( ParserRuleContext ctx ) { | ||
| - return new WhereClauseList<Payload>( new Payload( ctx ) ); | ||
| } | ||
| } | ||
| */ | ||
| protected boolean beautify() { | ||
| - return Boolean.getBoolean( "rxm_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 getWhitespace( int spaces ) { | ||
| return beautify() ? getNewline() + getIndent( spaces ) : " "; | ||
| } | ||
| /** | ||
| * Helper method. | ||
| + * | ||
| + * @return A non-null string with the default amount of space. | ||
| */ | ||
| protected String getWhitespace() { | ||
| +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 { | ||
| + private StringBuilder buffer = new StringBuilder( 2048 ); | ||
| + | ||
| + /** | ||
| + */ | ||
| + public WhereClause() { | ||
| + } | ||
| + | ||
| + /** | ||
| + * Appends "<code>WHERE </code>" to the output. | ||
| + * | ||
| + * @param ctx The WHERE expression context (unused). | ||
| + */ | ||
| + public void enterWhere( QueryParser.WhereContext ctx ) { | ||
| + append( "WHERE " ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Appends "<code> AND </code>" to the output. | ||
| + * | ||
| + * @param ctx The AND expression context (unused). | ||
| + */ | ||
| + public void exitExprAnd( QueryParser.ExprAndContext ctx ) { | ||
| + append( " AND " ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Appends "<code> OR </code>" to the output. | ||
| + * | ||
| + * @param ctx The OR expression context (unused). | ||
| + */ | ||
| + public void exitExprOr( QueryParser.ExprOrContext ctx ) { | ||
| + 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(); | ||
| + QueryParser.ExprValueContext value = ctx.exprValue(); | ||
| + | ||
| + // If the terminal node is null, then the equality is T_INEQ. | ||
| + TerminalNode equal = ctx.getToken( QueryParser.T_EQ, 0 ); | ||
| + | ||
| + String entity = tableColumn.getText(); | ||
| + String result = ""; | ||
| + | ||
| + if( set == null ) { | ||
| + // Presume = or <> by default (could change to IS or IS NOT). | ||
| + String comparator = ctx.getChild(1).getText(); | ||
| + | ||
| + QueryParser.LiteralContext literal = value.literal(); | ||
| + | ||
| + if( literal == null ) { | ||
| + result = 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 switched to NULL and the | ||
| + // comparators likewise revised. | ||
| + if( literal.T_NULL() != null ) { | ||
| + rhs = "NULL"; | ||
| + comparator = " IS " + (equal == null ? "NOT " : ""); | ||
| + } | ||
| + | ||
| + result = String.format( "%s%s%s", entity, comparator, rhs ); | ||
| + } | ||
| + } | ||
| + else { | ||
| + result = String.format( "%s %sIN (%s)", | ||
| + entity, | ||
| + equal == null ? "NOT " : "", | ||
| + set.exprList().getText() ); | ||
| + } | ||
| + | ||
| + append( result ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * 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() ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Appends a given string to the buffer. | ||
| + * | ||
| + * @param s The string to append. | ||
| + */ | ||
| + private void append( String s ) { | ||
| + getBuffer().append( s ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Returns the buffer used for building the transformed output. | ||
| + * | ||
| + * @return A non-null string builder, possibly empty. | ||
| + */ | ||
| + private StringBuilder getBuffer() { | ||
| + return this.buffer; | ||
| + } | ||
| + | ||
| + /** | ||
| + * 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(); | ||
| + } | ||
| +} | ||
| + | ||
| -package com.whitemagicsoftware.rxm.tree.xml; | ||
| - | ||
| -import com.whitemagicsoftware.rxm.tree.Payload; | ||
| - | ||
| -import org.antlr.v4.runtime.Token; | ||
| - | ||
| -/** | ||
| - * <p> | ||
| - * Stores a list of Payload instances. This is instantiated by the parser | ||
| - * to create a transformation list capable of generating SQL/XML WHERE | ||
| - * expressions. The payload dynamically determines the set of wrapper classes | ||
| - * using the package for name of class. | ||
| - * </p> | ||
| - * <p> | ||
| - * The tree is being re-used as a list. (A list is simply a tree with | ||
| - * one branch and many siblings.) | ||
| - * </p> | ||
| - */ | ||
| -public class WhereClauseList<T extends Payload> extends JoinClauseList<T> { | ||
| - /** | ||
| - * Constructs a list capable of transforming itself into a SQL/XML | ||
| - * FROM clause. | ||
| - * | ||
| - * @param payload The data associated with this list (must not be null). | ||
| - */ | ||
| - public WhereClauseList( T payload ) { | ||
| - super( payload ); | ||
| - } | ||
| - | ||
| - public String toString() { | ||
| - String w = getWhitespace(0); | ||
| - return getBranches().size() > 0 ? | ||
| - String.format( "%sWHERE%s%s", w, getWhitespace(), super.toString() ) : | ||
| - ""; | ||
| - } | ||
| -} | ||
| - | ||
| @Override | ||
| public Token getStart() { | ||
| - QueryParser.ExpressionContext expression = expression(); | ||
| - | ||
| - return new Token( expression.getText() ); | ||
| + return new Token( "" ); | ||
| } | ||
| Author | Dave Jarvis <email> |
|---|---|
| Date | 2015-03-16 18:54:37 GMT-0700 |
| Commit | 390c452899a460e886ae3dd606e1f3b31c02d9e2 |
| Parent | 50e194b |
| Delta | 215 lines added, 104 lines removed, 111-line increase |