/*
* 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.fromClause;
import java.util.*;
import mockit.emulation.hibernate3.ast.*;
public final class FromClause
{
final List<FromClassOrOuterQueryPath> ranges;
private FromClause(List<FromClassOrOuterQueryPath> ranges)
{
this.ranges = ranges;
}
public static FromClause parse(Tokens tokens)
{
tokens.next("from");
List<FromClassOrOuterQueryPath> ranges = new LinkedList<FromClassOrOuterQueryPath>();
FromClassOrOuterQueryPath range = FromClassOrOuterQueryPath.parse(tokens);
ranges.add(range);
while (tokens.hasNext()) {
char nextToken = tokens.nextChar();
if (nextToken == ',') {
range = FromClassOrOuterQueryPath.parse(tokens);
ranges.add(range);
}
else if (!parseJoins(range, tokens)) break;
}
return new FromClause(ranges);
}
private static boolean parseJoins(FromClassOrOuterQueryPath range, Tokens tokens)
{
tokens.pushback();
FromJoin join = FromJoin.parse(tokens);
if (join == null) return false;
range.join = join;
while (tokens.hasNext() && join != null) {
join.nextJoin = FromJoin.parse(tokens);
join = join.nextJoin;
}
return true;
}
public List<Object[]> matches(Collection<?> entities)
{
buildAllResultListsFromKnownEntities(entities);
List<Object[]> tuples = createListOfTuples();
fillRangeResultValues(tuples, 0, 0, 0);
return tuples;
}
private int fillRangeResultValues(List<Object[]> tuples, int rangeIndex, int initialRow, int j)
{
FromClassOrOuterQueryPath range = ranges.get(rangeIndex);
int i = initialRow;
Object[] previousTuple = null;
for (Object value : range.result) {
Object[] tuple = tuples.get(i);
tuple[j] = value;
if (rangeIndex > 0) {
if (previousTuple != null) {
copyRepeatValues(previousTuple, tuple, j);
}
previousTuple = tuple;
}
FromJoin join = range.join;
if (join == null) {
i++;
}
else {
i = fillJoinResultValues(tuples, join, value, i, j);
}
if (rangeIndex + 1 < ranges.size()) {
i = fillRangeResultValues(tuples, rangeIndex + 1, i, j + 1);
}
}
return i;
}
private int fillJoinResultValues(
List<Object[]> tuples, FromJoin join, Object parentValue, int initialRow, int j)
{
int i = initialRow;
int jj = j + 1;
List<Object> joinedValues = join.result.get(parentValue);
Object[] previousTuple = null;
for (Object joinedValue : joinedValues) {
Object[] tuple = tuples.get(i);
tuple[jj] = joinedValue;
if (previousTuple != null) {
copyRepeatValues(previousTuple, tuple, j);
}
previousTuple = tuple;
if (join.nextJoin == null) {
i++;
}
else {
i = fillJoinResultValues(tuples, join.nextJoin, joinedValue, i, jj);
}
}
return i;
}
private void copyRepeatValues(Object[] previousTuple, Object[] tuple, int j)
{
for (int jj = 0; jj <= j; jj++) {
tuple[jj] = previousTuple[jj];
}
}
private void buildAllResultListsFromKnownEntities(Collection<?> entities)
{
for (FromClassOrOuterQueryPath range : ranges) {
range.matches(entities);
}
}
private List<Object[]> createListOfTuples()
{
int columns = 0;
int rows = 1;
for (FromClassOrOuterQueryPath range : ranges) {
columns += range.depth();
rows *= range.tupleCount();
}
return listOfEmptyTuples(rows, columns);
}
private List<Object[]> listOfEmptyTuples(int rows, int columns)
{
List<Object[]> tuples = new ArrayList<Object[]>(rows);
for (int i = 0; i < rows; i++) {
tuples.add(new Object[columns]);
}
return tuples;
}
public int columnIndex(String alias)
{
for (FromClassOrOuterQueryPath range : ranges) {
int index = range.columnIndex(alias);
if (index >= 0) {
return index;
}
}
throw new RuntimeException("Invalid alias \"" + alias + "\"");
}
public void getAliases(Map<String, Object> aliasToValue)
{
for (FromClassOrOuterQueryPath range : ranges) {
range.getAliases(aliasToValue);
}
}
}