/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.translator.excel;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.teiid.language.AndOr;
import org.teiid.language.ColumnReference;
import org.teiid.language.Comparison;
import org.teiid.language.Comparison.Operator;
import org.teiid.language.DerivedColumn;
import org.teiid.language.In;
import org.teiid.language.Limit;
import org.teiid.language.Literal;
import org.teiid.language.NamedTable;
import org.teiid.language.visitor.HierarchyVisitor;
import org.teiid.metadata.Column;
import org.teiid.metadata.KeyRecord;
import org.teiid.metadata.Table;
import org.teiid.translator.TranslatorException;
public class ExcelQueryVisitor extends HierarchyVisitor {
protected Stack<Object> onGoingExpression = new Stack<Object>();
private List<Integer> projectedColumns = new ArrayList<Integer>();
protected ArrayList<TranslatorException> exceptions = new ArrayList<TranslatorException>();
private Table table;
private String xlsPath;
private String sheetName;
private int firstDataRowNumber;
static interface Filter {
public boolean allows (int row);
}
static class InFilter implements Filter {
Integer[] values;
public InFilter(Integer[] allowed) {
this.values = allowed;
}
@Override
public boolean allows(int row) {
for (int i = 0; i < values.length; i++) {
if (values[i] == row) {
return true;
}
}
return false;
}
}
static class CompareFilter implements Filter {
int start;
Operator op;
public CompareFilter (int start, Operator op) {
this.start = start;
this.op = op;
}
public boolean allows (int row) {
switch(op) {
case EQ:
return row == start;
case NE:
return row != start;
case LT:
return row < start;
case LE:
return row <= start;
case GT:
return row > start;
case GE:
return row >= start;
}
return false;
}
}
private ArrayList<ExcelQueryVisitor.Filter> filters = new ArrayList<ExcelQueryVisitor.Filter>();
public List<Integer> getProjectedColumns() {
return projectedColumns;
}
public int getFirstDataRowNumber() {
return firstDataRowNumber;
}
public ArrayList<TranslatorException> getExceptions() {
return exceptions;
}
public Table getTable() {
return table;
}
public String getXlsPath() {
return xlsPath;
}
public String getSheetName() {
return sheetName;
}
@Override
public void visit(ColumnReference obj) {
this.onGoingExpression.push(obj.getMetadataObject());
}
@Override
public void visit(DerivedColumn obj) {
visitNode(obj.getExpression());
Column column = (Column) this.onGoingExpression.pop();
String str = column.getProperty(ExcelMetadataProcessor.CELL_NUMBER, false);
if (str == null) {
this.exceptions.add(new TranslatorException(ExcelPlugin.Event.TEIID23007, ExcelPlugin.Util.gs(ExcelPlugin.Event.TEIID23007, column.getName())));
return;
}
if (str.equalsIgnoreCase(ExcelMetadataProcessor.ROW_ID)) {
this.projectedColumns.add(-1);
}
else {
this.projectedColumns.add(Integer.valueOf(str));
}
}
@Override
public void visit(NamedTable obj) {
this.table = obj.getMetadataObject();
this.xlsPath = this.table.getProperty(ExcelMetadataProcessor.FILE, false);
this.sheetName = this.table.getSourceName();
String firstRow = this.table.getProperty(ExcelMetadataProcessor.FIRST_DATA_ROW_NUMBER, false);
if (firstRow != null) {
// -1 make it zero based index
this.firstDataRowNumber = Integer.parseInt(firstRow)-1;
}
}
@Override
public void visit(AndOr obj) {
visitNode(obj.getLeftCondition());
visitNode(obj.getRightCondition());
}
@Override
public void visit(Comparison obj) {
visitNode(obj.getLeftExpression());
Column column = (Column)this.onGoingExpression.pop();
visitNode(obj.getRightExpression());
Integer rightExpr = (Integer)this.onGoingExpression.pop();
if (isPartOfPrimaryKey(column)) {
switch(obj.getOperator()) {
case EQ:
this.filters.add(new CompareFilter(rightExpr-1, Operator.EQ));
break;
case NE:
this.filters.add(new CompareFilter(rightExpr-1, Operator.NE));
break;
case LT:
this.filters.add(new CompareFilter(rightExpr-1, Operator.LT));
break;
case LE:
this.filters.add(new CompareFilter(rightExpr-1, Operator.LE));
break;
case GT:
this.filters.add(new CompareFilter(rightExpr-1, Operator.GT));
break;
case GE:
this.filters.add(new CompareFilter(rightExpr-1, Operator.GE));
break;
}
}
else {
this.exceptions.add(new TranslatorException(ExcelPlugin.Event.TEIID23008, ExcelPlugin.Util.gs(ExcelPlugin.Event.TEIID23008, column.getName())));
}
}
@Override
public void visit(In obj) {
visitNode(obj.getLeftExpression());
Column column = (Column)this.onGoingExpression.pop();
visitNodes(obj.getRightExpressions());
if (isPartOfPrimaryKey(column)) {
ArrayList<Integer> values = new ArrayList<Integer>();
// NOTE: we are popping in reverse order to IN stmt
for (int i = 0; i < obj.getRightExpressions().size(); i++) {
values.add((Integer)this.onGoingExpression.pop());
}
this.filters.add(new InFilter(values.toArray(new Integer[values.size()])));
}
else {
this.exceptions.add(new TranslatorException(ExcelPlugin.Event.TEIID23008, ExcelPlugin.Util.gs(ExcelPlugin.Event.TEIID23008, column.getName())));
}
}
@Override
public void visit(Literal obj) {
this.onGoingExpression.push(obj.getValue());
}
@Override
public void visit(Limit obj) {
int offset = obj.getRowOffset();
if (offset != 0) {
this.firstDataRowNumber = offset + this.firstDataRowNumber;
}
this.filters.add(new CompareFilter(this.firstDataRowNumber, Operator.GE));
this.filters.add(new CompareFilter(this.firstDataRowNumber+obj.getRowLimit(), Operator.LT));
}
public static boolean isPartOfPrimaryKey(Column column) {
KeyRecord pk = ((Table)column.getParent()).getPrimaryKey();
if (pk != null) {
for (Column col:pk.getColumns()) {
if (col.getName().equals(column.getName())) {
if (col.getProperty(ExcelMetadataProcessor.CELL_NUMBER, false).equalsIgnoreCase(ExcelMetadataProcessor.ROW_ID)) {
return true;
}
}
}
}
return false;
}
public boolean allows(int row) {
if (this.filters.isEmpty()) {
return true;
}
for (Filter f:this.filters) {
if (!f.allows(row)) {
return false;
}
}
return true;
}
}