Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
prerna.query.interpreters.RInterpreter Maven / Gradle / Ivy
package prerna.query.interpreters;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import prerna.algorithm.api.SemossDataType;
import prerna.ds.r.RDataTable;
import prerna.ds.r.RSyntaxHelper;
import prerna.engine.api.IRawSelectWrapper;
import prerna.query.querystruct.HardSelectQueryStruct;
import prerna.query.querystruct.SelectQueryStruct;
import prerna.query.querystruct.filters.AndQueryFilter;
import prerna.query.querystruct.filters.BetweenQueryFilter;
import prerna.query.querystruct.filters.IQueryFilter;
import prerna.query.querystruct.filters.OrQueryFilter;
import prerna.query.querystruct.filters.SimpleQueryFilter;
import prerna.query.querystruct.filters.SimpleQueryFilter.FILTER_TYPE;
import prerna.query.querystruct.selectors.IQuerySelector;
import prerna.query.querystruct.selectors.IQuerySort;
import prerna.query.querystruct.selectors.QueryArithmeticSelector;
import prerna.query.querystruct.selectors.QueryColumnOrderBySelector;
import prerna.query.querystruct.selectors.QueryColumnOrderBySelector.ORDER_BY_DIRECTION;
import prerna.reactor.IReactor;
import prerna.reactor.qs.SubQueryExpression;
import prerna.query.querystruct.selectors.QueryColumnSelector;
import prerna.query.querystruct.selectors.QueryConstantSelector;
import prerna.query.querystruct.selectors.QueryCustomOrderBy;
import prerna.query.querystruct.selectors.QueryFunctionHelper;
import prerna.query.querystruct.selectors.QueryFunctionSelector;
import prerna.query.querystruct.selectors.QueryIfSelector;
import prerna.sablecc2.om.nounmeta.NounMetadata;
import prerna.sablecc2.om.task.ITask;
import prerna.util.Constants;
import prerna.util.Utility;
public class RInterpreter extends AbstractQueryInterpreter {
private static final Logger classLogger = LogManager.getLogger(RInterpreter.class);
private String dataTableName = null;
private Map colDataTypes;
// store the qs value for each header to its alias
Map qsToAliasMap = new HashMap<>();
//keep track of the selectors
private StringBuilder selectorCriteria = new StringBuilder();
// keep track of the filters
private StringBuilder filterCriteria = new StringBuilder();
private StringBuilder havingFilterCriteria = new StringBuilder();
private List havingColumns = new Vector<>();
// keep track of group bys
private StringBuilder groupBys = new StringBuilder();
// keep track of order bys
private StringBuilder orderBys = new StringBuilder();
// keep the main query so we can cache by it
private StringBuilder mainQuery = new StringBuilder();
// need to keep track of selectors
// to make sure the order by's are accurate
private List validHeaders = new Vector<>();
// keep track of date columns that are aggregated
private Map aggregatedDateVals = new HashMap<>();
// keep track of columns that have had their data type changed
private Map convertedDates = new HashMap<>();
// this is the temp var name that we create
// this is because we have to make operations on operations
private String tempVarName = null;
// this is because we need to handle subquery
private transient RDataTable rDataTable;
@Override
public String composeQuery() {
if(this.dataTableName == null) {
throw new IllegalArgumentException("Please define the table name to use for the r data table query syntax to use");
}
StringBuilder query = new StringBuilder();
this.tempVarName = "temp" + Utility.getRandomString(10);
query.append(tempVarName + " <- ");
if(this.qs instanceof HardSelectQueryStruct) {
String customQuery = ((HardSelectQueryStruct) this.qs).getQuery().trim();
if(customQuery.length() > "select".length() &&
customQuery.substring(0,"select".length()).toUpperCase().startsWith("SELECT")) {
// wrap as R
query.append("as.data.table(sqldf(\"").append(customQuery.replace("\"", "\\\"")).append("\"));");
return "library(sqldf);"+query.toString();
} else {
// must be some valid r
query.append("{").append(customQuery).append("}");
return query.toString();
}
}
if(this.colDataTypes == null) {
this.colDataTypes = new Hashtable();
}
boolean isDistinct = ((SelectQueryStruct) this.qs).isDistinct();
// note, that the join info in the QS has no meaning for a R frame as
// we cannot connect across data tables
addFilters(qs.getCombinedFilters().getFilters(), this.dataTableName, this.filterCriteria, false, false);
// add having filters
addFilters(qs.getHavingFilters().getFilters(), this.tempVarName, this.havingFilterCriteria, true, true);
// once the filters have been added, enable
StringBuilder cachedFrame = new StringBuilder(this.dataTableName);
addSelector(qs.getSelectors());
addGroupBy();
if(isDistinct) {
query.append("unique(");
mainQuery.append("unique(");
}
//query.append(this.dataTableName)
query.append(cachedFrame)
.append("[ ")
.append(this.filterCriteria.toString())
.append(", ")
.append(this.selectorCriteria.toString())
.append(this.groupBys)
.append("]");
mainQuery.append(cachedFrame)
//mainQuery.append(this.dataTableName)
.append("[ ")
.append(this.filterCriteria.toString())
.append(", ")
.append(this.selectorCriteria.toString())
.append(this.groupBys)
.append("]");
if(isDistinct) {
query.append(")");
mainQuery.append(")");
}
//append having filters
String having = this.havingFilterCriteria.toString();
if(!having.isEmpty()) {
query.append("; ")
.append(this.tempVarName)
.append("<-")
.append(this.tempVarName)
.append("[ ")
.append(having)
.append(", ] ");
}
// get the order by values
addOrderBy(this.tempVarName);
// append order by at the end
String order = this.orderBys.toString();
query.append(";");
if(!order.isEmpty()) {
query.append(order).append("; ");
}
// we need to convert dates from being integer values
// to output as dates
boolean addedColToDateChange = false;
for (String column : this.colDataTypes.keySet()) {
SemossDataType dataType = this.colDataTypes.get(column);
if(dataType == SemossDataType.DATE || dataType == SemossDataType.TIMESTAMP) {
if (column.contains("__")) {
column = column.split("__")[1];
}
if (validHeaders.contains(column)) {
addedColToDateChange = true;
// String javaFormat = this.additionalTypes.get(this.dataTableName + "__" + column);
addDateConversionFunction(query, this.tempVarName, column, dataType, null);
}
}
}
for(String column : aggregatedDateVals.keySet()) {
if (validHeaders.contains(column)) {
addedColToDateChange = true;
addDateConversionFunction(query, this.tempVarName, column, aggregatedDateVals.get(column), null);
}
}
if(addedColToDateChange) {
query.append(this.tempVarName).append(";");
}
if(this.logger.isDebugEnabled()) {
if(query.length() > 500) {
this.logger.debug("R QUERY.... " + query.substring(0, 500) + "...");
} else {
this.logger.debug("R QUERY.... " + query);
}
}
return query.toString();
}
/**
* Get the temp var name generated from the data
* @return
*/
public String getTempVarName() {
return this.tempVarName;
}
private void addDateConversionFunction(StringBuilder query, String tempVarName, String column, SemossDataType type, String javaFormatAdditionalType) {
String javaFormat = javaFormatAdditionalType;
if(javaFormat == null) {
if(type == SemossDataType.DATE) {
javaFormat = "yyyy-MM-dd";
} else if(type == SemossDataType.TIMESTAMP) {
javaFormat = "yyyy-MM-dd HH:mm:ss.SSS";
}
}
String[] rFormat = RSyntaxHelper.translateJavaRDateTimeFormat(javaFormat).split("\\|");
query.append("if (is.Date(" + tempVarName + "$" + column + ") || is.POSIXct(" + tempVarName + "$"
+ column + ")) {").append("options(digits.secs =" + rFormat[1] + ");")
.append(tempVarName + "$" + column + " <- format(" + tempVarName + "$" + column + ", format='"
+ rFormat[0] + "')");
// handle potential leading zero and second/millisecond delimiter
String rSubSyntax = getRSubSyntax(javaFormat, rFormat[0]);
if (rSubSyntax.length() > 0) {
query.append(" %>% " + rSubSyntax);
}
query.append("};");
convertedDates.put(column, type);
}
///////////////////////// helper functions to parse additional datatypes /////////////////////////
/**
* Remove leading zeroes if instructed by the java date/time format and/or
* update delimiter separating seconds and milliseconds, if applicable
* @param jFormat
* @param rFormat
* @return
*/
private String getRSubSyntax(String jFormat, String rFormat) {
StringBuilder sb = new StringBuilder();
//regex that correspond to values that may potentially not need a leading zero
String regex = "MdHm" ;
String firstParam = rFormat;
String secondParam = "";
//remove anything that is wrapped in single quotes
Pattern sqPattern = Pattern.compile("([\"'])(\\\\?.)*?\\1");
jFormat = sqPattern.matcher(jFormat).replaceAll("");
List matchedRRegex = new ArrayList();
for (char ch: regex.toCharArray()) {
Pattern pattern = Pattern.compile(String.valueOf(ch));
Matcher matcher = pattern.matcher(jFormat);
int count = 0;
while (matcher.find()){
count++;
}
if (count == 1) {
//if the character is matched once in the jFormat, then fetch corresponding r syntax
String rRegex = (String.valueOf(ch).equals("M")) ? RSyntaxHelper.getValueJavaRDatTimeTranslationMap(ch + "1") :
RSyntaxHelper.getValueJavaRDatTimeTranslationMap(String.valueOf(ch));
matchedRRegex.add(rRegex);
}
}
if (matchedRRegex.size() > 0) {
//if leading zero is absent, then construct the R gsub syntax
int firstParamCurIndex = 0;
int secondParamValue = 1;
for (int i=0; i < rFormat.length(); i++){
String c = Character.toString(rFormat.charAt(i));
if (c.equals("%")) {
String substr = (rFormat.substring(i, i+2).equals("%O")) ? rFormat.substring(i, i+3) : rFormat.substring(i, i+2);
if (matchedRRegex.contains(substr)){
firstParam = firstParam.replaceAll(substr, "0?(.+)");
firstParamCurIndex += 6;
} else {
firstParam = firstParam.replaceAll(substr, "(.*)");
firstParamCurIndex += 4;
}
secondParam += "\\\\" + secondParamValue;
secondParamValue++;
//increment i appropriately
i = (substr.equals("%OS")) ? i+2 : i+1;
} else {
firstParam = firstParam.substring(0, firstParamCurIndex) + "\\\\" + firstParam.substring(firstParamCurIndex);
firstParamCurIndex += 3;
secondParam += c;
}
}
//if minutes is one where we have to handle leading zeros, then need to ensure "00" is left as such
if (matchedRRegex.contains("%M")) {
sb.append("sub('" + firstParam + "','" + secondParam + "', .) %>% gsub(':0:', ':00:', .)");
} else {
sb.append("sub('" + firstParam + "','" + secondParam + "', .)");
}
}
//check if delimiter between seconds and milliseconds needs to be addressed (replaced if not a period)
int indexSeconds = jFormat.lastIndexOf("s");
int indexMilliSeconds = jFormat.indexOf("S");
if (indexSeconds < indexMilliSeconds) {
String delimiter = jFormat.substring(indexSeconds + 1, indexMilliSeconds);
if (!delimiter.equals(".")){
if (sb.length() > 0) {
sb.append(" %>% ");
}
sb.append("gsub('.([^.]+)$', '\\\\2" + delimiter + "\\\\1', .)");
}
}
return sb.toString();
}
//////////////////////////////////// start adding selectors /////////////////////////////////////
private void addSelector(List selectors) {
StringBuilder selectorBuilder = new StringBuilder("{ ");
StringBuilder outputNames = new StringBuilder(" ; list(");
List qsValues = new Vector<>();
//iterate through to get properties of each selector
int numSelectors = selectors.size();
for(int i = 0; i < numSelectors; i++) {
IQuerySelector selector = selectors.get(i);
qsValues.add(selector.getQueryStructName());
String alias = selector.getAlias();
if(i >= 1) {
selectorBuilder.append(" ; ");
outputNames.append(" , ");
}
String tempName = "V" + i;
selectorBuilder.append(tempName).append("=").append(processSelector(selector, this.dataTableName, false, false));
outputNames.append(alias).append("=").append(tempName);
// also keep track of headers
// so we know what order by's are valid
this.validHeaders.add(alias);
// store header to alias
qsToAliasMap.put(selector.getQueryStructName(), selector.getAlias());
}
if(!this.havingColumns.isEmpty()) {
int numHavings = this.havingColumns.size();
for(int i = 0; i < numHavings; i++) {
IQuerySelector selector = this.havingColumns.get(i);
// ignore this - already have it
if(qsValues.contains(selector.getQueryStructName())) {
continue;
}
qsValues.add(selector.getQueryStructName());
String alias = selector.getAlias();
// we must always have selectors
selectorBuilder.append(" ; ");
outputNames.append(" , ");
String tempName = "V" + (numSelectors+i);
selectorBuilder.append(tempName).append("=").append(processSelector(selector, this.dataTableName, false, false));
outputNames.append(alias).append("=").append(tempName);
// store header to alias
qsToAliasMap.put(selector.getQueryStructName(), selector.getAlias());
}
}
// append selectors + outputs to perform correct calculations + add correct alias
this.selectorCriteria.append(selectorBuilder).append(outputNames).append(") }");
}
/**
* Method is used to generate the appropriate syntax for each type of selector
* Note, this returns everything without the alias (unless we have indicated to use alias) since this is called again from
* the base methods it calls to allow for complex math expressions
* @param selector
* @param tableName
* @param includeTableName
* @param useAlias
* @return
*/
private String processSelector(IQuerySelector selector, String tableName, boolean includeTableName, boolean useAlias) {
if(useAlias) {
if(includeTableName) {
return tableName + "$" + selector.getAlias();
}
return selector.getAlias();
}
IQuerySelector.SELECTOR_TYPE selectorType = selector.getSelectorType();
if(selectorType == IQuerySelector.SELECTOR_TYPE.CONSTANT) {
return processConstantSelector((QueryConstantSelector) selector);
} else if(selectorType == IQuerySelector.SELECTOR_TYPE.COLUMN) {
return processColumnSelector((QueryColumnSelector) selector, tableName, includeTableName, useAlias);
} else if(selectorType == IQuerySelector.SELECTOR_TYPE.FUNCTION) {
return processFunctionSelector((QueryFunctionSelector) selector, tableName, includeTableName, useAlias);
} else if(selectorType == IQuerySelector.SELECTOR_TYPE.ARITHMETIC) {
return processArithmeticSelector((QueryArithmeticSelector) selector, tableName, includeTableName, useAlias);
}
else if(selectorType == IQuerySelector.SELECTOR_TYPE.IF_ELSE)
{
return processIfElseSelector((QueryIfSelector)selector, tableName, includeTableName, useAlias);
}
return null;
}
private String processIfElseSelector(QueryIfSelector selector, String tableName, boolean includeTableName, boolean useAlias)
{
// get the condition first
IQueryFilter condition = selector.getCondition();
StringBuffer buf = new StringBuffer("ifelse(");
StringBuilder filterBuilder = new StringBuilder();
filterBuilder = this.processFilter(condition, tableName, useAlias, false);
// builder shoudl have what we need at this point
buf.append(filterBuilder.toString());
buf.append(",");
// get the precedent
IQuerySelector precedent = selector.getPrecedent();
buf.append(processSelector(precedent, tableName, includeTableName, useAlias));
IQuerySelector antecedent = selector.getAntecedent();
if(antecedent != null)
{
buf.append(", ");
buf.append(processSelector(antecedent, tableName, includeTableName, useAlias));
}
buf.append(")");
return buf.toString();
}
private String processConstantSelector(QueryConstantSelector selector) {
Object constant = selector.getConstant();
if(constant instanceof SubQueryExpression) {
ITask innerTask = null;
try {
innerTask = ((SubQueryExpression) constant).generateQsTask();
innerTask.setLogger(this.logger);
if(innerTask.hasNext()) {
Object value = innerTask.next().getValues()[0];
if(value instanceof Number) {
return value.toString();
} else {
return "\"" + value + "\"";
}
}
} catch(Exception e) {
classLogger.error(Constants.STACKTRACE, e);
} finally {
if(innerTask != null) {
try {
innerTask.close();
} catch (IOException e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
}
// if this doesn't return anything...
return "NA";
} else if(constant instanceof Number) {
return constant.toString();
} else {
return "\"" + constant + "\"";
}
}
private String processColumnSelector(QueryColumnSelector selector, String tableName, boolean includeTableName, boolean useAlias) {
if(includeTableName) {
if(useAlias) {
return tableName + "$" + selector.getAlias();
}
return tableName + "$" + selector.getColumn();
}
if(useAlias) {
return selector.getAlias();
}
return selector.getColumn();
}
private String processFunctionSelector(QueryFunctionSelector selector, String tableName, boolean includeTableName, boolean useAlias) {
boolean mathFunction = false;
boolean allDateColumns = true;
SemossDataType dateType = null;
List innerSelectors = selector.getInnerSelector();
String function = selector.getFunction();
StringBuilder expression = new StringBuilder();
// we auto add some cleaning up for specific functions
StringBuilder endExpr = new StringBuilder();
if(function.equals(QueryFunctionHelper.GROUP_CONCAT)) {
expression.append(QueryFunctionHelper.convertFunctionToRSyntax(function));
expression.append("(na.omit(");
endExpr.append("), collapse = \", \")");
} else if (function.equals(QueryFunctionHelper.UNIQUE_GROUP_CONCAT)) {
expression.append(QueryFunctionHelper.convertFunctionToRSyntax(function));
expression.append("(unique((na.omit(");
endExpr.append("))), collapse = \", \")");
} else if(function.equals(QueryFunctionHelper.COUNT) || function.equals(QueryFunctionHelper.UNIQUE_COUNT) ) {
expression.append(QueryFunctionHelper.convertFunctionToRSyntax(function));
expression.append("(na.omit(");
endExpr.append("))");
} else if(function.equals(QueryFunctionHelper.MONTH_NAME)) {
expression.append("(format(as.Date(");
endExpr.append("), \"%B\"))");
} else if(function.equals(QueryFunctionHelper.DAY_NAME)) {
expression.append("(format(as.Date(");
endExpr.append("), \"%A\"))");
} else if(function.equals(QueryFunctionHelper.QUARTER)) {
expression.append("(format(quarter(");
endExpr.append(")))");
} else if(function.equals(QueryFunctionHelper.WEEK)) {
expression.append("(format(as.Date(");
endExpr.append("), \"%W\"))");
} else if(function.equals(QueryFunctionHelper.YEAR)) {
expression.append("(format(as.Date(");
endExpr.append("), \"%Y\"))");
} else {
expression.append(QueryFunctionHelper.convertFunctionToRSyntax(function));
// if we have a non-defined type of function
// we need to account for additional params
List additionalParams = selector.getAdditionalFunctionParams();
for(int i = 0; i < additionalParams.size(); i++) {
endExpr.append(",");
Object[] param = additionalParams.get(i);
String name = param[0].toString();
if(!name.equals("noname")) {
endExpr.append(name).append("=");
}
for(int j = 1; j < param.length; j++) {
if(j > 1) {
endExpr.append(",");
}
endExpr.append(param[j]);
}
}
if(QueryFunctionHelper.determineTypeOfFunction(function).equals("NUMBER")) {
mathFunction = true;
if(selector.isDistinct()) {
expression.append("(unique(na.omit(as.numeric(");
endExpr.insert(0, ")))");
endExpr.append(")");
} else {
expression.append("(na.omit(as.numeric(");
endExpr.insert(0, "))");
endExpr.append(")");
}
} else {
expression.append("(");
endExpr.append(")");
}
}
int size = innerSelectors.size();
for(int i = 0; i< size; i++) {
if(i == 0) {
expression.append(processSelector(innerSelectors.get(i), tableName, includeTableName, false));
} else {
expression.append(",").append(processSelector(innerSelectors.get(i), tableName, includeTableName, useAlias));
}
if(mathFunction && innerSelectors.get(i).getSelectorType() == IQuerySelector.SELECTOR_TYPE.COLUMN) {
String alias = innerSelectors.get(i).getAlias();
SemossDataType type = colDataTypes.get(alias);
if(type == null) {
type = colDataTypes.get(tableName + "__" + alias);
}
if(type == null || !(type == SemossDataType.DATE || type == SemossDataType.TIMESTAMP)) {
allDateColumns = false;
} else {
// it is either null to start
// and then we only care if the types range between timestamp / date
// always choose the lowest level
if(dateType == null) {
dateType = type;
} else if(dateType == SemossDataType.DATE && type == SemossDataType.TIMESTAMP) {
dateType = type;
}
}
} else {
allDateColumns = false;
}
}
if(mathFunction && allDateColumns) {
this.aggregatedDateVals.put(selector.getAlias(), dateType);
}
expression.append(endExpr);
return expression.toString();
}
private String processArithmeticSelector(QueryArithmeticSelector selector, String tableName, boolean includeTableName, boolean useAlias) {
IQuerySelector leftSelector = selector.getLeftSelector();
IQuerySelector rightSelector = selector.getRightSelector();
String mathExpr = selector.getMathExpr();
return "(" + processSelector(leftSelector, tableName, includeTableName, useAlias) + " " + mathExpr + " " + processSelector(rightSelector, tableName, includeTableName, useAlias) + ")";
}
//////////////////////////////////// end adding selectors /////////////////////////////////////
//////////////////////////////////// start adding filters /////////////////////////////////////
public void addFilters(List filters, String tableName, StringBuilder builder, boolean useAlias, boolean captureColumns) {
for(IQueryFilter filter : filters) {
StringBuilder filterSyntax = processFilter(filter, tableName, useAlias, captureColumns);
if(filterSyntax != null) {
if (builder.length() > 0) {
builder.append(" & ");
}
builder.append(filterSyntax.toString());
}
}
}
private StringBuilder processFilter(IQueryFilter filter, String tableName, boolean useAlias, boolean captureColumns) {
IQueryFilter.QUERY_FILTER_TYPE filterType = filter.getQueryFilterType();
if(filterType == IQueryFilter.QUERY_FILTER_TYPE.SIMPLE) {
return processSimpleQueryFilter((SimpleQueryFilter) filter, tableName, useAlias, captureColumns);
} else if(filterType == IQueryFilter.QUERY_FILTER_TYPE.AND) {
return processAndQueryFilter((AndQueryFilter) filter, tableName, useAlias, captureColumns);
} else if(filterType == IQueryFilter.QUERY_FILTER_TYPE.OR) {
return processOrQueryFilter((OrQueryFilter) filter, tableName, useAlias, captureColumns);
} else if(filterType == IQueryFilter.QUERY_FILTER_TYPE.BETWEEN) {
return processBetweenQueryFilter((BetweenQueryFilter) filter, tableName, useAlias);
}
return null;
}
private StringBuilder processOrQueryFilter(OrQueryFilter filter, String tableName, boolean useAlias, boolean captureColumns) {
StringBuilder filterBuilder = new StringBuilder();
List filterList = filter.getFilterList();
int numAnds = filterList.size();
for(int i = 0; i < numAnds; i++) {
if(i == 0) {
filterBuilder.append("(");
} else {
filterBuilder.append(" | ");
}
filterBuilder.append(processFilter(filterList.get(i), tableName, useAlias, captureColumns));
}
filterBuilder.append(")");
return filterBuilder;
}
private StringBuilder processAndQueryFilter(AndQueryFilter filter, String tableName, boolean useAlias, boolean captureColumns) {
StringBuilder filterBuilder = new StringBuilder();
List filterList = filter.getFilterList();
int numAnds = filterList.size();
for(int i = 0; i < numAnds; i++) {
if(i == 0) {
filterBuilder.append("(");
} else {
filterBuilder.append(" & ");
}
filterBuilder.append(processFilter(filterList.get(i), tableName, useAlias, captureColumns));
}
filterBuilder.append(")");
return filterBuilder;
}
private StringBuilder processBetweenQueryFilter(BetweenQueryFilter filter, String tableName, boolean useAlias)
{
StringBuilder retBuilder = new StringBuilder();
String columnName = processSelector(filter.getColumn(), tableName, true, useAlias);
retBuilder.append(columnName);
retBuilder.append(" >= ");
retBuilder.append(filter.getStart());
retBuilder.append(" & ");
retBuilder.append(columnName);
retBuilder.append(" <= ");
retBuilder.append(filter.getEnd());
return retBuilder;
}
private StringBuilder processSimpleQueryFilter(SimpleQueryFilter filter, String tableName, boolean useAlias, boolean captureColumns) {
NounMetadata leftComp = filter.getLComparison();
NounMetadata rightComp = filter.getRComparison();
String thisComparator = filter.getComparator();
FILTER_TYPE fType = filter.getSimpleFilterType();
// let us see if it is getting us the right result first
if(fType == FILTER_TYPE.COL_TO_COL) {
return addSelectorToSelectorFilter(leftComp, rightComp, thisComparator, tableName, useAlias, captureColumns);
} else if(fType == FILTER_TYPE.COL_TO_VALUES) {
return addSelectorToValuesFilter(leftComp, rightComp, thisComparator, tableName, useAlias, captureColumns);
} else if(fType == FILTER_TYPE.VALUES_TO_COL) {
// same logic as above, just switch the order and reverse the comparator if it is numeric
return addSelectorToValuesFilter(rightComp, leftComp, IQueryFilter.getReverseNumericalComparator(thisComparator), tableName, useAlias, captureColumns);
} else if(fType == FILTER_TYPE.COL_TO_QUERY) {
return addSelectorToQueryFilter(leftComp, rightComp, thisComparator, tableName, useAlias, captureColumns);
} else if(fType == FILTER_TYPE.QUERY_TO_COL) {
// same logic as above, just switch the order and reverse the comparator if it is numeric
return addSelectorToQueryFilter(rightComp, leftComp, IQueryFilter.getReverseNumericalComparator(thisComparator), tableName, useAlias, captureColumns);
} else if(fType == FILTER_TYPE.COL_TO_LAMBDA) {
return addSelectorToLambda(leftComp, rightComp, thisComparator, tableName, useAlias, captureColumns);
} else if(fType == FILTER_TYPE.LAMBDA_TO_COL) {
// same logic as above, just switch the order and reverse the comparator if it is numeric
return addSelectorToLambda(rightComp, leftComp, IQueryFilter.getReverseNumericalComparator(thisComparator), tableName, useAlias, captureColumns);
}
else if(fType == FILTER_TYPE.VALUE_TO_VALUE) {
// WHY WOULD YOU DO THIS!!!
}
return null;
}
/**
*
* @param leftComp
* @param rightComp
* @param thisComparator
* @return
*/
private StringBuilder addSelectorToLambda(NounMetadata leftComp, NounMetadata rightComp, String thisComparator, String tableName, boolean useAlias, boolean captureColumns) {
// need to evaluate the lambda on the right
IReactor reactor = (IReactor) rightComp.getValue();
NounMetadata nounEvaluated = reactor.execute();
Map mergeMetadata = reactor.mergeIntoQsMetadata();
if(mergeMetadata.get(IReactor.MERGE_INTO_QS_FORMAT).equals(IReactor.MERGE_INTO_QS_FORMAT_SCALAR)) {
return addSelectorToValuesFilter(leftComp, nounEvaluated, thisComparator, tableName, useAlias, captureColumns);
}
throw new IllegalArgumentException("Unknown qs format to merge");
}
/**
* Add filter for column to column
* @param leftComp
* @param rightComp
* @param thisComparator
*/
private StringBuilder addSelectorToSelectorFilter(NounMetadata leftComp, NounMetadata rightComp, String thisComparator, String tableName, boolean useAlias, boolean captureColumns) {
// get the left side
IQuerySelector leftSelector = (IQuerySelector) leftComp.getValue();
IQuerySelector rightSelector = (IQuerySelector) rightComp.getValue();
// store the columns used
if(captureColumns) {
this.havingColumns.add(leftSelector);
this.havingColumns.add(rightSelector);
}
/*
* Add the filter syntax here once we have the correct physical names
*/
String lSelector = processSelector(leftSelector, tableName, true, useAlias);
String rSelector = processSelector(rightSelector, tableName, true, useAlias);
StringBuilder filterBuilder = new StringBuilder();
if(thisComparator.equals("!=") || thisComparator.equals("<>")) {
filterBuilder.append("( !(").append(lSelector).append(" == ").append(rSelector)
// account for NA
.append(") | ( is.na(").append(lSelector).append(") & !is.na(").append(rSelector)
.append(") ) | ( !is.na(").append(lSelector).append(") & is.na(").append(rSelector).append(")) )");
} else if(thisComparator.equals(SEARCH_COMPARATOR)) {
// some operation
filterBuilder.append("as.character(").append(lSelector)
.append(") %like% as.character(").append(rSelector).append(")");
} else if(thisComparator.equals(NOT_SEARCH_COMPARATOR)) {
// some operation
filterBuilder.append(" !(as.character(").append(lSelector)
.append(") %like% as.character(").append(rSelector).append(")) ");
} else {
if(thisComparator.equals("==")) {
filterBuilder.append("(").append(lSelector).append(" == ").append(rSelector)
// account for NA
.append(" | is.na(").append(lSelector).append(") & is.na(").append(rSelector).append(") )");
} else {
// other op
filterBuilder.append(lSelector).append(" ").append(thisComparator)
.append(" ").append(rSelector);
}
}
return filterBuilder;
}
private StringBuilder addSelectorToValuesFilter(NounMetadata leftComp, NounMetadata rightComp, String thisComparator,
String tableName, boolean useAlias, boolean captureColumns) {
// get the left side
IQuerySelector leftSelector = (IQuerySelector) leftComp.getValue();
String leftSelectorExpression = processSelector(leftSelector, tableName, true, useAlias);
SemossDataType leftDataType = SemossDataType.convertStringToDataType(leftSelector.getDataType());
// store the columns used
if(captureColumns) {
this.havingColumns.add(leftSelector);
}
// if it is null, then we know we have a column
// need to grab from metadata
if(leftDataType == null) {
leftDataType = this.colDataTypes.get(leftSelector.getQueryStructName());
}
// grab the objects we are setting up for the comparison
List objects = new Vector();
// ugh... this is gross
if(rightComp.getValue() instanceof Collection) {
objects.addAll( (Collection) rightComp.getValue());
} else {
objects.add(rightComp.getValue());
}
// need to account for null inputs
boolean addNullCheck = objects.remove(null);
boolean nullCheckWithEquals = true;
if(leftDataType != null && SemossDataType.isNotString(leftDataType)) {
if(objects.remove("null")) {
addNullCheck = true;
}
if(objects.remove("nan")) {
addNullCheck = true;
}
if(thisComparator.equals("==") && objects.remove("")) {
addNullCheck = true;
}
}
if(!addNullCheck) {
// are we searching for null?
addNullCheck = IQueryInterpreter.getAllSearchComparators().contains(thisComparator) &&
(objects.contains("n") || objects.contains("nu") || objects.contains("nul") || objects.contains("null"));
}
StringBuilder filterBuilder = new StringBuilder();;
// add the null check now
if(addNullCheck) {
if(thisComparator.equals("==") || IQueryInterpreter.getPosSearchComparators().contains(thisComparator)) {
filterBuilder = new StringBuilder();
filterBuilder.append("is.na(").append(leftSelectorExpression).append(") ");
} else if(thisComparator.equals("!=") || thisComparator.equals("<>") || IQueryInterpreter.getNegSearchComparators().contains(thisComparator)) {
nullCheckWithEquals = false;
filterBuilder = new StringBuilder();
filterBuilder.append("!is.na(").append(leftSelectorExpression).append(") ");
}
}
// if there are other instances as well
// also add that
if(!objects.isEmpty()) {
boolean multi = false;
String myFilterFormatted = null;
// format the objects based on the type of the column
boolean useStringForType = IQueryInterpreter.getAllSearchComparators().contains(thisComparator);
SemossDataType formatDataType = leftDataType;
if(useStringForType) {
formatDataType = SemossDataType.STRING;
}
String additionalTimestampParameter = null;
if(formatDataType == SemossDataType.TIMESTAMP) {
additionalTimestampParameter = "tz(" + leftSelectorExpression + ")";
}
if(objects.size() > 1) {
multi = true;
myFilterFormatted = RSyntaxHelper.createRColVec(objects, formatDataType, additionalTimestampParameter);
} else {
// dont bother doing this if we have a date
// since we cannot use "in" with dates
myFilterFormatted = RSyntaxHelper.formatFilterValue(objects.get(0), formatDataType, additionalTimestampParameter);
}
// account for bad input
// example - filtering out empty + null when its a number...
if(myFilterFormatted.isEmpty()) {
return filterBuilder;
}
if(addNullCheck) {
// we added a null check above
// we need to wrap
filterBuilder.insert(0, "(");
if(nullCheckWithEquals) {
filterBuilder.append("| ");
} else {
filterBuilder.append("& ");
}
}
if(multi) {
// special processing for date types
int size = objects.size();
if(SemossDataType.DATE == leftDataType || SemossDataType.TIMESTAMP == leftDataType ) {
if(thisComparator.equals("==")) {
filterBuilder.append("(");
for (int i = 0; i < size; i++) {
filterBuilder.append(leftSelectorExpression).append(" == ").append(RSyntaxHelper.formatFilterValue(objects.get(i), leftDataType, additionalTimestampParameter));
if ((i+1) < size) {
filterBuilder.append(" | ");
}
}
filterBuilder.append(")");
} else if(thisComparator.equals("!=") | thisComparator.equals("<>")) {
filterBuilder.append("(");
for (int i = 0; i < size; i++) {
filterBuilder.append(leftSelectorExpression).append(" != ").append(RSyntaxHelper.formatFilterValue(objects.get(i), leftDataType, additionalTimestampParameter));
if ((i+1) < size) {
filterBuilder.append(" & ");
}
}
filterBuilder.append(")");
} else {
// this will probably break...
myFilterFormatted = RSyntaxHelper.formatFilterValue(objects.get(0), leftDataType, additionalTimestampParameter);
filterBuilder.append(leftSelectorExpression).append(" ").append(thisComparator).append(myFilterFormatted);
}
}
// now all the other types
else {
// use in
if(thisComparator.equals("==")) {
filterBuilder.append(leftSelectorExpression).append(" ").append(" %in% ").append(myFilterFormatted);
// use %in%
} else if(thisComparator.equals("!=") | thisComparator.equals("<>")) {
filterBuilder.append("!(").append(leftSelectorExpression).append(" ").append(" %in% ").append(myFilterFormatted).append(")");
// loop through the results - use |
} else if(thisComparator.equals(BEGINS_COMPARATOR) || thisComparator.equals(ENDS_COMPARATOR)){
String rFunction = thisComparator.equals(BEGINS_COMPARATOR) ? "startsWith" : "endsWith";
if(SemossDataType.STRING == leftDataType) {
for (int i = 0; i < size; i++) {
String expression = rFunction + "( tolower(" + leftSelectorExpression + "),"
+ RSyntaxHelper.formatFilterValue(objects.get(i), leftDataType, null) + ")";
filterBuilder.append(expression);
if ((i+1) < size) {
filterBuilder.append(" | ");
}
}
} else {
for (int i = 0; i < size; i++) {
String expression = rFunction + "( tolower(as.character(" + leftSelectorExpression + ")),"
+ RSyntaxHelper.formatFilterValue(objects.get(i), leftDataType, null) + ")";
filterBuilder.append(expression);
if ((i+1) < size) {
filterBuilder.append(" | ");
}
}
}
// loop through results - use &
} else if(thisComparator.equals(NOT_BEGINS_COMPARATOR) || thisComparator.equals(NOT_ENDS_COMPARATOR)){
String rFunction = thisComparator.equals(NOT_BEGINS_COMPARATOR) ? "startsWith" : "endsWith";
if(SemossDataType.STRING == leftDataType) {
for (int i = 0; i < size; i++) {
String expression = "!" + rFunction + "( tolower(" + leftSelectorExpression + ")," + RSyntaxHelper.formatFilterValue(objects.get(i), leftDataType, null) + ")";
filterBuilder.append(expression);
if ((i+1) < size) {
filterBuilder.append(" | ");
}
}
} else {
for (int i = 0; i < size; i++) {
String expression = "!" + rFunction + "( tolower(as.character(" + leftSelectorExpression + "))," + RSyntaxHelper.formatFilterValue(objects.get(i), leftDataType, null) + ")";
filterBuilder.append(expression);
if ((i+1) < size) {
filterBuilder.append(" | ");
}
}
}
}
// loop through results
else if(thisComparator.equals(SEARCH_COMPARATOR) || thisComparator.equals(NOT_SEARCH_COMPARATOR)){
String startFilter = thisComparator.equals(NOT_SEARCH_COMPARATOR) ? "!(" : "";
String endFilter = thisComparator.equals(NOT_SEARCH_COMPARATOR) ? ")" : "";
if(SemossDataType.STRING == leftDataType) {
for (int i = 0; i < size; i++) {
filterBuilder.append(startFilter).append("tolower(").append(leftSelectorExpression)
.append(") %like% tolower(")
.append(RSyntaxHelper.formatFilterValue(objects.get(i), leftDataType, null))
.append(")")
.append(endFilter);
if ((i+1) < size) {
filterBuilder.append(" | ");
}
}
} else {
for (int i = 0; i < size; i++) {
filterBuilder.append(startFilter).append("tolower(as.character(").append(leftSelectorExpression)
.append(")) %like% tolower(")
.append(RSyntaxHelper.formatFilterValue(objects.get(i), leftDataType, null))
.append(")")
.append(endFilter);
if ((i+1) < size) {
filterBuilder.append(" | ");
}
}
}
}
else {
// not sure why you are here... this will probably break
filterBuilder.append(leftSelectorExpression).append(" ").append(thisComparator).append(myFilterFormatted);
}
}
} else {
if(thisComparator.equals(BEGINS_COMPARATOR) || thisComparator.equals(ENDS_COMPARATOR)){
String rFunction = thisComparator.equals(BEGINS_COMPARATOR) ? "startsWith" : "endsWith";
String expression = null;
if(SemossDataType.STRING == leftDataType) {
expression = rFunction + "( tolower(" + leftSelectorExpression + ")," + myFilterFormatted.toLowerCase() + ")";
} else {
expression = rFunction + "( tolower(as.character(" + leftSelectorExpression + "))," + myFilterFormatted.toLowerCase() + ")";
}
filterBuilder.append(expression);
} else if(thisComparator.equals(NOT_BEGINS_COMPARATOR) || thisComparator.equals(NOT_ENDS_COMPARATOR)){
String rFunction = thisComparator.equals(NOT_BEGINS_COMPARATOR) ? "startsWith" : "endsWith";
String expression = null;
if(SemossDataType.STRING == leftDataType) {
expression = "!" + rFunction + "( tolower(" + leftSelectorExpression + ")," + myFilterFormatted.toLowerCase() + ")";
} else {
expression = "!" + rFunction + "( tolower(as.character(" + leftSelectorExpression + "))," + myFilterFormatted.toLowerCase() + ")";
}
filterBuilder.append(expression);
}
else if(IQueryInterpreter.getPosSearchComparators().contains(thisComparator)) {
if(myFilterFormatted.endsWith("\\\"")) {
myFilterFormatted = myFilterFormatted.substring(0, myFilterFormatted.length()-1) + "\\\"";
}
if(SemossDataType.STRING == leftDataType) {
filterBuilder.append("tolower(").append(leftSelectorExpression).append(") %like% tolower(").append(myFilterFormatted).append(")");
} else {
filterBuilder.append("tolower(as.character(").append(leftSelectorExpression).append(")) %like% tolower(").append(myFilterFormatted).append(")");
}
}
else if(IQueryInterpreter.getNegSearchComparators().contains(thisComparator)) {
if(SemossDataType.STRING == leftDataType) {
filterBuilder.append("!(tolower(").append(leftSelectorExpression).append(") %like% tolower(").append(myFilterFormatted).append("))");
} else {
filterBuilder.append("!(tolower(as.character(").append(leftSelectorExpression).append(")) %like% tolower(").append(myFilterFormatted).append("))");
}
} else {
filterBuilder.append(leftSelectorExpression).append(" ").append(thisComparator).append(" ").append(myFilterFormatted);
}
}
}
if(addNullCheck && !objects.isEmpty()) {
// close due to wrapping
filterBuilder.append(")");
}
return filterBuilder;
}
/**
* Flush the subquery to a list of values and add a normal filter
* @param leftComp
* @param rightComp
* @param thisComparator
* @param tableName
* @param useAlias
* @param captureColumns
* @return
*/
private StringBuilder addSelectorToQueryFilter(NounMetadata leftComp, NounMetadata rightComp, String thisComparator, String tableName, boolean useAlias, boolean captureColumns) {
// flush out the right side to a list of values
SelectQueryStruct subQs = (SelectQueryStruct) rightComp.getValue();
IRawSelectWrapper subQueryValues = this.rDataTable.query(subQs);
List values = new ArrayList<>();
while(subQueryValues.hasNext()) {
values.add(subQueryValues.next().getValues()[0]);
}
NounMetadata newRightComp = new NounMetadata(values, SemossDataType.convertToPixelDataType(subQueryValues.getTypes()[0]));
return addSelectorToValuesFilter(leftComp, newRightComp, thisComparator, tableName, useAlias, captureColumns);
}
//////////////////////////////////// end adding filters /////////////////////////////////////
private void addGroupBy() {
List groups = ((SelectQueryStruct) this.qs).getGroupBy();
if(groups == null || groups.isEmpty()) {
return;
}
int numGroups = groups.size();
this.groupBys.append(" , by = list(");
for(int i = 0; i < numGroups; i++) {
IQuerySelector groupBySelector = groups.get(i);
if(i >= 1) {
groupBys.append(",");
}
String selectorAlias = qsToAliasMap.get(groupBySelector.getQueryStructName());
if(selectorAlias != null) {
groupBys.append(selectorAlias).append("=")
.append(processSelector(groupBySelector, this.dataTableName, false, false));
} else {
groupBys.append(groupBySelector.getAlias()).append("=")
.append(processSelector(groupBySelector, this.dataTableName, false, false));
}
}
this.groupBys.append(")");
}
private void addOrderBy(String tempTableName) {
//grab the order by and get the corresponding display name for that order by column
List orderByList = ((SelectQueryStruct) this.qs).getCombinedOrderBy();
if (orderByList == null || orderByList.isEmpty()) {
return;
}
// custom sort
StringBuilder builderOrdering = null;
StringBuilder customSortArray = null;
boolean initialized = false;
for(IQuerySort orderBy : orderByList) {
if(orderBy.getQuerySortType() == IQuerySort.QUERY_SORT_TYPE.COLUMN) {
QueryColumnOrderBySelector orderBySelector = (QueryColumnOrderBySelector) orderBy;
String tableName = orderBySelector.getTable();
String columnName = orderBySelector.getColumn();
ORDER_BY_DIRECTION orderByDir = orderBySelector.getSortDir();
String orderByName = null;
if(columnName.equals(SelectQueryStruct.PRIM_KEY_PLACEHOLDER)) {
orderByName = tableName;
} else {
orderByName = columnName;
}
if(!this.validHeaders.contains(orderByName)) {
// not a valid order by column based on what data is being
// return, so just continue
continue;
}
if(initialized) {
builderOrdering.append(",");
} else {
builderOrdering = new StringBuilder();
initialized = true;
}
if(orderByDir == ORDER_BY_DIRECTION.DESC) {
builderOrdering.append("-");
}
builderOrdering.append(tempTableName).append("$").append(orderByName);
} else if(orderBy.getQuerySortType() == IQuerySort.QUERY_SORT_TYPE.CUSTOM) {
QueryCustomOrderBy customSort = (QueryCustomOrderBy) orderBy;
QueryColumnSelector orderBySelector = customSort.getColumnToSort();
String tableName = orderBySelector.getTable();
String columnName = orderBySelector.getColumn();
String orderByName = null;
if(columnName.equals(SelectQueryStruct.PRIM_KEY_PLACEHOLDER)) {
orderByName = tableName;
} else {
orderByName = columnName;
}
if(!this.validHeaders.contains(orderByName)) {
// not a valid order by column based on what data is being
// return, so just continue
continue;
}
if(initialized) {
builderOrdering.append(", ");
} else {
builderOrdering = new StringBuilder();
initialized = true;
}
if(customSortArray == null) {
customSortArray = new StringBuilder();
}
//TODO: should not always use STRING as the data type
String randomSortMatchVar = "cSort_" + Utility.getRandomString(6);
customSortArray.append(randomSortMatchVar).append(" <- ")
.append(RSyntaxHelper.createRColVec(customSort.getCustomOrder(), SemossDataType.STRING, null)).append(";");
builderOrdering.append("match(").append(tempTableName).append("$").append(orderByName)
.append(" , ").append(randomSortMatchVar).append(")");
}
}
if(builderOrdering != null) {
if(customSortArray != null) {
orderBys.append(customSortArray.toString());
}
orderBys.append(tempTableName).append(" <- ").append(tempTableName)
.append("[order(").append(builderOrdering.toString()).append("),]");
}
}
public void setDataTableName(String dataTableName) {
this.dataTableName = dataTableName;
}
public void setColDataTypes(Map colDataTypes) {
this.colDataTypes = colDataTypes;
}
public StringBuilder getFilterCriteria() {
return this.filterCriteria;
}
public StringBuilder getHavingFilterCriteria() {
return this.havingFilterCriteria;
}
public Map getConvertedDates() {
return this.convertedDates;
}
public void setRDataTable(RDataTable rDataTable) {
this.rDataTable = rDataTable;
}
// public static void main(String[] args) {
//// SelectQueryStruct qsTest = new SelectQueryStruct();
//// qsTest.addSelector("Title", null);
//// qsTest.addSelector("Other2", null);
//// //qsTest.addSelector("Movie_Budget", null);
////
//// //Vector filterData1 = new Vector<>();
//// //filterData1.add("American Hustle");
//// //filterData1.add("Captain Phillips");
////
//// NounMetadata test1 = new NounMetadata("Title", PixelDataType.COLUMN);
//// List values = new Vector();
//// values.add(500);
//// //values.add("string2");
//// //values.add(2.3);
//// NounMetadata test2 = new NounMetadata(values, PixelDataType.CONST_INT);
//// NounMetadata test3 = new NounMetadata("Nominated", PixelDataType.CONST_STRING);
////
//// SimpleQueryFilter filter1 = new SimpleQueryFilter(test1, "=", test3);
//// qsTest.addExplicitFilter(filter1);
////
//// //Vector filterData2 = new Vector<>();
//// //filterData2.add(40000000);
//// //qs.addFilter("Movie_Budget", ">", filterData2);
////
//// RInterpreter rI = new RInterpreter();
//// rI.setQueryStruct(qsTest);
////
//// Map colDataTypes = new Hashtable();
//// colDataTypes.put("Title", SemossDataType.STRING);
//// colDataTypes.put("Other2", SemossDataType.STRING);
////
//// rI.setColDataTypes(colDataTypes);
////
//// String query = rI.composeQuery();
//// System.out.println(query);
//
//// String jFormat = "sss:SSS";
// String jFormat = "MM-dd'T'HH:mm:ss.SSS'Z'" ;
// //String jFormat = "M-d'T'HH:mm:ss" ;
// //String jFormat = "M-d'T'HH:mm" ;
//
// String rFormat = "%m-%dT%H:%M:%OSZ";
//
// StringBuilder sb = new StringBuilder();
// //regex that correspond to values that may potentially not need a leading zero
// String regex = "MdHm" ;
// String firstParam = rFormat;
// String secondParam = "";
//
// //remove anything that is wrapped in single quotes
// Pattern sqPattern = Pattern.compile("([\"'])(\\\\?.)*?\\1");
// jFormat = sqPattern.matcher(jFormat).replaceAll("");
//
// List matchedRRegex = new ArrayList();
// for (char ch: regex.toCharArray()) {
// Pattern pattern = Pattern.compile(String.valueOf(ch));
// Matcher matcher = pattern.matcher(jFormat);
// int count = 0;
// while (matcher.find()){
// count++;
// }
// if (count == 1) {
// //if the character is matched once in the jFormat, then fetch corresponding r syntax
// String rRegex = (String.valueOf(ch).equals("M")) ? RSyntaxHelper.getValueJavaRDatTimeTranslationMap(ch + "1") :
// RSyntaxHelper.getValueJavaRDatTimeTranslationMap(String.valueOf(ch));
// matchedRRegex.add(rRegex);
// }
// }
//
// if (matchedRRegex.size() > 0) {
// //if leading zero is absent, then construct the R gsub syntax
// int secondParamValue = 1;
// for (int i=0; i < rFormat.length(); i++){
// String c = Character.toString(rFormat.charAt(i));
// if (c.equals("%")) {
// String substr = (rFormat.substring(i, i+2).equals("%O")) ? rFormat.substring(i, i+3) : rFormat.substring(i, i+2);
// System.out.println("INDEX: " + i + " ::: " + substr);
// if (matchedRRegex.contains(substr)){
// firstParam = firstParam.replaceAll(substr, "0?(.+)");
// } else {
// firstParam = firstParam.replaceAll(substr, "(.*)");
// }
// secondParam += "\\\\" + secondParamValue;
// secondParamValue++;
// //increment i appropriately
// i = (substr.equals("%OS")) ? i+2 : i+1;
// } else {
// secondParam += c;
// }
// }
//
// //if minutes is one where we have to handle leading zeros, then need to ensure "00" is left as such
// if (matchedRRegex.contains("%M")) {
// sb.append("('" + firstParam + "','" + secondParam + "', COLUMNNAME) %>% gsub(':0:', ':00:', .)");
// } else {
// sb.append("('" + firstParam + "','" + secondParam + "', COLUMNNAME)");
// }
// }
//
// //get delimiter between seconds and milliseconds, if present, from the java format
// int indexSeconds = jFormat.lastIndexOf("s");
// int indexMilliSeconds = jFormat.indexOf("S");
// if (indexSeconds < indexMilliSeconds) {
// String delimiter = jFormat.substring(indexSeconds + 1, indexMilliSeconds);
// if (!delimiter.equals(".")){
// sb.append(" %>% gsub('.([^.]+)$', '\\\\2" + delimiter + "\\\\1', .)");
// }
// }
// String x = sb.toString();
//
// System.out.println(sb.toString()==null);
// System.out.println(sb.length());
// }
public String getMainQuery() {
return this.mainQuery.toString();
}
}