/********************************************************************************************************************* | |
* Copyright (c) 2008, 2012 Attensity Europe GmbH and brox IT Solutions GmbH. All rights reserved. This program and the | |
* accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this | |
* distribution, and is available at http://www.eclipse.org/legal/epl-v10.html | |
**********************************************************************************************************************/ | |
package org.eclipse.smila.search.servlet; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import org.apache.commons.lang.StringUtils; | |
import org.eclipse.smila.datamodel.AnyMap; | |
import org.eclipse.smila.datamodel.Value; | |
import org.eclipse.smila.search.api.QueryConstants; | |
import org.eclipse.smila.search.api.QueryConstants.SortOrder; | |
import org.eclipse.smila.search.api.helper.QueryBuilder; | |
import org.eclipse.smila.utils.collections.MultiValueMap; | |
/** | |
* base class for request parsers. | |
* | |
* @author jschumacher | |
*/ | |
public class ARequestParser { | |
/** | |
* prefix for http parameter names specifying attribute values. The attribute name is the rest of the parameter name | |
* after this prefix. | |
*/ | |
public static final String PREFIX_ATTRIBUTE = "A."; | |
/** | |
* prefix for SortBy parameter. After the prefix follows the attribute name, the value is either ASCENDING or | |
* DESCENDING. | |
*/ | |
public static final String PREFIX_SORTBY = QueryConstants.SORTBY + "."; | |
/** | |
* prefix for enum filter values. Rest of parameter name is attribute name. | |
*/ | |
public static final String PREFIX_FILTER_VAL = "F.val."; | |
/** | |
* prefix for range filter min values. Rest of parameter name is attribute name. | |
*/ | |
public static final String PREFIX_FILTER_MIN = "F.min."; | |
/** | |
* prefix for range filter max values. Rest of parameter name is attribute name. | |
*/ | |
public static final String PREFIX_FILTER_MAX = "F.max."; | |
/** | |
* prefix for ranking parameters. Rest of parameter name is attribute name dot ranking parameter. | |
*/ | |
public static final String PREFIX_RANKING = "R."; | |
public static final String PREFIX_SOLR = "_solr."; | |
/** | |
* collects enum filter values during parameter processing. | |
*/ | |
private final MultiValueMap<String, String> _filterValues = new MultiValueMap<String, String>(); | |
/** | |
* collects range filter min values during parameter processing. | |
*/ | |
private final Map<String, String> _filterMin = new HashMap<String, String>(); | |
/** | |
* collects range filter max values during parameter processing. | |
*/ | |
private final Map<String, String> _filterMax = new HashMap<String, String>(); | |
/** ranking parameters. */ | |
private AnyMap _ranking; | |
/** | |
* create new instance with default pipeline. | |
* | |
* @param defaultPipeline | |
* default pipeline name to use, if request does not contain a pipeline parameter. | |
*/ | |
public ARequestParser() { | |
super(); | |
} | |
/** | |
* add stuff collected while parsing the request to query builder. | |
* | |
* @param builder | |
* query builder. | |
*/ | |
protected void setupQuery(final QueryBuilder builder) { | |
if (_ranking != null) | |
builder.setRanking(_ranking); | |
setupFilters(builder); | |
setDefaultParameters(builder); | |
} | |
/** | |
* create filters from information collected during http parameter processing. | |
* | |
* @param builder | |
* query builder | |
*/ | |
protected void setupFilters(final QueryBuilder builder) { | |
for (final String attributeName : _filterValues.keySet()) | |
setupListFilter(builder, attributeName); | |
final Set<String> rangeFilterAttributes = new HashSet<String>(); | |
rangeFilterAttributes.addAll(_filterMin.keySet()); | |
rangeFilterAttributes.addAll(_filterMax.keySet()); | |
for (final String attributeName : rangeFilterAttributes) | |
setupRangeFilter(builder, attributeName); | |
} | |
/** | |
* add an enum filter. | |
* | |
* @param builder | |
* query builder. | |
* @param attributeName | |
* attribute name. | |
*/ | |
protected void setupListFilter(final QueryBuilder builder, final String attributeName) { | |
final List<String> values = _filterValues.get(attributeName); | |
if ((values != null) && (values.size() > 0)) | |
try { | |
builder.addOneOfFilter(attributeName, values.toArray()); | |
} catch (Exception ex) { | |
ex = null; // ignore. strings are ok here. | |
} | |
} | |
/** | |
* add a range filter. | |
* | |
* @param builder | |
* query builder. | |
* @param attributeName | |
* attribute name. | |
*/ | |
protected void setupRangeFilter(final QueryBuilder builder, final String attributeName) { | |
try { | |
builder.addRangeFilter(attributeName, _filterMin.get(attributeName), _filterMax.get(attributeName)); | |
} catch (Exception ex) { | |
ex = null; // ignore. strings are ok here. | |
} | |
} | |
/** | |
* set default parameters for resultSize and resultOffset parameters, if no values where found in the request. This is | |
* done mainly to make life easier for the XSL sheet processing the result. | |
* | |
* @param builder | |
* query builder created from http request. | |
*/ | |
protected void setDefaultParameters(final QueryBuilder builder) { | |
if (!builder.getMetadata().containsKey(QueryConstants.MAXCOUNT)) | |
builder.setMaxCount(QueryConstants.DEFAULT_MAXCOUNT); | |
if (!builder.getMetadata().containsKey(QueryConstants.OFFSET)) | |
builder.setOffset(QueryConstants.DEFAULT_OFFSET); | |
} | |
/** | |
* process http param values. | |
* | |
* @param builder | |
* query builder | |
* @param paramName | |
* parameter name | |
* @param paramValues | |
* parameter values, must not be null or empty. | |
*/ | |
protected void processParameter(final QueryBuilder builder, final String paramName, final String[] paramValues) { | |
final List<String> nonEmptyValues = new ArrayList<String>(paramValues.length); | |
for (final String paramValue : paramValues) | |
if (!StringUtils.isEmpty(paramValue)) | |
nonEmptyValues.add(paramValue); | |
if (!nonEmptyValues.isEmpty()) | |
if (paramName.startsWith(PREFIX_RANKING)) | |
processRankingParameter(builder, paramName, nonEmptyValues); | |
else if (paramName.startsWith(PREFIX_FILTER_VAL)) | |
processFilterValue(paramName, nonEmptyValues); | |
else if (paramName.startsWith(PREFIX_FILTER_MIN)) | |
processFilterMin(paramName, nonEmptyValues); | |
else if (paramName.startsWith(PREFIX_FILTER_MAX)) | |
processFilterMax(paramName, nonEmptyValues); | |
else if (paramName.startsWith(PREFIX_SORTBY)) | |
processSortBy(builder, paramName, nonEmptyValues); | |
else if (paramName.startsWith(PREFIX_ATTRIBUTE)) { | |
final String name = paramName.substring(PREFIX_ATTRIBUTE.length()); | |
builder.setQueryAttribute(name, nonEmptyValues.toArray()); | |
} else | |
setCustomParameterValues(builder, paramName, nonEmptyValues); | |
} | |
/** | |
* process a parameter name of the form "Fmax.<attributeName>" to set a max value in a range filter annotation for | |
* an attribute. | |
* | |
* @param paramName | |
* parameter name | |
* @param paramValues | |
* parameter values. Only the first value is used. | |
*/ | |
private void processFilterMax(final String paramName, final List<String> paramValues) { | |
final String attributeName = paramName.substring(PREFIX_FILTER_MAX.length()); | |
if (!StringUtils.isEmpty(attributeName)) | |
_filterMax.put(attributeName, paramValues.get(0)); | |
} | |
/** | |
* process a parameter name of the form "Fmin.<attributeName>" to set a min value in a range filter annotation for | |
* an attribute. | |
* | |
* @param paramName | |
* parameter name | |
* @param paramValues | |
* parameter values. Only the first value is used. | |
*/ | |
private void processFilterMin(final String paramName, final List<String> paramValues) { | |
final String attributeName = paramName.substring(PREFIX_FILTER_MIN.length()); | |
if (!StringUtils.isEmpty(attributeName)) | |
_filterMin.put(attributeName, paramValues.get(0)); | |
} | |
/** | |
* process a parameter name of the form "Fval.<attributeName>" to set a filter mode annotation for an attribute. | |
* | |
* @param paramName | |
* parameter name | |
* @param paramValues | |
* parameter values. All values are added to the filter annotation. | |
*/ | |
private void processFilterValue(final String paramName, final List<String> paramValues) { | |
final String attributeName = paramName.substring(PREFIX_FILTER_VAL.length()); | |
if (!StringUtils.isEmpty(attributeName)) | |
for (final String value : paramValues) | |
if (!StringUtils.isEmpty(value)) | |
_filterValues.add(attributeName, value); | |
} | |
/** | |
* process a parameter name of the form "R.<parameter>" or "R.<attribute>.<parameter>" to set ranking | |
* annotation values either on the record or one of its attributes. | |
* | |
* @param builder | |
* query builder | |
* @param paramName | |
* parameter name | |
* @param paramValues | |
* parameter values. Only the first value is used. | |
*/ | |
private void processRankingParameter(final QueryBuilder builder, final String paramName, | |
final List<String> paramValues) { | |
final String attributeAndParameterName = paramName.substring(PREFIX_RANKING.length()); | |
final int dotIndex = attributeAndParameterName.indexOf('.'); | |
String valueName = null; | |
String attributeName = null; | |
if (dotIndex < 0) | |
valueName = attributeAndParameterName; | |
else { | |
valueName = attributeAndParameterName.substring(0, dotIndex); | |
attributeName = attributeAndParameterName.substring(dotIndex + 1); | |
} | |
final Value paramValue = builder.getFactory().createStringValue(paramValues.get(0)); | |
if (_ranking == null) | |
_ranking = builder.getFactory().createAnyMap(); | |
if (attributeName != null) { | |
AnyMap attributeRanking = _ranking.getMap(attributeName); | |
if (attributeRanking == null) { | |
attributeRanking = builder.getFactory().createAnyMap(); | |
_ranking.put(attributeName, attributeRanking); | |
} | |
attributeRanking.put(valueName, paramValue); | |
} else | |
_ranking.put(valueName, paramValue); | |
} | |
/** | |
* set query parameters from http params. | |
* | |
* @param builder | |
* query builder | |
* @param paramName | |
* parameter name | |
* @param paramValues | |
* parameter values, must not be null or empty. | |
*/ | |
protected void setCustomParameterValues(final QueryBuilder builder, final String paramName, | |
final List<String> paramValues) { | |
// set first value as single-value-parameter | |
if (paramValues.size() == 1) | |
builder.setParameter(paramName, paramValues.get(0)); | |
else | |
// set all values als list parameter. | |
for (final String value : paramValues) | |
builder.addParameter(paramName, value); | |
} | |
/** | |
*/ | |
private void processSortBy(final QueryBuilder builder, final String paramName, final List<String> paramValues) { | |
final String attributeName = paramName.substring(PREFIX_SORTBY.length()); | |
if (!StringUtils.isEmpty(attributeName)) | |
try { | |
final SortOrder orderMode = SortOrder.valueOf(paramValues.get(0).toUpperCase()); | |
builder.addSortBy(attributeName, orderMode); | |
} catch (Exception ex) { | |
ex = null; // illegal order by mode | |
} | |
} | |
} |