/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.metamodel.query;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Represents a default implementation of the {@link CompiledQuery} interface.
* This implementation does not actually do anything to prepare the query, but
* allows creating a clone of the originating query with the parameters replaced
* by values.
*/
public class DefaultCompiledQuery implements CompiledQuery {
private final Query _query;
private final List<QueryParameter> _parameters;
public DefaultCompiledQuery(Query query) {
_query = query;
_parameters = createParameterList();
}
/**
* Clones the query while replacing query parameters with corresponding
* values.
*
* @param values
* @return
*/
public Query cloneWithParameterValues(Object[] values) {
final AtomicInteger parameterIndex = new AtomicInteger(0);
final Query clonedQuery = _query.clone();
replaceParametersInQuery(values, parameterIndex, _query, clonedQuery);
return clonedQuery;
}
private void replaceParametersInQuery(Object[] values, AtomicInteger parameterIndex, Query originalQuery,
Query newQuery) {
replaceParametersInFromClause(values, parameterIndex, originalQuery, newQuery);
replaceParametersInWhereClause(values, parameterIndex, originalQuery, newQuery);
}
private void replaceParametersInWhereClause(Object[] values, final AtomicInteger parameterIndex,
Query originalQuery, Query newQuery) {
// creates a clone of the original query, but rebuilds a completely new
// where clause based on parameter values
final List<FilterItem> items = originalQuery.getWhereClause().getItems();
int i = 0;
for (FilterItem filterItem : items) {
final FilterItem newFilter = copyFilterItem(filterItem, values, parameterIndex);
if (filterItem != newFilter) {
newQuery.getWhereClause().removeItem(i);
newQuery.getWhereClause().addItem(i, newFilter);
}
i++;
}
}
private void replaceParametersInFromClause(Object[] values, AtomicInteger parameterIndex, Query originalQuery,
Query newQuery) {
final List<FromItem> fromItems = originalQuery.getFromClause().getItems();
int i = 0;
for (FromItem fromItem : fromItems) {
final Query subQuery = fromItem.getSubQuery();
if (subQuery != null) {
final Query newSubQuery = newQuery.getFromClause().getItem(i).getSubQuery();
replaceParametersInQuery(values, parameterIndex, subQuery, newSubQuery);
newQuery.getFromClause().removeItem(i);
newQuery.getFromClause().addItem(i, new FromItem(newSubQuery).setAlias(fromItem.getAlias()));
}
i++;
}
}
private FilterItem copyFilterItem(FilterItem item, Object[] values, AtomicInteger parameterIndex) {
if (item.isCompoundFilter()) {
final FilterItem[] childItems = item.getChildItems();
final FilterItem[] newChildItems = new FilterItem[childItems.length];
for (int i = 0; i < childItems.length; i++) {
final FilterItem childItem = childItems[i];
final FilterItem newChildItem = copyFilterItem(childItem, values, parameterIndex);
newChildItems[i] = newChildItem;
}
final FilterItem newFilter = new FilterItem(item.getLogicalOperator(), newChildItems);
return newFilter;
} else {
if (item.getOperand() instanceof QueryParameter) {
final Object newOperand = values[parameterIndex.getAndIncrement()];
final FilterItem newFilter = new FilterItem(item.getSelectItem(), item.getOperator(), newOperand);
return newFilter;
} else {
return item;
}
}
}
private List<QueryParameter> createParameterList() {
final List<QueryParameter> parameters = new ArrayList<QueryParameter>();
buildParameterListInFromClause(parameters, _query);
buildParameterListInWhereClause(parameters, _query);
return parameters;
}
private void buildParameterListInWhereClause(List<QueryParameter> parameters, Query query) {
List<FilterItem> items = query.getWhereClause().getItems();
for (FilterItem item : items) {
buildParameterFromFilterItem(parameters, item);
}
}
private void buildParameterListInFromClause(List<QueryParameter> parameters, Query query) {
List<FromItem> fromItems = query.getFromClause().getItems();
for (FromItem fromItem : fromItems) {
Query subQuery = fromItem.getSubQuery();
if (subQuery != null) {
buildParameterListInFromClause(parameters, subQuery);
buildParameterListInWhereClause(parameters, subQuery);
}
}
}
@Override
public List<QueryParameter> getParameters() {
return _parameters;
}
@Override
public String toSql() {
return _query.toSql();
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + toSql() + "]";
}
@Override
public void close() {
// do nothing
}
private void buildParameterFromFilterItem(List<QueryParameter> parameters, FilterItem item) {
if (item.isCompoundFilter()) {
FilterItem[] childItems = item.getChildItems();
for (FilterItem childItem : childItems) {
buildParameterFromFilterItem(parameters, childItem);
}
} else {
if (item.getOperand() instanceof QueryParameter) {
parameters.add((QueryParameter) item.getOperand());
}
}
}
}