/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/**
* Utility class to handle the parsing of an IQuery object into all the relevant LDAP search
* information. Uses LdapSearchDetails class to store this information and return the search details.
* This class was intended for use by the execution classes that need to translate SQL. As new capabilities
* are implemented, this class will be expanded to accommodate the appropriate SQL.
*
* This class should remove all the MMX-specific stuff, and turn it into something any
* LDAP implementation can understand.
*
*/
package org.teiid.translator.ldap;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.SortKey;
import org.teiid.core.util.EquivalenceUtil;
import org.teiid.core.util.StringUtil;
import org.teiid.language.*;
import org.teiid.language.Comparison.Operator;
import org.teiid.language.SortSpecification.Ordering;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.metadata.Column;
import org.teiid.metadata.Datatype;
import org.teiid.metadata.Table;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.TypeFacility;
import org.teiid.translator.ldap.LDAPExecutionFactory.SearchDefaultScope;
/**
* Utility class which translates a SQL query into an LDAP search.
*/
public class IQueryToLdapSearchParser {
private static final String ATTRIBUTES = "attributes"; //$NON-NLS-1$
private static final String COUNT_LIMIT = "count-limit"; //$NON-NLS-1$
private static final String TIMEOUT = "timeout";//$NON-NLS-1$
private static final String SEARCH_SCOPE = "search-scope";//$NON-NLS-1$
private static final String CRITERIA = "filter";//$NON-NLS-1$
private static final String CONTEXT_NAME = "context-name";//$NON-NLS-1$
LDAPExecutionFactory executionFactory;
/**
* Constructor.
*/
public IQueryToLdapSearchParser(LDAPExecutionFactory factory) {
this.executionFactory = factory;
}
/**
* Public entry point to the parser.
* Parses the IQuery object, and constructs an equivalent LDAP search filter,
* keeping track of the attributes of interest.
* Here are some example SQL queries, and the equivalent LDAP search info:
* SQL: select cn, managerName from people_table where managerName LIKE "John%" and cn!="Mar()"
* Context name: [people_table's NameInSource, e.g. (ou=people,dc=company,dc=com)]
* LDAP attributes: (cn, String), (managerName, String)
* LDAP search filter: (&(managerName="John*")(!(cn="Mar\(\)")))
*
* @param query the query
* @return the LDAPSearchDetails object
*/
// GHH 20080326 - added ability to restrict queries to only values where
// objectClass = table name. This is done by adding a third parameter,
// RESTRICT, to the NameInSource property in the model:
// ou=people,dc=company,dc=com?SUBTREE_SCOPE?RESTRICT
// TODO - change method for calling RESTRICT to also specify
// object class name (RESTRICT=inetOrgPerson)
public LDAPSearchDetails translateSQLQueryToLDAPSearch(Select query) throws TranslatorException {
// Parse SELECT symbols.
// The columns will be translated into LDAP attributes of interest.
ArrayList<Column> elementList = getElementsFromSelectSymbols(query);
// Parse FROM table.
// Only one table is expected here.
List<TableReference> fromList = query.getFrom();
Iterator<TableReference> itr = fromList.iterator();
if(!itr.hasNext()) {
final String msg = LDAPPlugin.Util.getString("IQueryToLdapSearchParser.noTablesInFromError"); //$NON-NLS-1$
throw new TranslatorException(msg);
}
TableReference fItm = itr.next();
if(itr.hasNext()) {
final String msg = LDAPPlugin.Util.getString("IQueryToLdapSearchParser.multiItemsInFromError"); //$NON-NLS-1$
throw new TranslatorException(msg);
}
LDAPSearchDetails sd = null;
NamedTable tbl = null;
NamedTable tblRight = null;
if (fItm instanceof NamedTable) {
tbl = (NamedTable)fItm;
} else if (fItm instanceof Join) {
Join join = (Join)fItm;
if (!(join.getLeftItem() instanceof NamedTable) || !(join.getRightItem() instanceof NamedTable)) {
//should not happen
final String msg = LDAPPlugin.Util.getString("IQueryToLdapSearchParser.groupCountExceededError"); //$NON-NLS-1$
throw new TranslatorException(msg);
}
tbl = (NamedTable)join.getLeftItem();
tblRight = (NamedTable)join.getRightItem();
} else {
throw new AssertionError("Unsupported construct"); //$NON-NLS-1$
}
String contextName = getContextNameFromFromItem(tbl);
int searchScope = getSearchScopeFromFromItem(tbl);
// GHH 20080326 - added check for RESTRICT parameter in
// NameInSource of from item
String classRestriction = getRestrictToNamedClass(tbl);
if (tblRight != null) {
String contextName1 = getContextNameFromFromItem(tblRight);
int searchScope1 = getSearchScopeFromFromItem(tblRight);
String classRestriction1 = getRestrictToNamedClass(tblRight);
if (!EquivalenceUtil.areEqual(contextName, contextName1) || searchScope != searchScope1
|| !EquivalenceUtil.areEqual(classRestriction, classRestriction1)) {
final String msg = LDAPPlugin.Util.getString("IQueryToLdapSearchParser.not_same", tbl.getMetadataObject().getFullName(), tblRight.getMetadataObject().getFullName()); //$NON-NLS-1$
throw new TranslatorException(msg);
}
}
// Parse the WHERE clause.
// Create an equivalent LDAP search filter.
List<String> searchStringList = new LinkedList<String>();
searchStringList = getSearchFilterFromWhereClause(query.getWhere(), searchStringList);
StringBuilder filterBuilder = new StringBuilder();
for (String string : searchStringList) {
filterBuilder.append(string);
}
// GHH 20080326 - if there is a class restriction,
// add it to the search filter
if (classRestriction != null && classRestriction.trim().length()>0) {
filterBuilder.insert(0, "(&").append("(objectClass=").append(classRestriction).append("))"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
// Parse the ORDER BY clause.
// Create an ordered sort list.
OrderBy orderBy = query.getOrderBy();
// Referenced the JNDI standard...arguably, this should not be done inside this
// class, and we should make our own key class. In practice, this makes things simpler.
SortKey[] sortKeys = getSortKeysFromOrderByClause(orderBy);
// Parse LIMIT clause.
// Note that offsets are not supported.
Limit limit = query.getLimit();
long countLimit = -1;
if(limit != null) {
countLimit = limit.getRowLimit();
}
// Create Search Details
sd = new LDAPSearchDetails(contextName, searchScope, filterBuilder.toString(), sortKeys, countLimit, elementList, 0);
// Search Details logging
sd.printDetailsToLog();
return sd;
}
/**
* get SortKeys from the supplied ORDERBY clause.
* @param orderBy the OrderBy clause
* @param the array of SortKeys
*/
private SortKey[] getSortKeysFromOrderByClause(OrderBy orderBy) throws TranslatorException {
SortKey[] sortKeys = null;
if(orderBy != null) {
List<SortSpecification> orderItems = orderBy.getSortSpecifications();
if(orderItems == null) {
return null;
}
SortKey sortKey = null;
sortKeys = new SortKey[orderItems.size()];
Iterator<SortSpecification> orderItr = orderItems.iterator();
int i = 0;
while(orderItr.hasNext()) {
SortSpecification item = orderItr.next();
String itemName = getExpressionQueryString(item.getExpression());
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "Adding sort key for item:", itemName); //$NON-NLS-1$
if(item.getOrdering() == Ordering.ASC) {
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "with ASC ordering."); //$NON-NLS-1$
sortKey = new SortKey(itemName, true, null);
} else if(item.getOrdering() == Ordering.DESC){
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "with DESC ordering."); //$NON-NLS-1$
sortKey = new SortKey(itemName, false, null);
}
sortKeys[i] = sortKey;
i++;
}
} else {
// Insert a default? No, allow the Execution to do this. Just return a null list.
}
return sortKeys;
}
/**
* Utility method to pull the name in source (or, base DN/context name) from the table.
* @param fromItem
* @return name in source
*/
// GHH 20080409 - changed to fall back on new connector property
// for default base DN if available
private String getContextNameFromFromItem(NamedTable fromItem) throws TranslatorException {
String nameInSource;
String contextName;
// TODO: Re-use the getExpressionString method if possible, rather than
// rewriting the same code twice.
Table group = fromItem.getMetadataObject();
nameInSource = group.getNameInSource();
// if NameInSource is null set it to an empty
// string instead so we can safely call split on it
if(nameInSource == null) {
nameInSource = ""; //$NON-NLS-1$
}
// now split it on ? to find the part of it that specifies context name
String nameInSourceArray[] = nameInSource.split("\\?"); //$NON-NLS-1$
contextName = nameInSourceArray[0];
// if there is no context name specified
// try the default in the connector properties
if(contextName.equals("")) { //$NON-NLS-1$
contextName = this.executionFactory.getSearchDefaultBaseDN();
}
// if the context name is not specified either in Name in Source
// or in the default connector properties it'll be either
// null or an empty string
if(contextName == null || contextName.equals("")) { //$NON-NLS-1$
final String msg = LDAPPlugin.Util.getString("IQueryToLdapSearchParser.baseContextNameError"); //$NON-NLS-1$
throw new TranslatorException(msg);
}
return contextName;
}
// GHH 20080326 - added below method to check for RESTRICT parameter in
// from item's NameInSource, and if true return name (not NameInSource)
// of from item as the objectClass name on to which the query should
// be restricted
private String getRestrictToNamedClass(NamedTable fromItem) {
String nameInSource;
String namedClass = null;
// Here we use slightly different logic than in
// getContextNameFromFromItem so it is easier to get
// the group name later if needed
Table mdIDGroup = fromItem.getMetadataObject();
nameInSource = mdIDGroup.getNameInSource();
// groupName = mdIDGroup.getName();
// if NameInSource is null set it to an empty
// string instead so we can safely call split on it
if(nameInSource == null) {
nameInSource = ""; //$NON-NLS-1$
}
// now split it on ? to find the part of it that specifies the objectClass we should restrict on
String nameInSourceArray[] = nameInSource.split("\\?"); //$NON-NLS-1$
if(nameInSourceArray.length >= 3) {
namedClass = nameInSourceArray[2];
}
// if there is no specification in the Name In Source,
// see if the connector property is set to true. If
// it is, use the Name of the class for the restriction.
if(namedClass == null || namedClass.equals("")) { //$NON-NLS-1$
if (!this.executionFactory.isRestrictToObjectClass()) {
namedClass = ""; //$NON-NLS-1$
} else {
namedClass = mdIDGroup.getName();
}
}
return namedClass;
}
private int getSearchScopeFromFromItem(NamedTable fromItem) {
String searchScopeString = ""; //$NON-NLS-1$
int searchScope = LDAPConnectorConstants.ldapDefaultSearchScope;
// TODO: Re-use the getExpressionString method if possible, rather than
// rewriting the same code twice.
Table group = fromItem.getMetadataObject();
String nameInSource = group.getNameInSource();
// if NameInSource is null set it to an empty
// string instead so we can safely call split on it
if(nameInSource == null) {
nameInSource = ""; //$NON-NLS-1$
}
// now split it on ? to find the part of it that specifies search scope
String nameInSourceArray[] = nameInSource.split("\\?"); //$NON-NLS-1$
if(nameInSourceArray.length >= 2) {
searchScopeString = nameInSourceArray[1];
}
// if there is no search scope specified
// try the default in the connector properties
if(searchScopeString.equals("")) { //$NON-NLS-1$
SearchDefaultScope searchDefaultScope = this.executionFactory.getSearchDefaultScope();
if (searchDefaultScope != null) {
searchScopeString = searchDefaultScope.name();
}
// protect against getting null back from the property
if(searchScopeString == null) {
searchScopeString = ""; //$NON-NLS-1$
}
}
if(searchScopeString.equals("SUBTREE_SCOPE")) { //$NON-NLS-1$
searchScope = SearchControls.SUBTREE_SCOPE;
} else if(searchScopeString.equals("ONELEVEL_SCOPE")) { //$NON-NLS-1$
searchScope = SearchControls.ONELEVEL_SCOPE;
} else if(searchScopeString.equals("OBJECT_SCOPE")) { //$NON-NLS-1$
searchScope = SearchControls.OBJECT_SCOPE;
}
return searchScope;
}
/**
* Utility method to convert operator to the appropriate string value for LDAP.
* @param op operator to evaluate
* @return LDAP-specific string equivalent of the operator
*/
private String parseCompoundCriteriaOp(AndOr.Operator op) throws TranslatorException {
switch(op) {
case AND:
return "&"; //$NON-NLS-1$
case OR:
return "|"; //$NON-NLS-1$
default:
final String msg = LDAPPlugin.Util.getString("IQueryToLdapSearchParser.criteriaNotParsableError"); //$NON-NLS-1$
throw new TranslatorException(msg);
}
}
/**
* Utility method to convert expression to the appropriate string value for LDAP.
* @param e expression to evaluate
* @return LDAP-specific string equivalent of the expression
*/
// GHH 20080326 - found that code to fall back on Name if NameInSource
// was null wasn't working properly, so replaced with tried and true
// code from another custom connector.
private String getExpressionQueryString(Expression e) throws TranslatorException {
String expressionName = null;
// GHH 20080326 - changed around the IElement handling here
// - the rest of this method is unchanged
if(e instanceof ColumnReference) {
Column mdIDElement = ((ColumnReference)e).getMetadataObject();
expressionName = mdIDElement.getNameInSource();
if(expressionName == null || expressionName.equals("")) { //$NON-NLS-1$
expressionName = mdIDElement.getName();
}
} else if(e instanceof Literal) {
expressionName = getLiteralString((Literal)e);
} else {
final String msg = LDAPPlugin.Util.getString("IQueryToLdapSearchParser.unsupportedElementError", e.getClass().getSimpleName()); //$NON-NLS-1$
throw new TranslatorException(msg + e.toString());
}
expressionName = escapeReservedChars(expressionName);
return expressionName;
}
static String getLiteralQueryString(Expression lhs, Expression rhs) {
Column mdIDElement = ((ColumnReference)lhs).getMetadataObject();
String expressionName = getLiteralString(mdIDElement, (Literal)rhs);
expressionName = escapeReservedChars(expressionName);
return expressionName;
}
static String getLiteralString(Column mdIDElement, Literal l) {
String type = mdIDElement.getProperty(LDAPExecutionFactory.RDN_TYPE, false);
String expressionName = getLiteralString(l);
if (l.getValue() != null && type != null) {
String prefix = mdIDElement.getProperty(LDAPExecutionFactory.DN_PREFIX, false);
expressionName = type + "=" + expressionName; //$NON-NLS-1$
if (prefix != null) {
expressionName = expressionName + "," + prefix; //$NON-NLS-1$
}
}
return expressionName;
}
static String getLiteralString(Literal l) {
if(l.getValue() instanceof Timestamp) {
Timestamp ts = (Timestamp)l.getValue();
Date dt = new Date(ts.getTime());
//TODO: Fetch format if provided.
SimpleDateFormat sdf = new SimpleDateFormat(LDAPConnectorConstants.ldapTimestampFormat);
String expressionName = sdf.format(dt);
return expressionName;
}
if (l.getValue() != null) {
return l.getValue().toString();
}
return "null"; //$NON-NLS-1$
}
static String escapeReservedChars(String expr) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < expr.length(); i++) {
char curChar = expr.charAt(i);
escapeReserved(sb, curChar);
}
return sb.toString();
}
static void escapeReserved(StringBuilder sb, char curChar) {
switch (curChar) {
case '\\':
sb.append("\\5c"); //$NON-NLS-1$
break;
case '*':
sb.append("\\2a"); //$NON-NLS-1$
break;
case '(':
sb.append("\\28"); //$NON-NLS-1$
break;
case ')':
sb.append("\\29"); //$NON-NLS-1$
break;
case '\u0000':
sb.append("\\00"); //$NON-NLS-1$
break;
default:
sb.append(curChar);
}
}
/**
* Recursive method to translate the where clause into an LDAP search filter.
* The goal is to convert infix notation to prefix (Polish) notation.
* TODO: There's probably a clever way to do this with a Visitor.
* @param criteria Criteria to evaluate.
* @param List list to hold each pre-fix character of the search filter.
* @return list list that can be traversed in order to construct the search filter.
*/
private List<String> getSearchFilterFromWhereClause(Condition criteria, List<String> filterList) throws TranslatorException {
if(criteria == null) {
filterList.add("(objectClass=*)"); //$NON-NLS-1$
return filterList;
}
boolean isNegated = false;
// Recursive case: compound criteria
if(criteria instanceof AndOr) {
AndOr crit = (AndOr)criteria;
AndOr.Operator op = crit.getOperator();
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "Parsing compound criteria."); //$NON-NLS-1$
String stringOp = parseCompoundCriteriaOp(op);
filterList.add("("); //$NON-NLS-1$
filterList.add(stringOp);
filterList.addAll(getSearchFilterFromWhereClause(crit.getLeftCondition(), new LinkedList<String>()));
filterList.addAll(getSearchFilterFromWhereClause(crit.getRightCondition(), new LinkedList<String>()));
filterList.add(")"); //$NON-NLS-1$
// Base case
} else if(criteria instanceof Comparison) {
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "Parsing compare criteria."); //$NON-NLS-1$
Comparison.Operator op = ((Comparison) criteria).getOperator();
isNegated = op == Operator.NE || op == Operator.GT || op == Operator.LT;
Expression lhs = ((Comparison) criteria).getLeftExpression();
Expression rhs = ((Comparison) criteria).getRightExpression();
String lhsString = getExpressionQueryString(lhs);
String rhsString = getLiteralQueryString(lhs, rhs);
addCompareCriteriaToList(filterList, op, lhsString, rhsString);
} else if(criteria instanceof Like) {
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "Parsing LIKE criteria."); //$NON-NLS-1$
Like like = (Like) criteria;
isNegated = like.isNegated();
// Convert LIKE to Equals, where any "%" symbol is replaced with "*".
Comparison.Operator op = Operator.EQ;
Expression lhs = like.getLeftExpression();
Expression rhs = like.getRightExpression();
String lhsString = getExpressionQueryString(lhs);
String rhsString = getLiteralString(((ColumnReference)lhs).getMetadataObject(), (Literal)rhs);
if (like.getEscapeCharacter() != null) {
char escape = like.getEscapeCharacter();
boolean escaped = false;
StringBuilder result = new StringBuilder();
for (int i = 0; i < rhsString.length(); i++) {
char c = rhsString.charAt(i);
if (c == escape) {
if (escaped) {
escapeReserved(result, c);
escaped = false;
} else {
escaped = true;
}
} else {
if (escaped) {
escaped = false;
} else {
if (c == '%') {
result.append('*');
continue;
} else if (c == '_') {
throw new TranslatorException(LDAPPlugin.Util.getString("IQueryToLdapSearchParser.invalid_pattern")); //$NON-NLS-1$
}
}
escapeReserved(result, c);
}
}
rhsString = result.toString();
} else {
rhsString = escapeReservedChars(rhsString);
rhsString = rhsString.replace("%", "*"); //$NON-NLS-1$ //$NON-NLS-2$
if (rhsString.indexOf('_') >= 0) {
throw new TranslatorException(LDAPPlugin.Util.getString("IQueryToLdapSearchParser.invalid_pattern")); //$NON-NLS-1$
}
}
addCompareCriteriaToList(filterList, op, lhsString, rhsString);
// Base case
} else if(criteria instanceof In) {
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "Parsing IN criteria."); //$NON-NLS-1$
isNegated = ((In) criteria).isNegated();
Expression lhs = ((In)criteria).getLeftExpression();
List<Expression> rhsList = ((In)criteria).getRightExpressions();
// Recursively add each IN expression to the filter list.
processInCriteriaList(filterList, rhsList, lhs);
} else if (criteria instanceof Not) {
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "Parsing NOT criteria."); //$NON-NLS-1$
isNegated = true;
filterList.addAll(getSearchFilterFromWhereClause(((Not)criteria).getCriteria(), new LinkedList<String>()));
} else {
throw new AssertionError("unknown predicate type"); //$NON-NLS-1$
}
if (isNegated) {
filterList.add(0, "(!"); //$NON-NLS-1$
filterList.add(")"); //$NON-NLS-1$
}
return filterList;
}
/**
* Process a list of right-hand side IN expresssions and add the corresponding LDAP filter
* clause string to the given filterList.
*/
private void processInCriteriaList(List<String> filterList, List<Expression> rhsList, Expression lhs) throws TranslatorException {
if(rhsList.size() == 0) {
return;
}
filterList.add("("); //$NON-NLS-1$
filterList.add(parseCompoundCriteriaOp(org.teiid.language.AndOr.Operator.OR));
Iterator<Expression> rhsItr = rhsList.iterator();
while(rhsItr.hasNext()) {
addCompareCriteriaToList(filterList, Operator.EQ, getExpressionQueryString(lhs),
getLiteralQueryString(lhs, rhsItr.next()));
}
filterList.add(")"); //$NON-NLS-1$
}
/**
* Add Compare Criteria to List
* @param filterList the filter list
* @param op
* @param lhs left hand side expression
* @param rhs right hand side expression
*/
private void addCompareCriteriaToList(List<String> filterList, Comparison.Operator op, String lhs, String rhs) throws TranslatorException {
// Push the comparison statement into the list, e.g.:
// (sn=Mike)
// !(empNum>=100)
filterList.add("("); //$NON-NLS-1$
filterList.add(lhs);
switch(op) {
case NE:
case EQ:
filterList.add("="); //$NON-NLS-1$
break;
case LT:
case GE:
filterList.add(">="); //$NON-NLS-1$
break;
case GT:
case LE:
filterList.add("<="); //$NON-NLS-1$
break;
default:
final String msg = LDAPPlugin.Util.getString("IQueryToLdapSearchParser.criteriaNotSupportedError"); //$NON-NLS-1$
throw new TranslatorException(msg);
}
filterList.add(rhs);
filterList.add(")"); //$NON-NLS-1$
}
/**
* Method to get SELECT Element list from the supplied query
* @param query the supplied Query
* @return the list of SELECT elements
*/
private ArrayList<Column> getElementsFromSelectSymbols(Select query) {
ArrayList<Column> selectElementList = new ArrayList<Column>();
Iterator<DerivedColumn> selectSymbolItr = query.getDerivedColumns().iterator();
while(selectSymbolItr.hasNext()) {
Column e = getElementFromSymbol(selectSymbolItr.next());
selectElementList.add(e);
}
return selectElementList;
}
/**
* Helper method for getting runtime {@link org.teiid.connector.metadata.runtime.Element} from a
* {@link org.teiid.language.DerivedColumn}.
* @param symbol Input ISelectSymbol
* @return Element returned metadata runtime Element
*/
private Column getElementFromSymbol(DerivedColumn symbol) {
ColumnReference expr = (ColumnReference) symbol.getExpression();
return expr.getMetadataObject();
}
public LDAPSearchDetails buildRequest(String query) throws TranslatorException {
ArrayList<String> attributes = new ArrayList<String>();
ArrayList<Column> columns = new ArrayList<Column>();
String contextName = null;
String criteria = ""; //$NON-NLS-1$
String searchScope = this.executionFactory.getSearchDefaultScope().name();
int timeLimit = 0;
long countLimit = -1;
List<String> parts = StringUtil.tokenize(query, ';');
for (String var : parts) {
int index = var.indexOf('=');
if (index == -1) {
throw new TranslatorException(LDAPPlugin.Util.gs(LDAPPlugin.Event.TEIID12013, var));
}
String key = var.substring(0, index).trim();
String value = var.substring(index+1).trim();
if (key.equalsIgnoreCase(CONTEXT_NAME)) {
contextName = value;
}
else if (key.equalsIgnoreCase(CRITERIA)) {
criteria = value;
}
else if (key.equalsIgnoreCase(SEARCH_SCOPE)) {
searchScope = value;
}
else if (key.equalsIgnoreCase(TIMEOUT)) {
timeLimit = Integer.parseInt(value);
}
else if (key.equalsIgnoreCase(COUNT_LIMIT)) {
countLimit = Long.parseLong(value);
}
else if (key.equalsIgnoreCase(ATTRIBUTES)) {
StringTokenizer attrTokens = new StringTokenizer(value, ","); //$NON-NLS-1$
while(attrTokens.hasMoreElements()) {
String name = attrTokens.nextToken().trim();
attributes.add(name);
Column column = new Column();
column.setName(name);
Datatype type = new Datatype();
type.setName(TypeFacility.RUNTIME_NAMES.OBJECT);
type.setJavaClassName(Object.class.getCanonicalName());
column.setDatatype(type, true);
columns.add(column);
}
} else {
throw new TranslatorException(LDAPPlugin.Util.gs(LDAPPlugin.Event.TEIID12013, var));
}
}
int searchScopeInt = buildSearchScope(searchScope);
return new LDAPSearchDetails(contextName, searchScopeInt, criteria, null, countLimit, columns, timeLimit);
}
private int buildSearchScope(String searchScope) {
int searchScopeInt = 0;
// this could be one of OBJECT_SCOPE, ONELEVEL_SCOPE, SUBTREE_SCOPE
if (searchScope.equalsIgnoreCase("OBJECT_SCOPE")) { //$NON-NLS-1$
searchScopeInt = SearchControls.OBJECT_SCOPE;
}
else if (searchScope.equalsIgnoreCase("ONELEVEL_SCOPE")) {//$NON-NLS-1$
searchScopeInt = SearchControls.ONELEVEL_SCOPE;
}
else if (searchScope.equalsIgnoreCase("SUBTREE_SCOPE")) {//$NON-NLS-1$
searchScopeInt = SearchControls.SUBTREE_SCOPE;
}
return searchScopeInt;
}
}