package org.ovirt.engine.core.searchbackend;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.errors.SqlInjectionException;
import org.ovirt.engine.core.compat.IntegerCompat;
import org.ovirt.engine.core.compat.RefObject;
import org.ovirt.engine.core.compat.Regex;
import org.ovirt.engine.core.compat.StringFormat;
import org.ovirt.engine.core.compat.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SyntaxChecker implements ISyntaxChecker {
public static final String TAG_COLUMN_NAME_IN_CRITERIA = "tag_name";
private static final Logger log = LoggerFactory.getLogger(SyntaxChecker.class);
public static final String SORTBY = "SORTBY";
public static final String PAGE = "PAGE";
public static final String SORTDIR_ASC = "ASC";
public static final String SORTDIR_DESC = "DESC";
private final SearchObjectAutoCompleter searchObjectAC;
private final BaseAutoCompleter colonAC;
private final BaseAutoCompleter pluralAC;
private final BaseAutoCompleter sortbyAC;
private final BaseAutoCompleter pageAC;
private final BaseAutoCompleter andAC;
private final BaseAutoCompleter orAC;
private final BaseAutoCompleter dotAC;
private final BaseAutoCompleter sortDirectionAC;
private final Map<SyntaxObjectType, SyntaxObjectType[]> stateMap;
private final Regex firstDQRegexp;
private final Regex nonSpaceRegexp;
private final List<Character> disAllowedChars;
private SqlInjectionChecker sqlInjectionChecker;
public SyntaxChecker(int searchReasultsLimit) {
searchObjectAC = new SearchObjectAutoCompleter();
colonAC = new BaseAutoCompleter(":");
pluralAC = new BaseAutoCompleter("S");
sortbyAC = new BaseAutoCompleter(SORTBY);
pageAC = new BaseAutoCompleter(PAGE);
sortDirectionAC = new BaseAutoCompleter(SORTDIR_ASC, SORTDIR_DESC);
andAC = new BaseAutoCompleter("AND");
orAC = new BaseAutoCompleter("OR");
dotAC = new BaseAutoCompleter(".");
disAllowedChars = new ArrayList<>(Arrays.asList(new Character[]{'\'', ';'}));
firstDQRegexp = new Regex("^\\s*\"$");
nonSpaceRegexp = new Regex("^\\S+$");
stateMap = new HashMap<>();
stateMap.put(SyntaxObjectType.BEGIN, new SyntaxObjectType[] { SyntaxObjectType.SEARCH_OBJECT });
stateMap.put(SyntaxObjectType.SEARCH_OBJECT, new SyntaxObjectType[] { SyntaxObjectType.COLON });
SyntaxObjectType[] afterColon =
{ SyntaxObjectType.CROSS_REF_OBJ, SyntaxObjectType.CONDITION_FIELD,
SyntaxObjectType.SORTBY, SyntaxObjectType.PAGE, SyntaxObjectType.CONDITION_VALUE,
SyntaxObjectType.END };
stateMap.put(SyntaxObjectType.COLON, afterColon);
SyntaxObjectType[] afterCrossRefObj = { SyntaxObjectType.DOT, SyntaxObjectType.CONDITION_RELATION };
stateMap.put(SyntaxObjectType.CROSS_REF_OBJ, afterCrossRefObj);
stateMap.put(SyntaxObjectType.DOT, new SyntaxObjectType[] { SyntaxObjectType.CONDITION_FIELD });
stateMap.put(SyntaxObjectType.CONDITION_FIELD, new SyntaxObjectType[] { SyntaxObjectType.CONDITION_RELATION });
stateMap.put(SyntaxObjectType.CONDITION_RELATION, new SyntaxObjectType[] { SyntaxObjectType.CONDITION_VALUE });
SyntaxObjectType[] afterConditionValue = { SyntaxObjectType.OR, SyntaxObjectType.AND,
SyntaxObjectType.CROSS_REF_OBJ, SyntaxObjectType.CONDITION_FIELD, SyntaxObjectType.SORTBY,
SyntaxObjectType.PAGE, SyntaxObjectType.CONDITION_VALUE };
stateMap.put(SyntaxObjectType.CONDITION_VALUE, afterConditionValue);
SyntaxObjectType[] AndOrArray = { SyntaxObjectType.CROSS_REF_OBJ, SyntaxObjectType.CONDITION_FIELD,
SyntaxObjectType.CONDITION_VALUE };
stateMap.put(SyntaxObjectType.AND, AndOrArray);
stateMap.put(SyntaxObjectType.OR, AndOrArray);
stateMap.put(SyntaxObjectType.SORTBY, new SyntaxObjectType[] { SyntaxObjectType.SORT_FIELD });
stateMap.put(SyntaxObjectType.SORT_FIELD, new SyntaxObjectType[] { SyntaxObjectType.SORT_DIRECTION });
stateMap.put(SyntaxObjectType.SORT_DIRECTION, new SyntaxObjectType[] { SyntaxObjectType.PAGE });
stateMap.put(SyntaxObjectType.PAGE, new SyntaxObjectType[] { SyntaxObjectType.PAGE_VALUE });
stateMap.put(SyntaxObjectType.PAGE_VALUE, new SyntaxObjectType[] { SyntaxObjectType.END });
// get sql injection checker for active database engine.
try {
sqlInjectionChecker = getSqlInjectionChecker();
} catch (Exception e) {
log.debug("Failed to load Sql Injection Checker: {}", e.getMessage());
}
}
private enum ValueParseResult {
Err,
Normal,
FreeText;
}
private ValueParseResult handleValuePhrase(boolean final2, String searchText, int idx, RefObject<Integer> startPos,
SyntaxContainer container) {
boolean addObjFlag = false;
ValueParseResult retval = ValueParseResult.Normal;
IConditionFieldAutoCompleter curConditionFieldAC;
char curChar = searchText.charAt(idx);
String strRealObj = searchText.substring(startPos.argvalue, idx + 1);
boolean betweenDoubleQuotes = searchText.substring(startPos.argvalue, idx).contains("\"");
if (curChar == '"') {
betweenDoubleQuotes = !betweenDoubleQuotes;
if (betweenDoubleQuotes) {
if (!firstDQRegexp.isMatch(strRealObj)) {
container.setErr(SyntaxError.INVALID_CONDITION_VALUE, startPos.argvalue, idx + 1);
return ValueParseResult.Err;
}
} else {
strRealObj = StringHelper.trim(strRealObj, new char[] { '\"' });
addObjFlag = true;
}
}
// Doing this condition to identify whether this is the last
// searchObject and no space is predicted !!
if (final2) {
if (((curChar == ' ') || (idx + 1 == searchText.length())) && !betweenDoubleQuotes && !addObjFlag) {
strRealObj = strRealObj.trim();
if (nonSpaceRegexp.isMatch(strRealObj)) {
addObjFlag = true;
} else {
startPos.argvalue = idx + 1;
}
}
} else {
if ((curChar == ' ') && !betweenDoubleQuotes && !addObjFlag) {
strRealObj = strRealObj.trim();
if (nonSpaceRegexp.isMatch(strRealObj)) {
addObjFlag = true;
} else {
startPos.argvalue = idx + 1;
}
}
}
if (addObjFlag) {
String curRefObj = container.getPreviousSyntaxObject(3, SyntaxObjectType.CROSS_REF_OBJ);
String curConditionField = container.getPreviousSyntaxObject(1, SyntaxObjectType.CONDITION_FIELD);
curConditionFieldAC = searchObjectAC.getFieldAutoCompleter(curRefObj);
if (curConditionFieldAC == null) {
container.setErr(SyntaxError.CANT_GET_CONDITION_FIELD_AC, startPos.argvalue, idx);
return ValueParseResult.Err;
}
if (!"".equals(curConditionField)
&& !curConditionFieldAC.validateFieldValue(curConditionField, strRealObj)) {
container.setErr(SyntaxError.INVALID_CONDITION_VALUE, startPos.argvalue, idx);
return ValueParseResult.Err;
}
container.addSyntaxObject(SyntaxObjectType.CONDITION_VALUE, strRealObj, startPos.argvalue, idx + 1);
retval = ValueParseResult.FreeText;
startPos.argvalue = idx + 1;
container.setvalid(true);
}
return retval;
}
/**
* gets the sql injection checker class for current db vendor.
*/
private SqlInjectionChecker getSqlInjectionChecker() throws Exception {
// This can not be done with reflection like:
// return (SqlInjectionChecker) Class.forName(props.getProperty(SQL_INJECTION)).newInstance();
// GWT lacks support of reflection.
if (((String) Config.getValue(ConfigValues.DBEngine)).equalsIgnoreCase("postgres")) {
return new PostgresSqlInjectionChecker();
}
else {
throw new IllegalStateException("Failed to get correct sql injection checker instance name :"
+ SqlInjectionChecker.class);
}
}
@Override
public SyntaxContainer analyzeSyntaxState(final String searchText, boolean final2) {
final SyntaxContainer syntaxContainer = new SyntaxContainer(searchText);
IConditionFieldAutoCompleter curConditionFieldAC = null;
IAutoCompleter curConditionRelationAC = null;
final List<String> freeTextObjSearched = new ArrayList<>();
char[] searchCharArr = searchText.toCharArray();
int curStartPos = 0;
String tryNextObj = "";
boolean keepValid;
for (int idx = 0; idx < searchCharArr.length; idx++) {
final SyntaxObjectType curState = syntaxContainer.getState();
final char curChar = searchCharArr[idx];
if (disAllowedChars.contains(curChar)) {
syntaxContainer.setErr(SyntaxError.INVALID_CHARECTER, curStartPos, idx + 1);
return syntaxContainer;
}
if ((curChar == ' ') && (curState != SyntaxObjectType.CONDITION_RELATION)
&& (curState != SyntaxObjectType.COLON) && (curState != SyntaxObjectType.CONDITION_VALUE)
&& (curState != SyntaxObjectType.OR) && (curState != SyntaxObjectType.AND)) {
curStartPos += 1;
continue;
}
String strRealObj = searchText.substring(curStartPos, idx + 1);
String nextObject = strRealObj.toUpperCase();
switch (curState) {
case BEGIN:
// we have found a search-object
if (!searchObjectAC.validate(nextObject)) {
if (!searchObjectAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_SEARCH_OBJECT, curStartPos, idx - curStartPos + 1);
return syntaxContainer;
}
} else {
if (searchCharArr.length >= idx + 2) { // Check that this
// maybe a plural
// Validate that the next character is an 's'
if (pluralAC.validate(searchText.substring(idx + 1, idx + 1 + 1))) {
// Then just move things along.
idx++;
StringBuilder sb = new StringBuilder(nextObject);
sb.append('S');
nextObject = sb.toString();
}
}
syntaxContainer.addSyntaxObject(SyntaxObjectType.SEARCH_OBJECT, nextObject, curStartPos, idx + 1);
syntaxContainer.setvalid(true);
curStartPos = idx + 1;
}
break;
case SEARCH_OBJECT:
if (!colonAC.validate(nextObject)) {
if (!colonAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.COLON_NOT_NEXT_TO_SEARCH_OBJECT, curStartPos, idx + 1);
return syntaxContainer;
}
} else {
syntaxContainer.addSyntaxObject(SyntaxObjectType.COLON, nextObject, idx, idx + 1);
curStartPos = idx + 1;
syntaxContainer.setvalid(true);
}
break;
case CROSS_REF_OBJ:
String curRefObj = syntaxContainer.getPreviousSyntaxObject(0, SyntaxObjectType.CROSS_REF_OBJ);
curConditionRelationAC = searchObjectAC.getObjectRelationshipAutoCompleter();
if (idx + 1 < searchCharArr.length) {
tryNextObj = searchText.substring(curStartPos, idx + 2).toUpperCase();
}
if (curConditionRelationAC == null) {
syntaxContainer.setErr(SyntaxError.CONDITION_CANT_CREATE_RRELATIONS_AC, curStartPos, idx + 1);
return syntaxContainer;
}
if (dotAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.DOT, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else if (!"".equals(tryNextObj) && curConditionRelationAC.validate(tryNextObj)) {
break; // i.e. the relation object has another charecter
} else if (curConditionRelationAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.CONDITION_RELATION,
nextObject,
curStartPos,
idx + 1);
curStartPos = idx + 1;
} else if (!curConditionRelationAC.validateCompletion(nextObject)
&& !dotAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_POST_CROSS_REF_OBJ, curStartPos, idx + 1);
return syntaxContainer;
}
tryNextObj = "";
break;
case DOT:
curRefObj = syntaxContainer.getPreviousSyntaxObject(1, SyntaxObjectType.CROSS_REF_OBJ);
curConditionFieldAC = searchObjectAC.getFieldAutoCompleter(curRefObj);
if (curConditionFieldAC == null) {
syntaxContainer.setErr(SyntaxError.CANT_GET_CONDITION_FIELD_AC, curStartPos, idx);
return syntaxContainer;
}
if (!curConditionFieldAC.validate(nextObject)) {
if (!curConditionFieldAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_CONDITION_FILED, curStartPos, idx + 1);
return syntaxContainer;
}
} else {
syntaxContainer.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
}
break;
case AND:
case OR:
keepValid = false;
curConditionFieldAC = searchObjectAC.getFieldAutoCompleter(syntaxContainer.getSearchObjectStr());
if (curConditionFieldAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else if (searchObjectAC.isCrossReference(nextObject, syntaxContainer.getFirst().getBody())) {
if (searchCharArr.length >= idx + 2) { // Check that this
// maybe a plural
// Validate that the next character is an 's'
if (pluralAC.validate(searchText.substring(idx + 1, idx + 1 + 1))) {
// Then just move things along.
idx++;
StringBuilder sb = new StringBuilder(nextObject);
sb.append('S');
nextObject = sb.toString();
}
}
syntaxContainer.addSyntaxObject(SyntaxObjectType.CROSS_REF_OBJ, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else {
RefObject<Integer> tempRefObject = new RefObject<>(curStartPos);
ValueParseResult ans = handleValuePhrase(final2, searchText, idx, tempRefObject, syntaxContainer);
curStartPos = tempRefObject.argvalue;
if (ans != ValueParseResult.Err) {
if (ans == ValueParseResult.FreeText) {
curRefObj = syntaxContainer.getSearchObjectStr();
if (freeTextObjSearched.contains(curRefObj)) {
syntaxContainer.setErr(SyntaxError.FREE_TEXT_ALLOWED_ONCE_PER_OBJ, curStartPos, idx + 1);
return syntaxContainer;
}
freeTextObjSearched.add(curRefObj);
syntaxContainer.setvalid(true);
keepValid = true;
}
} else if (!curConditionFieldAC.validateCompletion(nextObject)
&& !searchObjectAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_POST_OR_AND_PHRASE, curStartPos, idx + 1);
return syntaxContainer;
}
}
if (!keepValid) {
syntaxContainer.setvalid(false);
}
break;
case COLON:
keepValid = false;
curConditionFieldAC = searchObjectAC.getFieldAutoCompleter(syntaxContainer.getSearchObjectStr());
if (curConditionFieldAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else if (sortbyAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.SORTBY, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else if (pageAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.PAGE, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else if (searchObjectAC.isCrossReference(nextObject, syntaxContainer.getFirst().getBody())) {
if (searchCharArr.length >= idx + 2) { // Check that this
// maybe a plural
// Validate that the next character is an 's'
if (pluralAC.validate(searchText.substring(idx + 1, idx + 1 + 1))) {
// Then just move things along.
idx++;
StringBuilder sb = new StringBuilder(nextObject);
sb.append('S');
nextObject = sb.toString();
}
}
syntaxContainer.addSyntaxObject(SyntaxObjectType.CROSS_REF_OBJ, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else {
RefObject<Integer> tempRefObject2 = new RefObject<>(curStartPos);
ValueParseResult ans = handleValuePhrase(final2, searchText, idx, tempRefObject2, syntaxContainer);
curStartPos = tempRefObject2.argvalue;
if (ans != ValueParseResult.Err) {
if (ans == ValueParseResult.FreeText) {
freeTextObjSearched.add(syntaxContainer.getSearchObjectStr());
}
keepValid = true;
} else if (!curConditionFieldAC.validateCompletion(nextObject)
&& !sortbyAC.validateCompletion(nextObject)
&& !searchObjectAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_POST_COLON_PHRASE, curStartPos, idx + 1);
return syntaxContainer;
}
}
if (!keepValid) {
syntaxContainer.setvalid(false);
}
break;
case CONDITION_VALUE:
nextObject = nextObject.trim();
if (nextObject.length() > 0) {
keepValid = false;
curRefObj = syntaxContainer.getSearchObjectStr();
curConditionFieldAC = searchObjectAC.getFieldAutoCompleter(curRefObj);
if (curConditionFieldAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD,
nextObject,
curStartPos,
idx + 1);
curStartPos = idx + 1;
} else if (sortbyAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.SORTBY, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else if (pageAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.PAGE, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else if (searchObjectAC.isCrossReference(nextObject, syntaxContainer.getFirst().getBody())) {
if (searchCharArr.length >= idx + 2) { // Check that this
// maybe a
// plural
// Validate that the next character is an 's'
if (pluralAC.validate(searchText.substring(idx + 1, idx + 1 + 1))) {
// Then just move things along.
idx++;
StringBuilder sb = new StringBuilder(nextObject);
sb.append('S');
nextObject = sb.toString();
}
}
syntaxContainer.addSyntaxObject(SyntaxObjectType.CROSS_REF_OBJ,
nextObject,
curStartPos,
idx + 1);
curStartPos = idx + 1;
} else if (andAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.AND, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
} else if (orAC.validate(nextObject)) {
syntaxContainer.addSyntaxObject(SyntaxObjectType.OR, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
}
else if (!curConditionFieldAC.validateCompletion(nextObject)
&& !sortbyAC.validateCompletion(nextObject)
&& !searchObjectAC.validateCompletion(nextObject)
&& !andAC.validateCompletion(nextObject) && !orAC.validateCompletion(nextObject)) {
RefObject<Integer> tempRefObject3 = new RefObject<>(curStartPos);
ValueParseResult ans =
handleValuePhrase(final2, searchText, idx, tempRefObject3, syntaxContainer);
curStartPos = tempRefObject3.argvalue;
if (ans != ValueParseResult.Err) {
if (ans == ValueParseResult.FreeText) {
if (freeTextObjSearched.contains(curRefObj)) {
syntaxContainer.setErr(SyntaxError.FREE_TEXT_ALLOWED_ONCE_PER_OBJ,
curStartPos,
idx + 1);
return syntaxContainer;
}
freeTextObjSearched.add(curRefObj);
syntaxContainer.setvalid(true);
keepValid = true;
}
} else {
syntaxContainer.setErr(SyntaxError.INVALID_POST_CONDITION_VALUE_PHRASE,
curStartPos,
idx + 1);
return syntaxContainer;
}
}
if (!keepValid) {
syntaxContainer.setvalid(false);
}
}
break;
case CONDITION_FIELD:
curRefObj = syntaxContainer.getPreviousSyntaxObject(2, SyntaxObjectType.CROSS_REF_OBJ);
String curConditionField = syntaxContainer.getPreviousSyntaxObject(0, SyntaxObjectType.CONDITION_FIELD);
curConditionRelationAC = searchObjectAC
.getFieldRelationshipAutoCompleter(curRefObj, curConditionField);
if (curConditionRelationAC == null) {
syntaxContainer.setErr(SyntaxError.CONDITION_CANT_CREATE_RRELATIONS_AC, curStartPos, idx + 1);
return syntaxContainer;
}
if (idx + 1 < searchCharArr.length) {
tryNextObj = searchText.substring(curStartPos, idx + 2).toUpperCase();
if (curConditionRelationAC.validate(tryNextObj)) {
break;
}
}
if (!curConditionRelationAC.validate(nextObject)) {
if (!curConditionRelationAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_CONDITION_RELATION, curStartPos, idx + 1);
return syntaxContainer;
}
} else {
syntaxContainer.addSyntaxObject(SyntaxObjectType.CONDITION_RELATION,
nextObject,
curStartPos,
idx + 1);
}
curStartPos = idx + 1;
syntaxContainer.setvalid(false);
tryNextObj = "";
break;
case CONDITION_RELATION:
RefObject<Integer> tempRefObject4 = new RefObject<>(curStartPos);
ValueParseResult ans = handleValuePhrase(final2, searchText, idx, tempRefObject4, syntaxContainer);
curStartPos = tempRefObject4.argvalue;
if (ans == ValueParseResult.Err) {
return syntaxContainer;
}
if (ans == ValueParseResult.FreeText) {
if (syntaxContainer.getPreviousSyntaxObjectType(2) == SyntaxObjectType.CROSS_REF_OBJ) {
curRefObj = syntaxContainer.getObjSingularName(syntaxContainer.getPreviousSyntaxObject(2,
SyntaxObjectType.CROSS_REF_OBJ));
if (freeTextObjSearched.contains(curRefObj)) {
syntaxContainer.setErr(SyntaxError.FREE_TEXT_ALLOWED_ONCE_PER_OBJ, curStartPos, idx + 1);
return syntaxContainer;
}
freeTextObjSearched.add(curRefObj);
}
}
break;
case SORTBY:
curConditionFieldAC = searchObjectAC.getFieldAutoCompleter(syntaxContainer.getSearchObjectStr());
if (!curConditionFieldAC.validate(nextObject)) {
if (!curConditionFieldAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_SORT_FIELD, curStartPos, idx + 1);
return syntaxContainer;
}
} else {
syntaxContainer.addSyntaxObject(SyntaxObjectType.SORT_FIELD, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
syntaxContainer.setvalid(true);
}
break;
case PAGE:
Integer pageNumber = IntegerCompat.tryParse(nextObject);
if (pageNumber == null) {
syntaxContainer.setErr(SyntaxError.INVALID_CHARECTER, curStartPos, idx + 1);
return syntaxContainer;
} else {
final StringBuilder buff = new StringBuilder();
int pos = idx;
// parsing the whole page number (can be more than one char)
while (pos < searchText.length() - 1 && Character.isDigit(nextObject.charAt(0))) {
buff.append(nextObject);
pos++;
strRealObj = searchText.substring(pos, pos + 1);
nextObject = strRealObj.toUpperCase();
}
buff.append(nextObject);
syntaxContainer.addSyntaxObject(SyntaxObjectType.PAGE_VALUE, buff.toString(), curStartPos, idx
+ buff.length());
// update index position
idx = pos + 1;
syntaxContainer.setvalid(true);
}
break;
case SORT_FIELD:
if (!sortDirectionAC.validate(nextObject)) {
if (!sortDirectionAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_SORT_DIRECTION, curStartPos, idx + 1);
return syntaxContainer;
}
} else {
syntaxContainer.addSyntaxObject(SyntaxObjectType.SORT_DIRECTION, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
syntaxContainer.setvalid(true);
}
break;
case PAGE_VALUE:
if (curChar != ' ') {
syntaxContainer.setErr(SyntaxError.NOTHING_COMES_AFTER_PAGE_VALUE, curStartPos, idx + 1);
return syntaxContainer;
}
break;
case SORT_DIRECTION:
if (!pageAC.validate(nextObject)) {
if (!pageAC.validateCompletion(nextObject)) {
syntaxContainer.setErr(SyntaxError.INVALID_PAGE_FEILD, curStartPos, idx);
return syntaxContainer;
}
} else {
syntaxContainer.addSyntaxObject(SyntaxObjectType.PAGE, nextObject, curStartPos, idx + 1);
curStartPos = idx + 1;
syntaxContainer.setvalid(true);
}
break;
default:
syntaxContainer.setErr(SyntaxError.UNIDENTIFIED_STATE, curStartPos, idx);
return syntaxContainer;
}
}
return syntaxContainer;
}
@Override
public SyntaxContainer getCompletion(String searchText) {
SyntaxContainer retval = analyzeSyntaxState(searchText, false);
if (retval.getError() == SyntaxError.NO_ERROR) {
IConditionFieldAutoCompleter conditionFieldAC;
IAutoCompleter conditionRelationAC;
IConditionValueAutoCompleter conditionValueAC;
int lastIdx = retval.getLastHandledIndex();
String curPartialWord = "";
if (lastIdx < searchText.length()) {
curPartialWord = searchText.substring(lastIdx, searchText.length());
curPartialWord = curPartialWord.trim();
}
SyntaxObjectType curState = retval.getState();
for (int idx = 0; idx < stateMap.get(curState).length; idx++) {
switch (stateMap.get(curState)[idx]) {
case SEARCH_OBJECT:
retval.addToACList(searchObjectAC.getCompletion(curPartialWord));
break;
case CROSS_REF_OBJ:
IAutoCompleter crossRefAC = searchObjectAC.getCrossRefAutoCompleter(retval.getFirst().getBody());
if (crossRefAC != null) {
retval.addToACList(crossRefAC.getCompletion(curPartialWord));
}
break;
case DOT:
retval.addToACList(dotAC.getCompletion(curPartialWord));
break;
case COLON:
retval.addToACList(colonAC.getCompletion(curPartialWord));
break;
case AND:
retval.addToACList(andAC.getCompletion(curPartialWord));
break;
case OR:
retval.addToACList(orAC.getCompletion(curPartialWord));
break;
case CONDITION_FIELD:
String relObj = retval.getPreviousSyntaxObject(1, SyntaxObjectType.CROSS_REF_OBJ);
conditionFieldAC = searchObjectAC.getFieldAutoCompleter(relObj);
if (conditionFieldAC != null) {
retval.addToACList(conditionFieldAC.getCompletion(curPartialWord));
}
break;
case CONDITION_RELATION:
if (curState == SyntaxObjectType.CONDITION_FIELD) {
relObj = retval.getPreviousSyntaxObject(2, SyntaxObjectType.CROSS_REF_OBJ);
String fldName = retval.getPreviousSyntaxObject(0, SyntaxObjectType.CONDITION_FIELD);
conditionRelationAC = searchObjectAC.getFieldRelationshipAutoCompleter(relObj, fldName);
} else { // curState == SyntaxObjectType.CROSS_REF_OBJ
relObj = retval.getPreviousSyntaxObject(0, SyntaxObjectType.CROSS_REF_OBJ);
conditionRelationAC = searchObjectAC.getObjectRelationshipAutoCompleter();
}
if (conditionRelationAC != null) {
retval.addToACList(conditionRelationAC.getCompletion(curPartialWord));
}
break;
case CONDITION_VALUE:
relObj = retval.getPreviousSyntaxObject(3, SyntaxObjectType.CROSS_REF_OBJ);
String fldName = retval.getPreviousSyntaxObject(1, SyntaxObjectType.CONDITION_FIELD);
conditionValueAC = searchObjectAC.getFieldValueAutoCompleter(relObj, fldName);
if (conditionValueAC != null) {
retval.addToACList(conditionValueAC.getCompletion(curPartialWord));
}
break;
case SORTBY:
retval.addToACList(sortbyAC.getCompletion(curPartialWord));
break;
case PAGE:
retval.addToACList(pageAC.getCompletion(curPartialWord));
break;
case SORT_FIELD:
conditionFieldAC = searchObjectAC.getFieldAutoCompleter(retval.getSearchObjectStr());
if (conditionFieldAC != null) {
retval.addToACList(conditionFieldAC.getCompletion(curPartialWord));
}
break;
case SORT_DIRECTION:
retval.addToACList(sortDirectionAC.getCompletion(curPartialWord));
break;
}
}
}
return retval;
}
@Override
public String generateQueryFromSyntaxContainer(SyntaxContainer syntax, boolean isSafe) {
String retval = "";
if (syntax.getvalid()) {
retval = generateSqlFromSyntaxContainer(syntax, isSafe);
}
return retval;
}
private String generateFromStatement(SyntaxContainer syntax, boolean useTags) {
LinkedList<String> innerJoins = new LinkedList<>();
ArrayList<String> refObjList = syntax.getCrossRefObjList();
String searchObjStr = syntax.getSearchObjectStr();
if (refObjList.size() > 0) {
if (SearchObjects.TEMPLATE_OBJ_NAME.equals(searchObjStr)) {
innerJoins.addFirst(searchObjectAC.getInnerJoin(SearchObjects.TEMPLATE_OBJ_NAME,
SearchObjects.VM_OBJ_NAME, useTags));
if (refObjList.contains(SearchObjects.VM_OBJ_NAME)) {
refObjList.remove(SearchObjects.VM_OBJ_NAME);
}
if (refObjList.contains(SearchObjects.VDC_USER_OBJ_NAME)) {
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME,
SearchObjects.VDC_USER_OBJ_NAME, true));
refObjList.remove(SearchObjects.VDC_USER_OBJ_NAME);
}
if (refObjList.contains(SearchObjects.VDS_OBJ_NAME)) {
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME,
SearchObjects.VDS_OBJ_NAME, true));
refObjList.remove(SearchObjects.VDS_OBJ_NAME);
}
if (refObjList.contains(SearchObjects.AUDIT_OBJ_NAME)) {
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME,
SearchObjects.AUDIT_OBJ_NAME, useTags));
refObjList.remove(SearchObjects.AUDIT_OBJ_NAME);
}
}
else if (SearchObjects.VDS_OBJ_NAME.equals(searchObjStr)) {
if (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME)) {
innerJoins.addFirst(searchObjectAC.getInnerJoin(SearchObjects.VDS_OBJ_NAME,
SearchObjects.VM_OBJ_NAME, useTags));
if (refObjList.contains(SearchObjects.VM_OBJ_NAME)) {
refObjList.remove(SearchObjects.VM_OBJ_NAME);
}
}
if (refObjList.contains(SearchObjects.VDC_USER_OBJ_NAME)) {
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VDS_OBJ_NAME,
SearchObjects.VDC_USER_OBJ_NAME, useTags));
refObjList.remove(SearchObjects.VDC_USER_OBJ_NAME);
}
if (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME)) {
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME,
SearchObjects.TEMPLATE_OBJ_NAME, useTags));
refObjList.remove(SearchObjects.TEMPLATE_OBJ_NAME);
}
}
else if (SearchObjects.VDC_USER_OBJ_NAME.equals(searchObjStr)) {
if (refObjList.contains(SearchObjects.VDS_OBJ_NAME)) {
innerJoins.addFirst(searchObjectAC.getInnerJoin(SearchObjects.VDC_USER_OBJ_NAME,
SearchObjects.VM_OBJ_NAME, useTags));
if (refObjList.contains(SearchObjects.VM_OBJ_NAME)) {
refObjList.remove(SearchObjects.VM_OBJ_NAME);
}
}
if (refObjList.contains(SearchObjects.VDS_OBJ_NAME)) {
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME,
SearchObjects.VDS_OBJ_NAME, useTags));
refObjList.remove(SearchObjects.VDS_OBJ_NAME);
}
if (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME)) {
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VDC_USER_OBJ_NAME,
SearchObjects.TEMPLATE_OBJ_NAME, useTags));
refObjList.remove(SearchObjects.TEMPLATE_OBJ_NAME);
}
}
else if (SearchObjects.AUDIT_OBJ_NAME.equals(searchObjStr)) {
if (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME)) {
innerJoins.addFirst(searchObjectAC.getInnerJoin(SearchObjects.AUDIT_OBJ_NAME,
SearchObjects.VM_OBJ_NAME, useTags));
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME,
SearchObjects.TEMPLATE_OBJ_NAME, useTags));
refObjList.remove(SearchObjects.TEMPLATE_OBJ_NAME);
if (refObjList.contains(SearchObjects.VM_OBJ_NAME)) {
refObjList.remove(SearchObjects.VM_OBJ_NAME);
}
}
}
else if (SearchObjects.DISK_OBJ_NAME.equals(searchObjStr)) {
if (refObjList.contains(SearchObjects.VDC_STORAGE_DOMAIN_OBJ_NAME)) {
innerJoins.addFirst(searchObjectAC.getInnerJoin(SearchObjects.DISK_OBJ_NAME,
SearchObjects.VDC_STORAGE_DOMAIN_IMAGE_OBJ_NAME, useTags));
innerJoins.addLast(searchObjectAC.getInnerJoin(SearchObjects.VDC_STORAGE_DOMAIN_IMAGE_OBJ_NAME,
SearchObjects.VDC_STORAGE_DOMAIN_OBJ_NAME, useTags));
refObjList.remove(SearchObjects.VDC_STORAGE_DOMAIN_OBJ_NAME);
}
}
}
for (String cro : refObjList) {
innerJoins.addLast(searchObjectAC.getInnerJoin(searchObjStr, cro, useTags));
}
innerJoins.addFirst(searchObjectAC.getRelatedTableName(searchObjStr, useTags));
StringBuilder sb = new StringBuilder();
for (String part : innerJoins) {
sb.append(" ");
sb.append(part);
sb.append(" ");
}
return sb.toString();
}
private String generateSqlFromSyntaxContainer(SyntaxContainer syntax, boolean isSafe) {
String retval = "";
if (syntax.getvalid()) {
ListIterator<SyntaxObject> objIter = syntax.listIterator(0);
IConditionFieldAutoCompleter conditionFieldAC;
LinkedList<String> whereBuilder = new LinkedList<>();
String searchObjStr = syntax.getSearchObjectStr();
String sortByPhrase = "";
String fromStatement = "";
String pageNumber = "";
boolean useTags = syntax.isSearchUsingTags();
List<SortByElement> sortByElements = null;
boolean sortAscending = true;
while (objIter.hasNext()) {
SyntaxObject obj = objIter.next();
switch (obj.getType()) {
case SEARCH_OBJECT:
fromStatement = generateFromStatement(syntax, useTags);
break;
case OR:
case AND:
whereBuilder.addLast(obj.getBody());
break;
case CONDITION_VALUE:
ConditionData conditionData =
generateConditionStatment(obj,
syntax.listIterator(objIter.previousIndex()),
searchObjStr,
syntax.getCaseSensitive(),
isSafe,
useTags);
whereBuilder.addLast(conditionData.getConditionText());
if (conditionData.isFullTableRequired() && !useTags) {
useTags = true;
fromStatement = generateFromStatement(syntax, useTags);
}
break;
case SORTBY:
break;
case PAGE_VALUE:
pageNumber = obj.getBody();
break;
case SORT_FIELD:
conditionFieldAC = searchObjectAC.getFieldAutoCompleter(searchObjStr);
sortByElements = conditionFieldAC.getSortByElements(obj.getBody());
break;
case SORT_DIRECTION:
sortAscending = !obj.getBody().equalsIgnoreCase("desc");
break;
default:
break;
}
}
if (sortByElements != null) {
StringBuilder builder = new StringBuilder();
builder.append(" ORDER BY ");
for(SortByElement sortByElement: sortByElements) {
builder.append(sortByElement.getExpression()).append(" ");
final boolean ascending = sortAscending == sortByElement.isAscending();
builder.append(ascending ? "ASC NULLS FIRST" : "DESC NULLS LAST").append(",");
}
builder.deleteCharAt(builder.length() - 1);
sortByPhrase = builder.toString();
}
// implying precedence rules
String[] lookFor = { "AND", "OR" };
for (int idx = 0; idx < lookFor.length; idx++) {
boolean found = true;
while (found) {
found = false;
ListIterator<String> iter = whereBuilder.listIterator(0);
while (iter.hasNext()) {
String queryPart = iter.next();
if (lookFor[idx].equals(queryPart)) {
iter.remove();
String nextPart = iter.next();
iter.remove();
String prevPart = iter.previous();
iter.set(StringFormat.format("( %1$s %2$s %3$s )", prevPart, queryPart, nextPart));
found = true;
break;
}
}
}
}
// Since when we search for events we must only search
// for not deleted events, add this to the where clause
if (searchObjStr.equalsIgnoreCase("EVENT")) {
whereBuilder.add("not deleted");
}
// adding WHERE if required and All implicit AND
StringBuilder wherePhrase = new StringBuilder();
if (whereBuilder.size() > 0) {
wherePhrase.append(" WHERE ");
ListIterator<String> iter = whereBuilder.listIterator(0);
while (iter.hasNext()) {
String queryPart = iter.next();
wherePhrase.append(queryPart);
if (iter.hasNext()) {
wherePhrase.append(" AND ");
}
}
}
// adding the sorting part if required
if ("".equals(sortByPhrase)) {
sortByPhrase = " ORDER BY " + searchObjectAC.getDefaultSort(searchObjStr);
}
// adding the paging phrase
String pagePhrase = getPagePhrase(syntax, pageNumber);
String primeryKey = searchObjectAC.getPrimeryKeyName(searchObjStr);
String tableName = searchObjectAC.getRelatedTableName(searchObjStr, useTags);
// adding a secondary default sort by entity name
StringBuilder sortExpr = new StringBuilder();
sortExpr.append(sortByPhrase);
if ( sortByPhrase.indexOf(searchObjectAC.getDefaultSort(searchObjStr)) < 0) {
sortExpr.append(",");
sortExpr.append(searchObjectAC.getDefaultSort(searchObjStr));
}
// TODO: The database configuration PostgresSearchTemplate has an extra closing braces. Hence our
// queries in this code have an extra opening one. Fix it in a future patch.
String inQuery = "";
if (useTags) {
inQuery = StringFormat.format(
"SELECT * FROM %1$s WHERE ( %2$s IN (%3$s)",
searchObjectAC.getRelatedTableName(searchObjStr, false),
primeryKey,
getInnerQuery(tableName, primeryKey, fromStatement, wherePhrase, sortExpr));
} else {
inQuery = "(" + getInnerQuery(tableName, "*", fromStatement, wherePhrase, sortExpr);
}
if (syntax.getSearchFrom() > 0) {
inQuery = StringFormat.format("%1$s and %2$s > %3$s", inQuery, primeryKey, syntax.getSearchFrom());
}
retval =
StringFormat.format(Config.<String> getValue(ConfigValues.DBSearchTemplate),
sortExpr.toString(),
inQuery,
pagePhrase);
// Check for sql injection if query is not safe
if (!isSafe) {
if (sqlInjectionChecker.hasSqlInjection(retval)) {
throw new SqlInjectionException();
}
}
log.trace("Search: {}", retval);
}
return retval;
}
private String getInnerQuery(String tableName, String primeryKey, String fromStatement, StringBuilder wherePhrase, StringBuilder sortExpr) {
// prevent using distinct when the sort expression has a function call since when distinct is used it is performed first and sorting
// is done on the result, so all fields in the sort clause should appear in the result set after distinct is applied
if (sortExpr.indexOf("(") > 0) {
return StringFormat.format("SELECT %1$s.%2$s FROM %3$s %4$s", tableName, primeryKey, fromStatement, wherePhrase);
}
else {
return StringFormat.format("SELECT distinct %1$s.%2$s FROM %3$s %4$s", tableName, primeryKey, fromStatement, wherePhrase);
}
}
protected String getPagePhrase(SyntaxContainer syntax, String pageNumber) {
String result = "";
Integer page = IntegerCompat.tryParse(pageNumber);
if (page == null) {
page = 1;
}
PagingType pagingType = getPagingType();
if (pagingType != null) {
String pagingSyntax = Config.<String> getValue(ConfigValues.DBPagingSyntax);
BigInteger bigPage = BigInteger.valueOf(page);
BigInteger bigCount = BigInteger.valueOf(syntax.getMaxCount());
BigInteger bigX = bigPage.subtract(BigInteger.ONE).multiply(bigCount).add(BigInteger.ONE);
BigInteger bigY = bigPage.multiply(bigCount);
switch (pagingType) {
case Range:
result =
StringFormat.format(pagingSyntax, bigX, bigY);
break;
case Offset:
result = StringFormat.format(pagingSyntax, bigX, bigCount);
break;
}
}
return result;
}
private PagingType getPagingType() {
String val = Config.<String> getValue(ConfigValues.DBPagingType);
PagingType type = null;
try {
type = PagingType.valueOf(val);
} catch (Exception e) {
log.error("Unknown paging type '{}'", val);
}
return type;
}
/**
* It describes one element of SQL 'ORDER BY' clause
*/
public static class SortByElement {
/**
* DB value expression, usually column just column name
*
* <p>Example of non-trivial value:
* <pre>"COALESCE(nullable_column_name, 'string replacing NULL')"</pre>
* </p>
*/
private final String expression;
/**
* asc/desc
*/
private final boolean ascending;
public SortByElement(String expression) {
this.expression = expression;
this.ascending = true;
}
public SortByElement(String expression, boolean ascending) {
this.expression = expression;
this.ascending = ascending;
}
public String getExpression() {
return expression;
}
public boolean isAscending() {
return ascending;
}
}
private enum ConditionType {
None,
FreeText,
FreeTextSpecificObj,
ConditionWithDefaultObj,
ConditionwithSpesificObj;
}
private ConditionData generateConditionStatment(SyntaxObject obj, ListIterator<SyntaxObject> objIter,
final String searchObjStr, final boolean caseSensitive, final boolean issafe, final boolean useTags) {
final String safeValue = issafe ? obj.getBody() : SqlInjectionChecker.enforceEscapeCharacters(obj.getBody());
return generateSafeConditionStatement(obj, objIter, searchObjStr, caseSensitive, safeValue, useTags);
}
private ConditionData generateSafeConditionStatement(final SyntaxObject obj,
ListIterator<SyntaxObject> objIter,
final String searchObjStr,
final boolean caseSensitive,
final String safeValue,
final boolean useTags) {
IConditionFieldAutoCompleter conditionFieldAC;
IConditionValueAutoCompleter conditionValueAC = null;
// check for sql injection
String fieldName = "";
String objName;
ConditionType conditionType;
SyntaxObject previous = objIter.previous();
SyntaxObject prev = previous;
SyntaxObjectType prevType = prev.getType();
if (prevType != SyntaxObjectType.CONDITION_RELATION) {
// free text of default search object
objName = searchObjStr;
conditionFieldAC = searchObjectAC.getFieldAutoCompleter(searchObjStr);
conditionType = ConditionType.FreeText;
} else {
prev = objIter.previous();
if (prev.getType() == SyntaxObjectType.CROSS_REF_OBJ) { // free text
// search
// for some
// object
objName = prev.getBody();
conditionFieldAC = searchObjectAC.getFieldAutoCompleter(objName);
conditionType = ConditionType.FreeTextSpecificObj;
} else { // if (prev.getType() == SyntaxObjectType.CONDITION_FIELD)
fieldName = prev.getBody();
prev = objIter.previous();
if (prev.getType() != SyntaxObjectType.DOT) {
// standard condition with default AC (search obj)
objName = searchObjStr;
conditionFieldAC = searchObjectAC.getFieldAutoCompleter(searchObjStr);
conditionType = ConditionType.ConditionWithDefaultObj;
} else {
// standard condition with specific AC
prev = objIter.previous();
objName = prev.getBody();
conditionFieldAC = searchObjectAC.getFieldAutoCompleter(objName);
conditionType = ConditionType.ConditionwithSpesificObj;
}
}
conditionValueAC = conditionFieldAC.getFieldValueAutoCompleter(fieldName);
}
final BaseConditionFieldAutoCompleter conditionAsBase =
(BaseConditionFieldAutoCompleter) ((conditionFieldAC instanceof BaseConditionFieldAutoCompleter) ? conditionFieldAC
: null);
final Class<?> curType = conditionAsBase != null ? conditionAsBase.getTypeDictionary().get(fieldName) : null;
final String customizedValue =
buildCustomizedValue(obj, conditionFieldAC, conditionValueAC, safeValue, fieldName, curType);
final String customizedRelation =
buildCustomizedRelation(caseSensitive,
conditionFieldAC,
conditionValueAC,
fieldName,
previous,
prevType);
return buildCondition(caseSensitive,
conditionFieldAC,
customizedValue,
customizedRelation,
fieldName,
objName,
conditionType,
useTags);
}
private String buildCustomizedRelation(final boolean caseSensitive,
IConditionFieldAutoCompleter conditionFieldAC,
IConditionValueAutoCompleter conditionValueAC,
String fieldName,
SyntaxObject previous,
SyntaxObjectType prevType) {
String customizedRelation = "=";
if (prevType == SyntaxObjectType.CONDITION_RELATION) {
customizedRelation = previous.getBody();
}
if (conditionValueAC == null && ("".equals(fieldName) ||
String.class.equals(conditionFieldAC.getDbFieldType(fieldName)))) {
/* enable case-insensitive search by changing operation to I/LIKE */
if ("=".equals(customizedRelation)) {
customizedRelation = conditionFieldAC.getMatchingSyntax(fieldName, true, caseSensitive);
} else if ("!=".equals(customizedRelation)) {
customizedRelation = conditionFieldAC.getMatchingSyntax(fieldName, false, caseSensitive);
}
}
return customizedRelation;
}
private String buildCustomizedValue(SyntaxObject obj,
IConditionFieldAutoCompleter conditionFieldAC,
IConditionValueAutoCompleter conditionValueAC,
String safeValue,
String fieldName,
final Class<?> curType) {
String customizedValue = safeValue;
if (curType == String.class && !StringHelper.isNullOrEmpty(customizedValue)
&& !"''".equals(customizedValue) && !"'*'".equals(customizedValue)) {
customizedValue = BaseConditionFieldAutoCompleter.getI18NPrefix() + customizedValue;
}
if (conditionValueAC != null) {
customizedValue = StringFormat.format("'%1$s'",
conditionValueAC.convertFieldEnumValueToActualValue(obj.getBody()));
} else if ("".equals(fieldName) /* search on all relevant fields */||
String.class.equals(conditionFieldAC.getDbFieldType(fieldName))) {
customizedValue = customizedValue.replace("*", conditionFieldAC.getWildcard(fieldName));
}
return customizedValue;
}
final ConditionData buildCondition(boolean caseSensitive,
IConditionFieldAutoCompleter conditionFieldAC,
String customizedValue,
String customizedRelation,
String fieldName,
String objName,
ConditionType conditionType,
boolean useTags) {
String tableName;
// We will take the table with tags for all subtables
// TODO: Optimize this
if (conditionType == ConditionType.ConditionwithSpesificObj) {
tableName = searchObjectAC.getRelatedTableName(objName, true);
} else {
tableName = searchObjectAC.getRelatedTableName(objName, fieldName, useTags);
}
ConditionData conditionData = new ConditionData();
switch (conditionType) {
case FreeText:
case FreeTextSpecificObj:
conditionData.setConditionText(conditionFieldAC.buildFreeTextConditionSql(tableName,
customizedRelation,
escapeUnderScore(customizedValue, customizedRelation),
caseSensitive));
conditionData.setFullTableRequired(true);
break;
case ConditionWithDefaultObj:
case ConditionwithSpesificObj:
conditionData.setConditionText(conditionFieldAC.buildConditionSql(objName,
fieldName,
customizedValue,
customizedRelation,
tableName,
caseSensitive));
conditionData.setFullTableRequired(false);
break;
default:
conditionData.setConditionText("");
conditionData.setFullTableRequired(false);
}
return conditionData;
}
public static String escapeUnderScore(final String customizedValue, final String customizedRelation) {
String escapedValue = customizedValue;
if (customizedRelation.equalsIgnoreCase("LIKE") || customizedRelation.equalsIgnoreCase("ILIKE")) {
// Since '_' is treated in Postgres as '?' when using like, (i.e. match any single character)
// we have to escape this character in the value to make it treated as a regular character.
// Due to changes between PG8.x and PG9.x on ESCAPE representation in a string, we should
// figure out what PG Release is running in order to escape the special character(_) correctly
// This is done in a IF block and not with Method Factory pattern since this is the only change
// right now, if we encounter other changes, this will be refactored to use the Method Factory pattern.
escapedValue = customizedValue.replace("_", getEscapedCharacter("_"));
}
return escapedValue;
}
public static String getEscapedCharacter(String charToEscape) {
int pgMajorRelease = Config.<Integer> getValue(ConfigValues.PgMajorRelease);
if (pgMajorRelease == SyntaxChecker.PgMajorRelease.PG8.getValue()) {
return "\\\\" + charToEscape;
} else if (pgMajorRelease == SyntaxChecker.PgMajorRelease.PG9.getValue()) {
return "\\" + charToEscape;
}
return charToEscape;
}
private static enum PgMajorRelease {
PG8(8),
PG9(9);
private int value;
private PgMajorRelease(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
private static class ConditionData {
private String conditionText;
private boolean fullTableRequired = false;
public String getConditionText() {
return conditionText;
}
public void setConditionText(String conditionText) {
this.conditionText = conditionText;
}
public boolean isFullTableRequired() {
return fullTableRequired;
}
public void setFullTableRequired(boolean fullTableRequired) {
this.fullTableRequired = fullTableRequired;
}
}
}