/*
*
* Copyright (c) 2010 ForgeRock Inc. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://www.opensource.org/licenses/cddl1.php or
* OpenIDM/legal/CDDLv1.0.txt
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at OpenIDM/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted 2010 [name of copyright owner]"
*
* $Id$
*/
package org.forgerock.openicf.connectors.xml;
import org.forgerock.openicf.connectors.xml.query.ComparisonQuery;
import org.forgerock.openicf.connectors.xml.query.ContainsQuery;
import org.forgerock.openicf.connectors.xml.query.FunctionQuery;
import org.forgerock.openicf.connectors.xml.query.abstracts.Query;
import org.forgerock.openicf.connectors.xml.query.QueryImpl;
import org.forgerock.openicf.connectors.xml.util.NamespaceLookupUtil;
import java.util.ArrayList;
import java.util.List;
import org.identityconnectors.framework.common.objects.AttributeBuilder;
import org.identityconnectors.framework.common.objects.AttributeUtil;
import org.identityconnectors.framework.common.objects.filter.*;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.Uid;
/**
* This is an implementation of AbstractFilterTranslator that gives a concrete representation
* of which filters can be applied at the connector level (natively). If the
* XQuery doesn't support a certain expression type, that factory
* method should return null. This level of filtering is present only to allow any
* native contructs that may be available to help reduce the result set for the framework,
* which will (strictly) reapply all filters specified after the connector does the initial
* filtering.<p><p>Note: The generic query type is most commonly a String, but does not have to be.
*
* @author $author$
* @version $Revision$ $Date$
* @since 1.0
*/
public class XMLFilterTranslator extends AbstractFilterTranslator<Query> {
private boolean preferUidOverName = false;
public XMLFilterTranslator(boolean preferUidOverName) {
this.preferUidOverName = preferUidOverName;
}
private String getAttributeName(Attribute attribute) {
String attrName = attribute.getName();
if (!preferUidOverName && Uid.NAME.equals(attrName)) {
attrName = Name.NAME;
}
return attrName;
}
@Override
public Query createEndsWithExpression(EndsWithFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
String prefixedName = createNameWithNamespace(attrName);
String value = AttributeUtil.getSingleValue(filter.getAttribute()).toString();
String[] args = createFunctionArgs(prefixedName, value);
return createFunctionQuery(args, "ends-with", not);
}
@Override
public Query createContainsAllValuesExpression(ContainsAllValuesFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
List<Object> values = filter.getAttribute().getValue();
int numOfValues = values.size();
if (numOfValues == 0) {
return null;
} else if (numOfValues == 1) {
ContainsFilter cf = new ContainsFilter(AttributeBuilder.build(attrName, values.get(0)));
return createContainsExpression(cf, not);
} else {
List<Query> equalsQueries = new ArrayList<Query>();
for (int i = 0; i < values.size(); i++) {
String value = values.get(i).toString();
EqualsFilter ef = new EqualsFilter(AttributeBuilder.build(attrName, value));
Query query = createEqualsExpression(ef, not);
equalsQueries.add(query);
}
Query orQuery = null;
Query leftSide = equalsQueries.get(0);
// creates and returns chained or-expression with all values
for (int i = 1; i < numOfValues; i++) {
Query rightSide = equalsQueries.get(i);
orQuery = createOrExpression(leftSide, rightSide);
if (i != numOfValues) {
leftSide = orQuery;
}
}
return orQuery;
}
}
@Override
public Query createStartsWithExpression(StartsWithFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
String prefixedName = createNameWithNamespace(attrName);
String value = AttributeUtil.getSingleValue(filter.getAttribute()).toString();
String[] args = createFunctionArgs(prefixedName, value);
return createFunctionQuery(args, "starts-with", not);
}
@Override
public Query createContainsExpression(ContainsFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
String prefixedName = createNameWithNamespace(attrName);
String value = AttributeUtil.getSingleValue(filter.getAttribute()).toString();
// contains uses a different format than the other functions
Query query = new QueryImpl();
query.set(new ContainsQuery(prefixedName, value, not));
return query;
}
@Override
public Query createEqualsExpression(EqualsFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
String prefixedName = createNameWithNamespace(attrName);
String value = AttributeUtil.getSingleValue(filter.getAttribute()).toString();
return createComparisonQuery(prefixedName, not ? "!=" : "=", value);
}
@Override
public Query createGreaterThanExpression(GreaterThanFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
String prefixedName = createNameWithNamespace(attrName);
String value = AttributeUtil.getSingleValue(filter.getAttribute()).toString();
return createComparisonQuery(prefixedName, not ? "<" : ">", value);
}
@Override
public Query createGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
String prefixedName = createNameWithNamespace(attrName);
String value = AttributeUtil.getSingleValue(filter.getAttribute()).toString();
return createComparisonQuery(prefixedName, not ? "<=" : ">=", value);
}
@Override
public Query createAndExpression(Query leftExpression, Query rightExpression) {
leftExpression.and(rightExpression);
return leftExpression;
}
@Override
public Query createOrExpression(Query leftExpression, Query rightExpression) {
leftExpression.or(rightExpression);
return leftExpression;
}
@Override
public Query createLessThanExpression(LessThanFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
String prefixedName = createNameWithNamespace(attrName);
String value = AttributeUtil.getSingleValue(filter.getAttribute()).toString();
return createComparisonQuery(prefixedName, not ? ">" : "<", value);
}
@Override
public Query createLessThanOrEqualExpression(LessThanOrEqualFilter filter, boolean not) {
String attrName = getAttributeName(filter.getAttribute());
String prefixedName = createNameWithNamespace(attrName);
String value = AttributeUtil.getSingleValue(filter.getAttribute()).toString();
return createComparisonQuery(prefixedName, not ? ">=" : "<=", value);
}
private Query createComparisonQuery(String name, String operator, String value) {
Query query = new QueryImpl();
query.set(new ComparisonQuery("$x/" + name, operator, escape(value)));
return query;
}
private Query createFunctionQuery(String[] args, String function, boolean not) {
Query query = new QueryImpl();
query.set(new FunctionQuery(args, function, not));
return query;
}
private String[] createFunctionArgs(String attrName, String value) {
String[] args = {"$x/" + attrName, escape(value)};
return args;
}
private String createNameWithNamespace(String attrName) {
String prefix = NamespaceLookupUtil.INSTANCE.getAttributePrefix(attrName);
return prefix + ":" + attrName;
}
private String escape(String value) {
return "'" + value.replaceAll("'","''") + "'";
}
}