package com.temenos.interaction.odataext.odataparser.output; /* * OData4j uses 'visitors' to convert it's expressions into Strings. However there does not appear to be a visitor that * produces output in the format required for re-parsing. Unlike OData4j, which parses once and then works * with the QueryInfo structure, we need to repeatedly parse parameters and write them back into the parameter list. * (Looks like PreOrderVisitor is moving towards this functionality but it is not present in oData4j 0.7.0) * * This class extend OUR version of PrintExpressionVisitor. Not the, almost identical, OData4j version. This means we can * make use of most of the parents, unchanged, visit() methods. These will call back to our, changed, append() methods. * If the OData4j PrintExpressionVisitor is extended then these calls will go to it's, private, append() methods and the * desired results will not be achieved. */ /* * #%L * interaction-odata4j-ext * %% * Copyright (C) 2012 - 2013 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import org.odata4j.expression.BoolParenExpression; import org.odata4j.expression.CeilingMethodCallExpression; import org.odata4j.expression.ConcatMethodCallExpression; import org.odata4j.expression.DateTimeLiteral; import org.odata4j.expression.DateTimeOffsetLiteral; import org.odata4j.expression.DayMethodCallExpression; import org.odata4j.expression.DecimalLiteral; import org.odata4j.expression.DoubleLiteral; import org.odata4j.expression.EndsWithMethodCallExpression; import org.odata4j.expression.FloorMethodCallExpression; import org.odata4j.expression.HourMethodCallExpression; import org.odata4j.expression.IndexOfMethodCallExpression; import org.odata4j.expression.Int64Literal; import org.odata4j.expression.IsofExpression; import org.odata4j.expression.LengthMethodCallExpression; import org.odata4j.expression.MethodCallExpression; import org.odata4j.expression.MinuteMethodCallExpression; import org.odata4j.expression.MonthMethodCallExpression; import org.odata4j.expression.OrderByExpression; import org.odata4j.expression.ParenExpression; import org.odata4j.expression.ReplaceMethodCallExpression; import org.odata4j.expression.RoundMethodCallExpression; import org.odata4j.expression.SecondMethodCallExpression; import org.odata4j.expression.StartsWithMethodCallExpression; import org.odata4j.expression.StringLiteral; import org.odata4j.expression.SubstringMethodCallExpression; import org.odata4j.expression.SubstringOfMethodCallExpression; import org.odata4j.expression.TimeLiteral; import org.odata4j.expression.ToLowerMethodCallExpression; import org.odata4j.expression.ToUpperMethodCallExpression; import org.odata4j.expression.TrimMethodCallExpression; import org.odata4j.expression.YearMethodCallExpression; import org.odata4j.internal.InternalUtil; import com.temenos.interaction.odataext.odataparser.odata4j.PrintExpressionVisitor; public class OutputExpressionVisitor extends PrintExpressionVisitor { // Variables handling the ExpressionPrinter tree, private OutputExpressionNode rootNode; private OutputExpressionNode currentNode; public OutputExpressionVisitor() { reset(); } // Reset tree to it's starting state. Put current tree up for garbage collection. public void reset() { super.reset(); rootNode = new OutputExpressionNode(); currentNode = rootNode; } @Override public String toString() { // Print out the expression tree return rootNode.toOdataParameter(); } protected void append(String format, Object... args) { // For non overridden methods append the arg as a simple string. Don't // do the formatting. append(String.format("%s", args)); } protected void appendFormatted(String format, Object... args) { // For overridden methods do the formatting. append(String.format(format, args)); } protected void append(String str) { if (!currentNode.setOp(str)) { throw new RuntimeException("Internal error adding:" + str); } } @Override public void afterDescend() { // Move a level up the ExpressionPrinter tree OutputExpressionNode parentNode = currentNode.getParent(); if (null == parentNode) { // Tried to go above the top of the tree. throw new RuntimeException("Tried to go above tree root."); } // Child is complete. Print it into parent arguments. parentNode.addArgument(currentNode.toOdataParameter()); currentNode = parentNode; } @Override public void beforeDescend() { // Create a new child node and start building it. currentNode = new OutputExpressionNode(currentNode); } @Override public void betweenDescend() { OutputExpressionNode parentNode = currentNode.getParent(); if (null == parentNode) { // Tried to go above the top of the tree. throw new RuntimeException("Tried to go above tree root."); } // Child is complete. Print it into parent arguments. parentNode.addArgument(currentNode.toOdataParameter()); // Move back to parent currentNode = parentNode; // Create next child node and move to it. currentNode = new OutputExpressionNode(currentNode); } // Don't append the 'orderBy' tag. @Override public void visit(OrderByExpression expr) { } // The current term is bracketed. Make a note of the fact. @Override public void visit(BoolParenExpression expr) { currentNode.setIsBracketed(); } @Override public void visit(ParenExpression expr) { currentNode.setIsBracketed(); } // Literal strings may contain spaces or dots. So must be quoted @Override public void visit(StringLiteral expr) { append("'" + expr.getValue() + "'"); } @Override public void visit(SubstringMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } public void visit(MethodCallExpression expr) { currentNode.setIsFunction(); } @Override public void visit(SubstringOfMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(ToUpperMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(ToLowerMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(ReplaceMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(LengthMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(TrimMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(YearMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(MonthMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(DayMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(HourMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(MinuteMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(SecondMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(RoundMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(FloorMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(CeilingMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(IsofExpression expr) { currentNode.setIsFunction(); // Isof has literal arguments. currentNode.setQuoteArguments(); super.visit(expr); } @Override public void visit(EndsWithMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(StartsWithMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(IndexOfMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(ConcatMethodCallExpression expr) { currentNode.setIsFunction(); super.visit(expr); } @Override public void visit(DecimalLiteral expr) { appendFormatted("%sM", expr.getValue()); } @Override public void visit(Int64Literal expr) { appendFormatted("%sL", expr.getValue()); } @Override public void visit(DoubleLiteral expr) { appendFormatted("%sd", expr.getValue()); } @Override public void visit(DateTimeLiteral expr) { appendFormatted("datetime'%s'", InternalUtil.formatDateTimeForXml(expr.getValue())); } /* * TODO Currently not sure about the syntax for this. */ @Override public void visit(DateTimeOffsetLiteral expr) { throw new UnsupportedOperationException("DateTimeOffsetLiteral not supported."); // Maybe should be something like. // appendFormatted("%sZ", // InternalUtil.formatDateTimeOffsetForXml(expr.getValue())); } /* * TODO Currently not sure about the syntax for this. */ @Override public void visit(TimeLiteral expr) { throw new UnsupportedOperationException("TimeLiteral not supported."); // Maybe should be something like. // appendFormatted("%s", // expr.getValue().toString(ExpressionParser.TIME_FORMATTER)); } }