package org.raidenjpa.query.executor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.raidenjpa.query.parser.GroupByClause;
import org.raidenjpa.query.parser.GroupByElements;
import org.raidenjpa.query.parser.JoinClause;
import org.raidenjpa.query.parser.OrderByClause;
import org.raidenjpa.query.parser.OrderByElement;
import org.raidenjpa.query.parser.SelectClause;
import org.raidenjpa.query.parser.SelectElement;
import org.raidenjpa.query.parser.WhereClause;
import org.raidenjpa.reflection.ReflectionUtil;
import org.raidenjpa.util.BadSmell;
import org.raidenjpa.util.FixMe;
import org.raidenjpa.util.ListUtil;
public class QueryResult implements Iterable<QueryResultRow> {
private List<QueryResultRow> rows = new ArrayList<QueryResultRow>();
public QueryResult addFrom(String alias, List<?> newElements) {
if (rows.isEmpty()) {
firstFrom(alias, newElements);
} else {
cartesianProduct(alias, newElements);
}
return this;
}
public void cartesianProduct(String alias, List<?> newElements) {
if (newElements.isEmpty()) {
return;
}
for (QueryResultRow row : new ArrayList<QueryResultRow>(rows)) {
row.add(alias, newElements.get(0));
for (int i = 1; i < newElements.size(); i++) {
QueryResultRow duplicatedRow = duplicate(row);
duplicatedRow.add(alias, newElements.get(i));
row = duplicatedRow;
}
}
}
private QueryResultRow duplicate(QueryResultRow row) {
int index = rows.indexOf(row);
QueryResultRow duplicatedRow = row.copy();
rows.add(index + 1, duplicatedRow);
return duplicatedRow;
}
private void firstFrom(String alias, List<?> objRows) {
for (Object obj : objRows) {
rows.add(new QueryResultRow(alias, obj));
}
}
public Iterator<QueryResultRow> iterator() {
return rows.iterator();
}
public void limit(int first, Integer maxResult) {
int size = rows.size();
int last = maxResult == null ? size : Math.min(maxResult + first, size);
if (first == 0 && last == size) {
return;
}
if (first >= last) {
rows = Collections.emptyList();
return;
}
rows = rows.subList(first, last);
}
@BadSmell("This double verification in groupBy is only necessary because of bad design")
public List<?> getList(SelectClause select, GroupByClause groupBy) {
List<Object[]> list = select(select, groupBy);
return ListUtil.simplifyListTypeIfPossible(list);
}
private List<Object[]> select(SelectClause select, GroupByClause groupBy) {
List<Object[]> result = new ArrayList<Object[]>();
for (QueryResultRow row : rows) {
Object[] resultRow = new Object[select.getElements().size()];
for (int i = 0; i < select.getElements().size(); i++) {
SelectElement selectElement = select.getElements().get(i);
if (selectElement.isCount()) {
resultRow[i] = new Long(row.getGroupedRows().size());
} else if (selectElement.isMax()) {
resultRow[i] = MaxUtil.max(row.getGroupedRows(), selectElement.getPath());
} else {
resultRow[i] = row.get(selectElement.getPath());
}
}
result.add(resultRow);
}
applyDistinct(select, result);
return result;
}
@BadSmell("Primitive obsession")
public Map<String, List<QueryResultRow>> aggregateRowsOld(GroupByClause groupBy) {
Map<String, List<QueryResultRow>> map = new HashMap<String, List<QueryResultRow>>();
for (QueryResultRow row : rows) {
String key = "";
for (GroupByElements element : groupBy.getElements()) {
key += ";" + element.getPath() + "=" + row.getObject(element.getPath());
}
List<QueryResultRow> aggregatedRows = map.get(key);
if (aggregatedRows == null) {
aggregatedRows = new ArrayList<QueryResultRow>();
}
aggregatedRows.add(row);
map.put(key, aggregatedRows);
}
return map;
}
private void applyDistinct(SelectClause select, List<Object[]> result) {
if (select.isDistinct()) {
Set<String> distinct = new HashSet<String>();
for (Object[] row : new ArrayList<Object[]>(result)) {
String idf = "";
for (Object column : row) {
idf += column + "-";
}
if (!distinct.add(idf)) {
result.remove(row);
}
}
}
}
public int size() {
return rows.size();
}
@BadSmell("It is weird to recive where")
void join(JoinClause join, WhereClause where, Map<String, Object> parameters) {
String leftAlias = join.getPath().get(0);
String attribute = join.getPath().get(1);
for (QueryResultRow row : new ArrayList<QueryResultRow>(rows)) {
Object leftObject = row.get(leftAlias);
Object obj = ReflectionUtil.getBeanField(leftObject, attribute);
if (obj instanceof Collection) {
joinCollection(join, where, row, (Collection<?>) obj, parameters);
} else {
joinObject(join, row, obj, parameters);
}
}
}
@FixMe("Receive where, like joinCollection")
private void joinObject(JoinClause join, QueryResultRow row, Object obj, Map<String, Object> parameters) {
if (obj == null) {
rows.remove(row); // TODO: Beware about LEFT
} else {
row.add(join.getAlias(), obj);
removeRowsNoMatchWith(join, parameters, Arrays.asList(row));
}
}
@BadSmell("We could avoid some parameters making this attributes")
private void joinCollection(JoinClause join, WhereClause where, QueryResultRow row, Collection<?> itensToAdd, Map<String, Object> parameters) {
Iterator<?> it = itensToAdd.iterator();
if (!it.hasNext()) {
rows.remove(row); // TODO: Beware about LEFT
return;
}
List<QueryResultRow> rowsInJoin = new ArrayList<QueryResultRow>();
rowsInJoin.add(row);
row.add(join.getAlias(), it.next());
while(it.hasNext()) {
Object itemToAdd = it.next();
QueryResultRow newRow = duplicate(row);
newRow.add(join.getAlias(), itemToAdd);
rowsInJoin.add(newRow);
row = newRow;
}
// @FixMe(We should not add)
removeRowsNoMatchWith(join, parameters, rowsInJoin);
removeRowsNoMatchWhere(where, parameters, rowsInJoin);
}
@BadSmell("Duplicate removeRowsNoMatchWith")
private void removeRowsNoMatchWhere(WhereClause where, Map<String, Object> parameters, List<QueryResultRow> rowsInJoin) {
if (where == null || where.getLogicExpression() == null) {
return;
}
LogicExpressionExecutor executor = new LogicExpressionExecutor(where.getLogicExpression(), parameters);
for (QueryResultRow rowInJoin : rowsInJoin) {
if (executor.match(rowInJoin, false)) {
} else {
rows.remove(rowInJoin);
}
}
}
private void removeRowsNoMatchWith(JoinClause join, Map<String, Object> parameters, List<QueryResultRow> rowsInJoin) {
if (join.getWith().getLogicExpression() == null) {
return;
}
LogicExpressionExecutor executor = new LogicExpressionExecutor(join.getWith().getLogicExpression(), parameters);
for (QueryResultRow rowInJoin : rowsInJoin) {
if (executor.match(rowInJoin, false)) {
} else {
rows.remove(rowInJoin);
}
}
}
public void sort(final OrderByClause orderBy) {
Collections.sort(rows, new Comparator<QueryResultRow>() {
public int compare(QueryResultRow row1, QueryResultRow row2) {
for (OrderByElement orderByElement : orderBy.getElements()) {
Object value1;
Object value2;
if (orderByElement.isMax()) {
value1 = MaxUtil.max(row1.getGroupedRows(), orderByElement.getPath());
value2 = MaxUtil.max(row2.getGroupedRows(), orderByElement.getPath());
} else {
value1 = row1.getObject(orderByElement.getPath());
value2 = row2.getObject(orderByElement.getPath());
}
int comp = ComparatorUtil.compare(value1, value2, orderByElement.getOrientation());
if (comp == 0) {
continue;
} else {
return comp;
}
}
return 0;
}
});
}
@BadSmell("Primitive obsession in paths parameter")
public void group(List<List<String>> paths) {
Collection<QueryResultRow> groupedRows = new PoolerRows().group(rows, paths);
rows = new ArrayList<QueryResultRow>(groupedRows);
}
}