package com.temenos.interaction.odataext.odataparser;
/*
* Utilities for converting between oData parameters and editable structures containing the same information. This is
* intended to replace the, light weight, oData parser under com.temenos.interaction.authorization.command.util.
*
* This version uses parsing based on oData4j to implement the full oData parameter syntax (previous version used in
* house code to support a limited subset).
*/
/*
* #%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%
*/
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.core.MultivaluedMap;
import org.odata4j.expression.BoolCommonExpression;
import org.odata4j.expression.CommonExpression;
import org.odata4j.expression.EntitySimpleProperty;
import org.odata4j.expression.OrderByExpression;
import org.odata4j.producer.EntityQueryInfo;
import org.odata4j.producer.resources.OptionsQueryParser;
import com.temenos.interaction.core.command.InteractionContext;
import com.temenos.interaction.odataext.odataparser.data.FieldName;
import com.temenos.interaction.odataext.odataparser.data.OrderBy;
import com.temenos.interaction.odataext.odataparser.data.RowFilter;
import com.temenos.interaction.odataext.odataparser.data.RowFilters;
import com.temenos.interaction.odataext.odataparser.odata4j.PrintExpressionVisitor;
import com.temenos.interaction.odataext.odataparser.output.ParameterPrinter;
public class ODataParser {
// Odata parameter keys.
public static final String FILTER_KEY = "$filter";
public static final String SELECT_KEY = "$select";
public static final String TOP_KEY = "$top";
public static final String SKIP_KEY = "$skip";
public static final String ORDERBY_KEY = "$orderby";
// Convert an OData string parameter into filters.
public static RowFilters parseFilters(String filterStr) {
// Parse in odat4j format
return (new RowFilters(filterStr));
}
// Support for old code that still uses RowFilter list. (note method name
// Filter NOT Filters)
@Deprecated
public static List<RowFilter> parseFilter(String filterStr) throws UnsupportedQueryOperationException {
// First do it the new way
RowFilters filters = parseFilters(filterStr);
// Then convert to old style
return filters.asRowFilters();
}
// Convert an OData select string parameter into a set of field names.
public static Set<FieldName> parseSelect(String selectStr) {
// Parse in odat4j format
List<EntitySimpleProperty> odata4jSelects = OData4jParseSelect(selectStr);
// Wrap the odata4j output.
Set<FieldName> selects = new HashSet<FieldName>();
for (EntitySimpleProperty select : odata4jSelects) {
selects.add(new FieldName(select));
}
return selects;
}
private static List<EntitySimpleProperty> OData4jParseSelect(String selectStr) {
if (null == selectStr) {
return (null);
}
return OptionsQueryParser.parseSelect(selectStr);
}
// Parse an $orderby expression.
public static List<OrderBy> parseOrderBy(String orderBy) {
if (null == orderBy) {
return null;
}
List<OrderByExpression> expressions = OData4jParseOrderBys(orderBy);
// Wrap the odata4j output.
List<OrderBy> orderBys = new ArrayList<OrderBy>();
for (OrderByExpression expression : expressions) {
orderBys.add(new OrderBy(expression));
}
return orderBys;
}
private static List<OrderByExpression> OData4jParseOrderBys(String orderByStr) {
if (null == orderByStr) {
return (null);
}
return OptionsQueryParser.parseOrderBy(orderByStr);
}
// Convert filter to an oData parameter.
public static String toFilters(RowFilters filters) {
if ((null == filters) || (filters.isBlockAll())) {
// Can't print the block all filters,
throw new NullPointerException("Cannot print 'block all' filter.");
}
return OData4jToFilters(filters.getOData4jExpression());
}
// Convert filter to an oData parameter using a custom visitor.
public static String toFilters(RowFilters filters, PrintExpressionVisitor visitor) {
if ((null == filters) || (filters.isBlockAll())) {
// Can't print the block all filters,
throw new NullPointerException("Cannot print 'block all' filter.");
}
return OData4jToFilters(filters.getOData4jExpression(), visitor);
}
// Once it is no longer needed for back compatibility this should be made
// private.
public static String OData4jToFilters(CommonExpression filters) {
return PrintOData4jToFilters(filters, new ParameterPrinter());
}
public static String OData4jToFilters(CommonExpression filters, PrintExpressionVisitor visitor) {
return PrintOData4jToFilters(filters, new ParameterPrinter(visitor));
}
private static String PrintOData4jToFilters(CommonExpression filters, ParameterPrinter printer) {
if (null != filters) {
StringBuffer sb = new StringBuffer();
printer.appendParameter(sb, filters, true);
return (sb.toString());
} else {
// This is the empty filter list case. Just return an empty string;
return "";
}
}
// Support for old code that still uses RowFilter list.
@Deprecated
public static String toFilter(List<RowFilter> filters) {
String filterStr = new String();
boolean first = true;
for (RowFilter filter : filters) {
if (first) {
first = false;
} else {
filterStr = filterStr.concat(" and ");
}
filterStr = filterStr.concat(toFilter(filter));
}
return filterStr;
}
// Support for old code that still uses RowFilter list.
@Deprecated
private static String toFilter(RowFilter filter) {
String name = filter.getFieldName().getName();
String filterStr = new String(name + " " + filter.getRelation().getoDataString() + " " + filter.getValue());
return filterStr;
}
// Convert select to an oData parameter
public static String toSelect(Set<FieldName> selects) {
List<EntitySimpleProperty> oData4jSelects = new ArrayList<EntitySimpleProperty>();
for (FieldName select : selects) {
oData4jSelects.add(select.getOData4jExpression());
}
return OData4jToSelect(oData4jSelects);
}
// Convert select to an oData parameter using a custom visitor.
public static String toSelect(Set<FieldName> selects, PrintExpressionVisitor visitor) {
List<EntitySimpleProperty> oData4jSelects = new ArrayList<EntitySimpleProperty>();
for (FieldName select : selects) {
oData4jSelects.add(select.getOData4jExpression());
}
return OData4jToSelect(oData4jSelects, visitor);
}
private static String OData4jToSelect(List<EntitySimpleProperty> selects) {
return PrintOData4jToSelect(selects, new ParameterPrinter());
}
private static String OData4jToSelect(List<EntitySimpleProperty> selects, PrintExpressionVisitor visitor) {
return PrintOData4jToSelect(selects, new ParameterPrinter(visitor));
}
private static String PrintOData4jToSelect(List<EntitySimpleProperty> selects, ParameterPrinter printer) {
StringBuffer sb = new StringBuffer();
printer.appendParameter(sb, selects, true);
return sb.toString();
}
// Convert order by to an OData parameter.
public static String toOrderBy(List<OrderBy> orderByList) {
List<OrderByExpression> oData4jOrderBys = new ArrayList<OrderByExpression>();
for (OrderBy orderBy : orderByList) {
oData4jOrderBys.add(orderBy.getOData4jExpression());
}
return OData4jToOrderBy(oData4jOrderBys);
}
public static String toOrderBy(List<OrderBy> orderByList, PrintExpressionVisitor visitor) {
List<OrderByExpression> oData4jOrderBys = new ArrayList<OrderByExpression>();
for (OrderBy orderBy : orderByList) {
oData4jOrderBys.add(orderBy.getOData4jExpression());
}
return OData4jToOrderBy(oData4jOrderBys, visitor);
}
private static String OData4jToOrderBy(List<OrderByExpression> orderBys) {
return PrintOData4jToOrderBy(orderBys, new ParameterPrinter());
}
private static String OData4jToOrderBy(List<OrderByExpression> orderBys, PrintExpressionVisitor visitor) {
return PrintOData4jToOrderBy(orderBys, new ParameterPrinter(visitor));
}
private static String PrintOData4jToOrderBy(List<OrderByExpression> orderBys, ParameterPrinter printer) {
StringBuffer sb = new StringBuffer();
printer.appendParameter(sb, orderBys, true);
return sb.toString();
}
/*
* Obtain the odata query information from the context's query parameters.
* This parses the incoming parameters into an oData4j EntityQueryInfo
* object. Further work is than required to convert this into our internal
* representation.
*/
public static EntityQueryInfo getEntityQueryInfo(InteractionContext ctx) {
MultivaluedMap<String, String> queryParams = ctx.getQueryParameters();
// Unpack parameters
String filter = queryParams.getFirst(FILTER_KEY);
String select = queryParams.getFirst(SELECT_KEY);
return new EntityQueryInfo(OptionsQueryParser.parseFilter(filter), null, null,
OptionsQueryParser.parseSelect(select));
}
// Convert an OData filter into a list of authorization framework
// RowFilters. A complete implementation of this would be complex. For now
// only parse simple filters and throw on failure.
public static List<RowFilter> parseFilter(BoolCommonExpression expression)
throws UnsupportedQueryOperationException {
if (null != expression) {
RowFilters filters = new RowFilters(expression);
return filters.asRowFilters();
}
return new ArrayList<RowFilter>();
}
// Convert an OData select into a list of authorization framework
// field names.
public static Set<FieldName> parseSelect(List<EntitySimpleProperty> propList) {
if (null == propList) {
return (null);
}
Set<FieldName> select = new HashSet<FieldName>();
for (EntitySimpleProperty prop : propList) {
select.add(new FieldName(prop));
}
return select;
}
// Errors thrown by parsing
public static class UnsupportedQueryOperationException extends Exception {
private static final long serialVersionUID = 1L;
public UnsupportedQueryOperationException(String message) {
super(message);
}
}
}