/* * Copyright (c) 2006-2011 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.emulation.hibernate3.ast; import java.util.*; import mockit.emulation.hibernate3.ast.fromClause.*; import mockit.emulation.hibernate3.ast.whereClause.*; /** * Abstract Syntax Tree and "Interpreter" (from the GoF Design Patterns) for HQL query statements. */ public final class QueryAST { private final Map<Object, Object> parameters; private final SelectClause select; private final FromClause from; private final Expr where; public QueryAST(String ql) { parameters = new HashMap<Object, Object>(); Tokens tokens = new Tokens(ql); select = SelectClause.parse(tokens); from = FromClause.parse(tokens); if (tokens.hasNext() && "where".equalsIgnoreCase(tokens.next())) { where = Expr.parse(tokens); } else { where = null; } } public void setParameter(int position, Object parameter) { parameters.put(position, parameter); } public void setParameter(String name, Object parameter) { parameters.put(name, parameter); } public List<?> matches(Collection<?> entities) { List<Object[]> tuples = from.matches(entities); if (where != null) { filterTuplesAccordingToWhereClause(tuples); } return getResult(tuples); } private void filterTuplesAccordingToWhereClause(List<Object[]> tuples) { Map<String, Object> aliasToValue = new LinkedHashMap<String, Object>(); from.getAliases(aliasToValue); for (Iterator<Object[]> itr = tuples.iterator(); itr.hasNext(); ) { Object[] tuple = itr.next(); setValuesInAliasToValueMap(aliasToValue, tuple); Boolean satisfiesWhereCondition = (Boolean) where.evaluate(new QueryEval(parameters, aliasToValue)); if (!satisfiesWhereCondition) { itr.remove(); } } } private void setValuesInAliasToValueMap(Map<String, Object> aliasToValue, Object[] tuple) { int j = 0; for (Map.Entry<String, Object> aliasAndValue : aliasToValue.entrySet()) { aliasAndValue.setValue(tuple[j]); j++; } } private List<?> getResult(List<Object[]> tuples) { List<Object> result = new ArrayList<Object>(); if (tuples.isEmpty()) { return result; } // TODO: handle distinct if (select != null) { extractSelectedPropertiesFromTuples(tuples, result); } else if (tuples.get(0).length > 1) { return tuples; } else { addSingleElementsToResult(tuples, result, 0); } return result; } private void extractSelectedPropertiesFromTuples(List<Object[]> tuples, List<Object> result) { // TODO: handle new List<PathAndAlias> selectedProperties = select.selectedProperties; int[] columns = columnIndexesForSelectedProperties(selectedProperties); if (columns.length == 1) { addSingleElementsToResult(tuples, result, columns[0]); } else { for (Object[] tuple : tuples) { Object[] selectedTuple = selectedTupleFromFullTuple(selectedProperties, columns, tuple); result.add(selectedTuple); } } } private Object[] selectedTupleFromFullTuple(List<PathAndAlias> selectedProperties, int[] columns, Object[] tuple) { Object[] selectedTuple = new Object[columns.length]; int j = 0; for (PathAndAlias selectedProperty : selectedProperties) { Object value = tuple[columns[j]]; selectedTuple[j] = selectedProperty.evaluate(value); j++; } return selectedTuple; } private int[] columnIndexesForSelectedProperties(List<PathAndAlias> selectedProperties) { int[] columns = new int[selectedProperties.size()]; int j = 0; for (PathAndAlias selectedProperty : selectedProperties) { columns[j] = from.columnIndex(selectedProperty.pathElements[0]); j++; } return columns; } private void addSingleElementsToResult(List<Object[]> tuples, List<Object> result, int index) { for (Object[] tuple : tuples) { result.add(tuple[index]); } } }