package org.ovirt.engine.core.searchbackend;
import java.util.ArrayList;
import java.util.HashMap;
import org.ovirt.engine.core.compat.Regex;
import org.ovirt.engine.core.compat.StringHelper;
public class ADSyntaxChecker implements ISyntaxChecker {
private AdSearchObjecAutoCompleter searchObjectAC;
private BaseAutoCompleter colonAC;
private BaseAutoCompleter pluralAC;
private HashMap<SyntaxObjectType, SyntaxObjectType[]> stateMap;
protected static final String USER_ACCOUNT_TYPE = "$USER_ACCOUNT_TYPE";
private static final String LDAP_GROUP_CATEGORY = "$LDAP_GROUP_CATEGORY";
private Regex firstDQRegexp;
private Regex nonSpaceRegexp;
public ADSyntaxChecker() {
searchObjectAC = new AdSearchObjecAutoCompleter();
colonAC = new BaseAutoCompleter(":");
pluralAC = new BaseAutoCompleter("S");
firstDQRegexp = new Regex("^\\s*\"$");
nonSpaceRegexp = new Regex("^\\S+$");
stateMap = new HashMap<>();
SyntaxObjectType[] beginArray = { SyntaxObjectType.SEARCH_OBJECT };
stateMap.put(SyntaxObjectType.BEGIN, beginArray);
SyntaxObjectType[] searchObjectArray = { SyntaxObjectType.COLON };
stateMap.put(SyntaxObjectType.SEARCH_OBJECT, searchObjectArray);
SyntaxObjectType[] colonArray = { SyntaxObjectType.CONDITION_FIELD, SyntaxObjectType.END };
stateMap.put(SyntaxObjectType.COLON, colonArray);
SyntaxObjectType[] conditionFieldArray = { SyntaxObjectType.CONDITION_RELATION };
stateMap.put(SyntaxObjectType.CONDITION_FIELD, conditionFieldArray);
SyntaxObjectType[] conditionRelationArray = { SyntaxObjectType.CONDITION_VALUE };
stateMap.put(SyntaxObjectType.CONDITION_RELATION, conditionRelationArray);
SyntaxObjectType[] conditionValueArray = { SyntaxObjectType.CONDITION_FIELD };
stateMap.put(SyntaxObjectType.CONDITION_VALUE, conditionValueArray);
}
@Override
public SyntaxContainer analyzeSyntaxState(String searchText, boolean final2) {
SyntaxContainer retval = new SyntaxContainer(searchText);
IConditionFieldAutoCompleter AdConditionFieldAC;
if (searchText.toUpperCase().contains("ADUSER")) {
AdConditionFieldAC = new AdUserConditionFieldAutoCompleter();
} else {
AdConditionFieldAC = new AdGroupConditionFieldAutoCompleter();
}
IAutoCompleter conditionRelationAC;
char[] searchCharArr = searchText.toCharArray();
boolean betweenDoubleQuotes = false;
int curStartPos = 0;
String curConditionField = "";
for (int idx = 0; idx < searchCharArr.length; idx++) {
SyntaxObjectType curState = retval.getState();
char curChar = searchCharArr[idx];
if ((curChar == ' ') && (curState != SyntaxObjectType.CONDITION_RELATION)) {
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)) {
// ERROR INVALID-SEARCH OBJECT
retval.setErr(SyntaxError.INVALID_SEARCH_OBJECT, curStartPos, idx - curStartPos + 1);
return retval;
}
} 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();
}
}
retval.addSyntaxObject(SyntaxObjectType.SEARCH_OBJECT, nextObject, curStartPos, idx + 1);
retval.setvalid(true);
curStartPos = idx + 1;
}
break;
case SEARCH_OBJECT:
if (!colonAC.validate(nextObject)) {
if (!colonAC.validateCompletion(nextObject)) {
retval.setErr(SyntaxError.COLON_NOT_NEXT_TO_SEARCH_OBJECT, curStartPos, idx + 1);
return retval;
}
} else {
retval.addSyntaxObject(SyntaxObjectType.COLON, nextObject, idx, idx + 1);
curStartPos = idx + 1;
retval.setvalid(true);
}
break;
case COLON:
case CONDITION_VALUE:
if (AdConditionFieldAC.validate(nextObject)) {
retval.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD, nextObject, curStartPos, idx + 1);
curConditionField = nextObject;
curStartPos = idx + 1;
} else if (!AdConditionFieldAC.validateCompletion(nextObject)) {
retval.setErr(SyntaxError.INVALID_CONDITION_FILED, curStartPos, idx + 1);
return retval;
}
retval.setvalid(false);
break;
case CONDITION_FIELD:
conditionRelationAC = AdConditionFieldAC.getFieldRelationshipAutoCompleter(curConditionField);
if (conditionRelationAC == null) {
retval.setErr(SyntaxError.CONDITION_CANT_CREATE_RRELATIONS_AC, curStartPos, idx + 1);
return retval;
}
if (idx + 1 < searchCharArr.length) {
String tryNextObj = searchText.substring(curStartPos, idx + 2).toUpperCase();
if (conditionRelationAC.validate(tryNextObj)) {
break;
}
}
if (!conditionRelationAC.validate(nextObject)) {
if (!conditionRelationAC.validateCompletion(nextObject)) {
retval.setErr(SyntaxError.INVALID_CONDITION_RELATION, curStartPos, idx + 1);
return retval;
}
} else {
retval.addSyntaxObject(SyntaxObjectType.CONDITION_RELATION, nextObject, curStartPos, idx + 1);
}
curStartPos = idx + 1;
retval.setvalid(false);
break;
case CONDITION_RELATION:
boolean addObjFlag = false;
if (curChar == '"') {
betweenDoubleQuotes = !betweenDoubleQuotes;
if (betweenDoubleQuotes) {
if (!firstDQRegexp.isMatch(strRealObj)) {
retval.setErr(SyntaxError.INVALID_CONDITION_VALUE, curStartPos, idx + 1);
return retval;
}
} 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 == searchCharArr.length)) && !betweenDoubleQuotes &&
!addObjFlag) {
strRealObj = strRealObj.trim();
if (nonSpaceRegexp.isMatch(strRealObj)) {
addObjFlag = true;
} else {
curStartPos = idx + 1;
}
}
} else {
if ((curChar == ' ') && !betweenDoubleQuotes && !addObjFlag) {
strRealObj = strRealObj.trim();
if (nonSpaceRegexp.isMatch(strRealObj)) {
addObjFlag = true;
} else {
curStartPos = idx + 1;
}
}
}
if (addObjFlag) {
if (!AdConditionFieldAC.validateFieldValue(curConditionField, strRealObj)) {
retval.setErr(SyntaxError.INVALID_CONDITION_VALUE, curStartPos, idx);
return retval;
} else {
retval.addSyntaxObject(SyntaxObjectType.CONDITION_VALUE, strRealObj, curStartPos, idx + 1);
curConditionField = "";
}
curStartPos = idx + 1;
retval.setvalid(true);
}
break;
default:
retval.setErr(SyntaxError.UNIDENTIFIED_STATE, curStartPos, idx);
return retval;
}
}
return retval;
}
@Override
public SyntaxContainer getCompletion(String searchText) {
SyntaxContainer retval = analyzeSyntaxState(searchText, false);
IConditionFieldAutoCompleter AdConditionFieldAC;
if (retval.getError() == SyntaxError.NO_ERROR) {
if (searchText.toUpperCase().contains("ADUSER")) {
AdConditionFieldAC = new AdUserConditionFieldAutoCompleter();
} else {
AdConditionFieldAC = new AdGroupConditionFieldAutoCompleter();
}
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 COLON:
retval.addToACList(colonAC.getCompletion(curPartialWord));
break;
case CONDITION_FIELD:
String[] tmpCompletions = AdConditionFieldAC.getCompletion(curPartialWord);
ArrayList<String> nonDuplicates = new ArrayList<>();
for (int itr = 0; itr < tmpCompletions.length; itr++) {
if (!retval.contains(SyntaxObjectType.CONDITION_FIELD, tmpCompletions[itr])) {
nonDuplicates.add(tmpCompletions[itr]);
}
}
retval.addToACList(nonDuplicates.toArray(new String[] {}));
break;
case CONDITION_RELATION:
conditionRelationAC = AdConditionFieldAC.getFieldRelationshipAutoCompleter(retval
.getPreviousSyntaxObject(1, SyntaxObjectType.CONDITION_FIELD));
if (conditionRelationAC != null) {
retval.addToACList(conditionRelationAC.getCompletion(curPartialWord));
}
break;
case CONDITION_VALUE:
conditionValueAC = AdConditionFieldAC.getFieldValueAutoCompleter(retval.getPreviousSyntaxObject(2,
SyntaxObjectType.CONDITION_FIELD));
if (conditionValueAC != null) {
retval.addToACList(conditionValueAC.getCompletion(curPartialWord));
}
break;
}
}
}
return retval;
}
@Override
public String generateQueryFromSyntaxContainer(SyntaxContainer syntax, boolean isSafe) {
String retval = "";
if (syntax.getvalid()) {
retval = generateAdQueryFromSyntaxContainer(syntax);
}
return retval;
}
private static String generateAdQueryFromSyntaxContainer(SyntaxContainer syntax) {
StringBuilder retval = new StringBuilder();
if (syntax.getvalid()) {
IConditionFieldAutoCompleter conditionFieldAC;
boolean searchingUsers = syntax.getSearchObjectStr().toUpperCase().contains("ADUSER");
if (searchingUsers) {
retval.append("(&");
retval.append("(" + USER_ACCOUNT_TYPE + ")");
conditionFieldAC = new AdUserConditionFieldAutoCompleter();
} else {
retval.append("(&(" + LDAP_GROUP_CATEGORY + ")");
conditionFieldAC = new AdGroupConditionFieldAutoCompleter();
}
StringBuilder phrase = new StringBuilder();
boolean nonEqual = false;
boolean findAll = false;
for (SyntaxObject so : syntax) {
switch (so.getType()) {
case CONDITION_FIELD:
if ("ALLNAMES".equals(so.getBody())) {
if (searchingUsers) {
phrase.append(" (|($GIVENNAME={value})(sn={value})($USER_ACCOUNT_NAME={value})($PRINCIPAL_NAME={value}))");
}
else {
phrase.append(" (|($CN={value}))");
}
/**
* mark this search as findAll for later use
*/
findAll = true;
} else {
phrase.append(" (").append(conditionFieldAC.getDbFieldName(so.getBody()));
}
break;
case CONDITION_RELATION:
/**
* append '=' only if not finding all
*/
if (!findAll) {
phrase.append("=");
}
if ("!=".equals(so.getBody())) {
nonEqual = true;
}
break;
case CONDITION_VALUE:
if (findAll) {
/**
* replace all {value} occurences with the value searched. We escape the $ here for regex match,
* as it is used in replace.
*/
phrase = replaceAll(phrase, "{value}", so.getBody().replace("$", "\\$"));
} else {
phrase.append(so.getBody()).append(")");
}
if (nonEqual) {
retval.append("(!").append(phrase).append(")");
} else {
retval.append(phrase.toString());
}
nonEqual = false;
findAll = false;
phrase.delete(0, phrase.length());
break;
default:
break;
}
}
}
retval.append(")");
return retval.toString();
}
private static StringBuilder replaceAll(StringBuilder builder, String oldText, String newText) {
String t = builder.toString();
return new StringBuilder(t.replaceAll(StringHelper.quote(oldText), newText));
}
}