package com.temenos.interaction.odataext.odataparser.data;
import java.util.ArrayList;
import java.util.List;
import org.odata4j.expression.AndExpression;
import org.odata4j.expression.BinaryCommonExpression;
import org.odata4j.expression.BoolCommonExpression;
import org.odata4j.expression.CommonExpression;
import org.odata4j.expression.EntitySimpleProperty;
import org.odata4j.expression.Expression;
import org.odata4j.expression.IntegralLiteral;
import org.odata4j.expression.StringLiteral;
import org.odata4j.producer.resources.OptionsQueryParser;
import com.temenos.interaction.odataext.odataparser.ODataParser;
import com.temenos.interaction.odataext.odataparser.ODataParser.UnsupportedQueryOperationException;
/*
* Classes containing information about a set of row filters.
*
* An empty RowFilters (allow everything) has a null oData4jExpression;
*
* A 'block everything' Rowfilters is represented by a either a null pointer or an oData4jExpression of BLOCK_ALL. The
* latter is required because if a null RowFilter is added to an existing RowFilter we can't null the parent object.
*/
/*
* #%L
* interaction-odata4j-ext
* %%
* Copyright (C) 2012 - 2013 Temenos Holdings N.V.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
public class RowFilters {
// Wrapped OData4j object. Null means an empty filter list.
private BoolCommonExpression oData4jExpression;
// Special oData object used to indicate a 'block all' access filter. Any
// BooleanCommonExpression will do.
private static BoolCommonExpression blockAllFilter = Expression.or(null, null);
public RowFilters() {
// By default we have an empty list
oData4jExpression = null;
}
public RowFilters(String filterStr) {
if (filterStr.isEmpty()) {
// OData4j parser appears to throw on empty strings. Mark this as an
// empty filter.
oData4jExpression = null;
} else {
oData4jExpression = OptionsQueryParser.parseFilter(filterStr);
}
}
@Deprecated
public RowFilters(List<RowFilter> filterList) {
if (filterList.isEmpty()) {
// OData4j parser appears to throw on empty strings. Mark this as an
// empty filter.
oData4jExpression = null;
} else {
// Add all the filters
for (RowFilter filter : filterList) {
addFilters((BoolCommonExpression) filter.getOData4jExpression());
}
}
}
public RowFilters(BoolCommonExpression expression) {
oData4jExpression = expression;
}
public BoolCommonExpression getOData4jExpression() {
return oData4jExpression;
}
// Add (and) a filter to the current filter.
public void addFilters(String filterStr) {
BoolCommonExpression newExpression = OptionsQueryParser.parseFilter(filterStr);
addFilters(newExpression);
}
// Add (and) extra filters to the current filter. We would like a signature
// something like:
public void addFilters(RowFilters addFilters) {
if ((null == addFilters) || (blockAllFilter == addFilters.getOData4jExpression())) {
// One side blocks everything. So must null result.
oData4jExpression = blockAllFilter;
} else {
addFilters(addFilters.getOData4jExpression());
}
}
// Add (and) a filter with the list. Where possible use the
// addFilter(RowFilters...) instead of this.
public void addFilters(BoolCommonExpression expr) {
if (null == expr) {
// For an empty filter nothing to add.
return;
}
if (null == oData4jExpression) {
// Null oData4jExpression is 'allow everything'. So now allow the
// added filter.
oData4jExpression = expr;
} else {
// If either expression is 'block all then the result is 'block
// all'.
if ((blockAllFilter == oData4jExpression) || (blockAllFilter == expr)) {
oData4jExpression = blockAllFilter;
} else {
// We become a new 'and' expression with the old expression and
// the
// added
// expression as leafs.
oData4jExpression = Expression.and(oData4jExpression, expr);
}
}
}
/*
* Convert to a list of, old style, RowFilters. Used for backwards
* compatibility with code that has not been converted to use 'RowFilters'.
*
* Can fail if the old style RowFilter syntax cannot express the contents of
* the BoolCommonExpression. In this case we may cause a security issue. So
* must throw.
*/
@Deprecated
public List<RowFilter> asRowFilters() throws UnsupportedQueryOperationException {
// Start wih top level expression
return asRowFilters(oData4jExpression);
}
@Deprecated
private List<RowFilter> asRowFilters(BoolCommonExpression expression) throws UnsupportedQueryOperationException {
List<RowFilter> filters = new ArrayList<RowFilter>();
if (null == expression) {
// This is the empty row filter case. Return the empty list.
return filters;
}
// Split BoolCommonExpression up across AndExpressions.
if (expression instanceof AndExpression) {
filters.addAll(asRowFilters(((AndExpression) expression).getLHS()));
filters.addAll(asRowFilters(((AndExpression) expression).getRHS()));
} else {
// Only handle the known relationships.
Relation rel = null;
for (Relation relation : Relation.values()) {
if (relation.getOData4jClass().isInstance(expression)) {
// Found it
rel = relation;
break;
}
}
if (null == rel) {
throw new UnsupportedQueryOperationException("Unrecognised relationship type " + expression);
}
if (!BinaryCommonExpression.class.isAssignableFrom(expression.getClass())) {
throw new UnsupportedQueryOperationException("Expression \"" + expression
+ "\" cannot be converted to a BinaryCommonExpression.");
}
CommonExpression lhsExpression = ((BinaryCommonExpression) expression).getLHS();
String lhsStr = toRowFilterCompatible(lhsExpression);
CommonExpression rhsExpression = ((BinaryCommonExpression) expression).getRHS();
String rhsStr = toRowFilterCompatible(rhsExpression);
filters.add(new RowFilter(lhsStr, rel, rhsStr));
}
return filters;
}
// Convert expression to a RowFilter name/value. Throw if it's too complex.
private String toRowFilterCompatible(CommonExpression expr) throws UnsupportedQueryOperationException {
// Convert expression to a string.
String str = ODataParser.OData4jToFilters(expr);
if (!(expr instanceof IntegralLiteral) && !(expr instanceof EntitySimpleProperty)
&& !(expr instanceof StringLiteral)) {
throw new UnsupportedQueryOperationException("Expression too complex for row filter. Type=\"" + expr
+ "\" value=\"" + str + "\"");
}
return str;
}
public boolean isEmpty() {
return (null == getOData4jExpression());
}
public boolean isBlockAll() {
return (blockAllFilter == getOData4jExpression());
}
}