/*******************************************************************************
* Copyright 2017 Capital One Services, LLC and Bitwise, Inc.
* Licensed 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 hydrograph.server.debug.lingual.querygenerator;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.TimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.antlr.v4.runtime.tree.TerminalNode;
import hydrograph.server.debug.antlr.parser.QueryParserBaseVisitor;
import hydrograph.server.debug.antlr.parser.QueryParserParser;
import hydrograph.server.debug.antlr.parser.QueryParserVisitor;
import hydrograph.server.debug.antlr.parser.QueryParserParser.JavaidenContext;
import hydrograph.server.debug.antlr.parser.QueryParserParser.SpecialexprContext;
import hydrograph.server.debug.lingual.json.GridRow;
/**
* Created by bhaveshs on 6/22/2016.
*/
public class LingualQueryCreator extends QueryParserBaseVisitor<String> implements QueryParserVisitor<String> {
String str = "";
HashMap<String, String> fieldDataMap;
private static final Logger LOG = LoggerFactory.getLogger(LingualQueryCreator.class);
public LingualQueryCreator(List<GridRow> schema) {
fieldDataMap = new HashMap<String, String>();
for (int i = 0; i < schema.size(); i++) {
fieldDataMap.put(schema.get(i).getFieldName(), schema.get(i).getDataTypeValue());
}
}
@Override
public String visitEval(QueryParserParser.EvalContext ctx) {
for (int i = 0; i < ctx.getChildCount(); i++) {
str += visit(ctx.getChild(i));
}
return str;
}
@Override
public String visitExpression(QueryParserParser.ExpressionContext ctx) {
String expr = "";
String fieldName = ctx.fieldname().get(0).getText();
String dataType = fieldDataMap.get(fieldName);
fieldName = "\"" + fieldName + "\"";
if (dataType.toLowerCase().contains("date")) {
expr = generateTimestampSyntax(ctx, expr, fieldName, dataType);
} else if (dataType.toLowerCase().contains("float")) {
expr = generateFloatSytntax(ctx, expr, fieldName, dataType);
} else if (dataType.toLowerCase().contains("double")) {
expr = generateDoubleSyntax(ctx, expr, fieldName, dataType);
} else if (dataType.toLowerCase().contains("boolean")) {
expr = generateBooleanSyntax(ctx, expr, fieldName, dataType);
} else {
expr = generateOtherDatatypeSyntax(ctx, expr, fieldName, dataType);
}
return expr;
}
private String generateOtherDatatypeSyntax(QueryParserParser.ExpressionContext ctx, String expr, String fieldName,
String dataType) {
if (ctx.condition() == null && ctx.specialexpr() != null) {
boolean notPresent = isNotClausePresentInExpression(ctx.specialexpr().getText());
if (notPresent) {
expr = generateNotClauseForField(fieldName);
}
expr += fieldName + " " + addSpace(ctx.specialexpr().getChild(0).getText())
+ getLeftBrace(ctx.specialexpr())
+ ((ctx.specialexpr().fieldname() != null && ctx.specialexpr().fieldname().size() > 0
? "\"" + ctx.specialexpr().fieldname().get(0).getText() + "\""
: generateIdentifierText(ctx.specialexpr().javaiden(), 0)))
+ generateBetweenText(ctx.specialexpr(), dataType) + getRightBrace(ctx.specialexpr())
+ (notPresent ? ")" : "");
} else {
if (ctx.javaiden() != null) {
expr = fieldName + " " + ctx.condition().getText() + " " + generateJavaIdentifierText(ctx.javaiden())
+ "";
} else {
expr = fieldName + " " + ctx.condition().getText() + " \"" + ctx.fieldname().get(1).getText() + "\"";
}
}
return expr;
}
private String generateBooleanSyntax(QueryParserParser.ExpressionContext ctx, String expr, String fieldName,
String dataType) {
if (ctx.condition() == null && ctx.specialexpr() != null) {
boolean notPresent = isNotClausePresentInExpression(ctx.specialexpr().getText());
if (notPresent) {
expr = generateNotClauseForField(fieldName);
}
expr += fieldName + " " + addSpace(ctx.specialexpr().getChild(0).getText())
+ ((ctx.specialexpr().fieldname() != null && ctx.specialexpr().fieldname().size() > 0
? "\"" + ctx.specialexpr().fieldname().get(0).getText() + "\""
: generateIdentifierText(ctx.specialexpr().javaiden(), 0)))
+ generateBetweenText(ctx.specialexpr(), dataType) + (notPresent ? ")" : "");
} else {
if (ctx.javaiden() != null) {
expr = fieldName + " " + ctx.condition().getText() + " " + ctx.javaiden().getText().replaceAll("'", "")
+ "";
} else {
expr = fieldName + " " + ctx.condition().getText() + " \"" + ctx.fieldname().get(1).getText() + "\"";
}
}
return expr;
}
private String generateDoubleSyntax(QueryParserParser.ExpressionContext ctx, String expr, String fieldName,
String dataType) {
if (ctx.condition() == null && ctx.specialexpr() != null) {
boolean notPresent = isNotClausePresentInExpression(ctx.specialexpr().getText());
if (notPresent) {
expr = generateNotClauseForField(fieldName);
}
expr += fieldName + " " + addSpace(ctx.specialexpr().getChild(0).getText())
+ getLeftBrace(ctx.specialexpr())
+ ((ctx.specialexpr().fieldname() != null && ctx.specialexpr().fieldname().size() > 0
? "\"" + ctx.specialexpr().fieldname().get(0).getText() + "\""
: " cast(" + generateIdentifierText(ctx.specialexpr().javaiden(), 0) + " as double) "))
+ generateBetweenText(ctx.specialexpr(), dataType) + getRightBrace(ctx.specialexpr())
+ (notPresent ? ")" : "");
} else {
if (ctx.javaiden() != null) {
expr = fieldName + " " + ctx.condition().getText() + " cast(" + ctx.javaiden().getText()
+ " as double)";
} else {
expr = fieldName + " " + ctx.condition().getText() + " \"" + ctx.fieldname().get(1).getText() + "\"";
}
}
return expr;
}
private String generateTimestampSyntax(QueryParserParser.ExpressionContext ctx, String expr, String fieldName,
String dataType) {
if (ctx.condition() == null && ctx.specialexpr() != null) {
boolean notPresent = isNotClausePresentInExpression(ctx.specialexpr().getText());
if (notPresent) {
expr = generateNotClauseForField(fieldName);
}
expr += fieldName + " " + addSpace(ctx.specialexpr().getChild(0).getText())
+ getLeftBrace(ctx.specialexpr())
+ (ctx.specialexpr().fieldname() != null && ctx.specialexpr().fieldname().size() > 0
? "\"" + ctx.specialexpr().fieldname().get(0).getText() + "\""
: "timestamp " + getZonewiseGeneratedTimestamp(generateIdentifierText(ctx.specialexpr().javaiden(), 0)))
+ generateBetweenText(ctx.specialexpr(), dataType.toLowerCase()) + getRightBrace(ctx.specialexpr())
+ (notPresent ? ")" : "");
} else {
if (ctx.javaiden() != null) {
expr = fieldName + " " + ctx.condition().getText() + " timestamp " + getZonewiseGeneratedTimestamp(generateIdentifierText(ctx.javaiden())) + "";
} else {
expr = fieldName + " " + ctx.condition().getText() + " \"" + ctx.fieldname().get(1).getText() + "\"";
}
}
return expr;
}
private String generateIdentifierText(JavaidenContext javaiden) {
String iden = "";
int i = 0;
for (TerminalNode identifier : javaiden.Identifier()) {
if (i == 0)
iden = iden + identifier.getText();
else
iden = iden + " " + identifier.getText();
i++;
}
return iden;
}
private static String getZonewiseGeneratedTimestamp(String timestampValue) {
TimeZone timezone = TimeZone.getDefault();
int zoneOffset = timezone.getRawOffset();
timestampValue = timestampValue.replace("'", "");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
String newTimestampString = null;
try {
date = formatter.parse(timestampValue);
} catch (ParseException parseException) {
LOG.error("Failed to parse input :" + timestampValue + " to date format 'yyyy-MM-dd HH:mm:ss'" + parseException.getMessage());
}
if (date != null) {
Long timestampInMillis = date.getTime();
long timestampasperzone = timestampInMillis - zoneOffset;
Date newtimestamp = new Date(timestampasperzone);
newTimestampString = formatter.format(newtimestamp);
}
newTimestampString = (newTimestampString==null) ? timestampValue : newTimestampString;
return "'" + newTimestampString + "'";
}
private String generateFloatSytntax(QueryParserParser.ExpressionContext ctx, String expr, String fieldName,
String dataType) {
if (ctx.condition() == null && ctx.specialexpr() != null) {
boolean notPresent = isNotClausePresentInExpression(ctx.specialexpr().getText());
if (notPresent) {
expr = generateNotClauseForField(fieldName);
}
expr += fieldName + " " + addSpace(ctx.specialexpr().getChild(0).getText())
+ getLeftBrace(ctx.specialexpr())
+ ((ctx.specialexpr().fieldname() != null && ctx.specialexpr().fieldname().size() > 0
? "\"" + ctx.specialexpr().fieldname().get(0).getText() + "\""
: " cast(" + generateIdentifierText(ctx.specialexpr().javaiden(), 0) + " as float) "))
+ generateBetweenText(ctx.specialexpr(), dataType) + getRightBrace(ctx.specialexpr())
+ (notPresent ? ")" : "");
} else {
if (ctx.javaiden() != null) {
expr = fieldName + " " + ctx.condition().getText() + " cast(" + ctx.javaiden().getText() + " as float)";
} else {
expr = fieldName + " " + ctx.condition().getText() + " \"" + ctx.fieldname().get(1).getText() + "\"";
}
}
return expr;
}
private String generateBetweenText(SpecialexprContext specialexprContext, String datType) {
String expr = "";
datType = datType.toLowerCase();
if (isBetweenPresent(specialexprContext.getText())) {
expr = getAndOr(specialexprContext) + " ";
String fieldName = "";
if (datType.contains("date")) {
expr += specialexprContext.fieldname() != null && specialexprContext.fieldname().size() > 0
? "\"" + specialexprContext.fieldname().get(1).getText() + "\""
: "timestamp " + getZonewiseGeneratedTimestamp(generateIdentifierText(specialexprContext.javaiden(), 1));
} else if (datType.contains("float")) {
expr += specialexprContext.fieldname() != null && specialexprContext.fieldname().size() > 0
? "\"" + specialexprContext.fieldname().get(1).getText() + "\""
: "cast(" + generateIdentifierText(specialexprContext.javaiden(), 1) + " as float)";
} else if (datType.contains("double")) {
expr += specialexprContext.fieldname() != null && specialexprContext.fieldname().size() > 0
? "\"" + specialexprContext.fieldname().get(1).getText() + "\""
: "cast(" + generateIdentifierText(specialexprContext.javaiden(), 1) + " as double)";
} else {
expr += specialexprContext.fieldname() != null && specialexprContext.fieldname().size() > 0
? "\"" + specialexprContext.fieldname().get(1).getText() + "\""
: generateIdentifierText(specialexprContext.javaiden(), 1);
}
}
return expr;
}
private boolean isBetweenPresent(String text) {
return text.toLowerCase().contains("between");
}
private String getLeftBrace(SpecialexprContext specialexprContext) {
String left = "";
if (specialexprContext.leftBrace() != null) {
left = left + specialexprContext.leftBrace().getText();
}
return left;
}
private String getRightBrace(SpecialexprContext specialexprContext) {
String right = "";
if (specialexprContext.rightBrace() != null) {
right = right + specialexprContext.rightBrace().getText();
}
return right;
}
private String getAndOr(SpecialexprContext specialexprContext) {
String andOr = "";
if (specialexprContext.andOr() != null) {
andOr = andOr + specialexprContext.andOr().getText();
}
return andOr;
}
private String generateIdentifierText(List<JavaidenContext> list, int index) {
String iden = "";
int i = 0;
for (TerminalNode identifier : list.get(index).Identifier()) {
if (i == 0)
iden = iden + identifier.getText();
else
iden = iden + " " + identifier.getText();
i++;
}
return iden;
}
private boolean isNotClausePresentInExpression(String expression) {
boolean notPresent = false;
if (expression.contains("not") || expression.contains("NOT")) {
notPresent = true;
}
return notPresent;
}
private String generateNotClauseForField(String fieldName) {
String expr;
expr = "(" + fieldName + " is not null and ";
return expr;
}
private String addSpace(String splExpression) {
if (splExpression.contains("LIKE")) {
splExpression = splExpression.replaceAll("LIKE", "LIKE ");
} else if (splExpression.contains("like")) {
splExpression = splExpression.replaceAll("like", "like ");
} else if (splExpression.contains("BETWEEN")) {
splExpression = splExpression.replaceAll("BETWEEN", "BETWEEN ");
} else if (splExpression.contains("between")) {
splExpression = splExpression.replaceAll("between", "between ");
}
if (splExpression.contains("and")) {
splExpression = splExpression.replaceAll("and", " and ");
} else if (splExpression.contains("AND")) {
splExpression = splExpression.replaceAll("AND", " AND ");
}
return splExpression;
}
@Override
public String visitAndOr(QueryParserParser.AndOrContext ctx) {
return " " + ctx.getText() + " ";
}
@Override
public String visitLeftBrace(QueryParserParser.LeftBraceContext ctx) {
return ctx.getText();
}
@Override
public String visitRightBrace(QueryParserParser.RightBraceContext ctx) {
return ctx.getText();
}
public String generateJavaIdentifierText(QueryParserParser.JavaidenContext ctx) {
String iden = "";
int i = 0;
for (TerminalNode identifier : ctx.Identifier()) {
if (i == 0)
iden = iden + identifier.getText();
else
iden = iden + " " + identifier.getText();
i++;
}
return iden;
}
}