/* * 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.mongodb; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.List; import java.util.Stack; import org.teiid.language.*; import org.teiid.language.visitor.HierarchyVisitor; import org.teiid.translator.TranslatorException; import org.teiid.translator.mongodb.MongoDBUpdateExecution.RowInfo; import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBObject; import com.mongodb.DBRef; public class ExpressionEvaluator extends HierarchyVisitor { private BasicDBObject row; private RowInfo rowInfo; private MongoDBExecutionFactory executionFactory; private DB mongoDB; protected Stack<Boolean> match = new Stack<Boolean>(); protected ArrayList<TranslatorException> exceptions = new ArrayList<TranslatorException>(); public static boolean matches(MongoDBExecutionFactory executionFactory, DB mongoDB, Condition condition, BasicDBObject row, RowInfo rowInfo) throws TranslatorException { ExpressionEvaluator evaluator = new ExpressionEvaluator(executionFactory, mongoDB, row, rowInfo); evaluator.append(condition); if (!evaluator.exceptions.isEmpty()) { throw evaluator.exceptions.get(0); } try { return evaluator.match.pop(); } catch (EmptyStackException e) { return true; } } private ExpressionEvaluator(MongoDBExecutionFactory executionFactory, DB mongoDB, BasicDBObject row, RowInfo rowInfo) { this.executionFactory = executionFactory; this.mongoDB = mongoDB; this.row = row; this.rowInfo = rowInfo; } @Override public void visit(Comparison obj) { try { Object o1 = getRowValue(obj.getLeftExpression()); Object o2 = getLiteralValue(obj.getRightExpression()); int compare = ((Comparable<Object>)o1).compareTo(o2); switch(obj.getOperator()) { case EQ: this.match.push(Boolean.valueOf(compare == 0)); break; case NE: this.match.push(Boolean.valueOf(compare != 0)); break; case LT: this.match.push(Boolean.valueOf(compare < 0)); break; case LE: this.match.push(Boolean.valueOf(compare <= 0)); break; case GT: this.match.push(Boolean.valueOf(compare > 0)); break; case GE: this.match.push(Boolean.valueOf(compare >= 0)); break; } } catch (TranslatorException e) { this.exceptions.add(e); } } private Object getRowValue(Expression obj) throws TranslatorException { if (!(obj instanceof ColumnReference)) { throw new TranslatorException(MongoDBPlugin.Util.gs(MongoDBPlugin.Event.TEIID18017)); } ColumnReference column = (ColumnReference)obj; Object value = null; if (MongoDBSelectVisitor.isPartOfPrimaryKey(column.getTable().getMetadataObject(), column.getName())) { // this is true one to many case value = this.row.get("_id"); //$NON-NLS-1$ if (value == null) { value = getValueFromRowInfo(column, value); } } if (value == null && MongoDBSelectVisitor.isPartOfForeignKey(column.getTable().getMetadataObject(), column.getName())) { value = getValueFromRowInfo(column, value); } if (value == null) { value = this.row.get(column.getName()); } if (value instanceof DBRef) { value = ((DBRef)value).getId(); } if (value instanceof DBObject) { value = ((DBObject) value).get(column.getName()); } return this.executionFactory.retrieveValue(value, column.getType(), this.mongoDB, column.getName(), column.getName()); } private Object getValueFromRowInfo(ColumnReference column, Object value) { String tableName = column.getTable().getMetadataObject().getName(); if (this.rowInfo.tableName.equals(tableName) || this.rowInfo.mergedTableName.equals(tableName)) { // one to one case value = this.rowInfo.PK; } else { // one to many case RowInfo info = this.rowInfo; while(info.parent != null) { info = info.parent; if (info.tableName.equals(tableName) || info.mergedTableName.equals(tableName)) { value = info.PK; break; } } } return value; } private Object getLiteralValue(Expression obj) throws TranslatorException { if (!(obj instanceof Literal)) { throw new TranslatorException(MongoDBPlugin.Util.gs(MongoDBPlugin.Event.TEIID18018)); } Literal right = (Literal)obj; return right.getValue(); } @Override public void visit(AndOr obj) { append(obj.getLeftCondition()); append(obj.getRightCondition()); if (!this.exceptions.isEmpty()) { return; } boolean right = this.match.pop(); boolean left = this.match.pop(); switch(obj.getOperator()) { case AND: this.match.push(right && left); break; case OR: this.match.push(right || left); break; } } @Override public void visit(In obj) { try { Object o1 = getRowValue(obj.getLeftExpression()); ArrayList<Object> values = new ArrayList<Object>(); for (int i = 0; i < obj.getRightExpressions().size(); i++) { values.add(getLiteralValue(obj.getRightExpressions().get(i))); } if (obj.isNegated()) { this.match.push(!values.contains(o1)); } else { this.match.push(values.contains(o1)); } } catch (TranslatorException e) { this.exceptions.add(e); } } @Override public void visit(IsNull obj) { try { Object o1 = getRowValue(obj.getExpression()); if (obj.isNegated()) { this.match.push(o1 != null); } else { this.match.push(o1 == null); } } catch (TranslatorException e) { this.exceptions.add(e); } } @Override public void visit(Like obj) { try { Object o1 = getRowValue(obj.getLeftExpression()); Object o2 = getLiteralValue(obj.getRightExpression()); if (o1 instanceof String && o2 instanceof String) { String value = (String)o1; if (obj.isNegated()) { this.match.push(!value.matches((String)o2)); } else { this.match.push(value.matches((String)o2)); } } else { this.exceptions.add(new TranslatorException(MongoDBPlugin.Util.gs(MongoDBPlugin.Event.TEIID18019))); } } catch (TranslatorException e) { this.exceptions.add(e); } } /** * Appends the string form of the LanguageObject to the current buffer. * @param obj the language object instance */ public void append(LanguageObject obj) { if (obj != null) { visitNode(obj); } } /** * Simple utility to append a list of language objects to the current buffer * by creating a comma-separated list. * @param items a list of LanguageObjects */ protected void append(List<? extends LanguageObject> items) { if (items != null && items.size() != 0) { append(items.get(0)); for (int i = 1; i < items.size(); i++) { append(items.get(i)); } } } /** * Simple utility to append an array of language objects to the current buffer * by creating a comma-separated list. * @param items an array of LanguageObjects */ protected void append(LanguageObject[] items) { if (items != null && items.length != 0) { append(items[0]); for (int i = 1; i < items.length; i++) { append(items[i]); } } } }