001 /* 002 * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org 003 * Copyright (C) 2011 NightLabs Consulting GmbH 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 package org.cumulus4j.store.query.eval; 019 020 import java.util.Collection; 021 import java.util.Date; 022 import java.util.Map; 023 import java.util.Set; 024 025 import org.cumulus4j.store.query.QueryEvaluator; 026 import org.cumulus4j.store.query.method.MethodEvaluator; 027 import org.datanucleus.query.expression.InvokeExpression; 028 import org.datanucleus.query.expression.PrimaryExpression; 029 import org.datanucleus.query.expression.VariableExpression; 030 import org.datanucleus.query.symbol.Symbol; 031 032 /** 033 * Evaluator handling method invocations like <code>Collection.contains(...)</code>. 034 * 035 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 036 */ 037 public class InvokeExpressionEvaluator 038 extends AbstractExpressionEvaluator<InvokeExpression> 039 { 040 public InvokeExpressionEvaluator(QueryEvaluator queryEvaluator, AbstractExpressionEvaluator<?> parent, InvokeExpression expression) 041 { 042 super(queryEvaluator, parent, expression); 043 } 044 045 @Override 046 protected Set<Long> _queryResultDataEntryIDs(ResultDescriptor resultDescriptor) 047 { 048 // The invocationTarget is always the left side of the InvokeExpression. It can be one of the following three: 049 // 1) PrimaryExpression 050 // 2) VariableExpression 051 // 3) ParameterExpression 052 // 4) InvokeExpression 053 // 5) null (for static methods or aggregates) 054 055 if (this.getLeft() instanceof PrimaryExpressionEvaluator) { 056 if (!getLeft().getResultSymbols().contains(resultDescriptor.getSymbol())) 057 return null; 058 059 // Evaluate the left-hand expression on which we perform the method invocation 060 PrimaryExpressionEvaluator primaryEval = (PrimaryExpressionEvaluator) this.getLeft(); 061 PrimaryExpression primaryExpr = primaryEval.getExpression(); 062 Class<?> invocationTargetType = getFieldType(primaryExpr); 063 064 // Find the evaluator to use and invoke it 065 MethodEvaluator eval = getMethodEvaluatorForMethodOfClass(invocationTargetType, getExpression().getOperation()); 066 synchronized (eval) { 067 if (eval.requiresComparisonArgument()) { 068 eval.setCompareToArgument(getCompareToArgument()); 069 } 070 return eval.evaluate(getQueryEvaluator(), this, primaryExpr, resultDescriptor); 071 } 072 } 073 else if (this.getLeft() instanceof VariableExpressionEvaluator) { 074 VariableExpressionEvaluator variableExprEval = (VariableExpressionEvaluator) this.getLeft(); 075 VariableExpression variableExpr = variableExprEval.getExpression(); 076 Symbol classSymbol = variableExprEval.getExpression().getSymbol(); 077 if (classSymbol == null) 078 throw new IllegalStateException("((VariableExpressionEvaluator)this.getLeft()).getExpression().getSymbol() returned null!"); 079 080 // Evaluate the left-hand expression on which we perform the method invocation 081 Class<?> invocationTargetType = getQueryEvaluator().getValueType(classSymbol); 082 083 // Find the evaluator to use and invoke it 084 MethodEvaluator eval = getMethodEvaluatorForMethodOfClass(invocationTargetType, getExpression().getOperation()); 085 synchronized (eval) { 086 if (eval.requiresComparisonArgument()) { 087 eval.setCompareToArgument(getCompareToArgument()); 088 } 089 return eval.evaluate(getQueryEvaluator(), this, variableExpr, resultDescriptor); 090 } 091 } 092 else if (this.getLeft() instanceof ParameterExpressionEvaluator) { 093 ParameterExpressionEvaluator paramExprEval = (ParameterExpressionEvaluator) this.getLeft(); 094 Symbol classSymbol = paramExprEval.getExpression().getSymbol(); 095 if (classSymbol == null) 096 throw new IllegalStateException("((ParameterExpressionEvaluator)this.getLeft()).getExpression().getSymbol() returned null!"); 097 098 // Evaluate the left-hand expression on which we perform the method invocation 099 Class<?> invocationTargetType = getQueryEvaluator().getValueType(classSymbol); 100 101 // Find the evaluator to use and invoke it 102 MethodEvaluator eval = getMethodEvaluatorForMethodOfClass(invocationTargetType, getExpression().getOperation()); 103 synchronized (eval) { 104 if (eval.requiresComparisonArgument()) { 105 eval.setCompareToArgument(getCompareToArgument()); 106 } 107 return eval.evaluate(getQueryEvaluator(), this, paramExprEval.getExpression(), resultDescriptor); 108 } 109 } 110 else if (this.getLeft() instanceof InvokeExpressionEvaluator) { 111 // TODO support this.getLeft() instanceof InvokeExpressionEvaluator 112 throw new UnsupportedOperationException("NYI: this.getLeft() instanceof InvokeExpressionEvaluator"); 113 } 114 else if (this.getLeft() instanceof SubqueryExpressionEvaluator) { 115 // TODO support this.getLeft() instanceof SubqueryExpressionEvaluator 116 throw new UnsupportedOperationException("NYI: this.getLeft() instanceof SubqueryExpressionEvaluator"); 117 } 118 else if (this.getLeft() == null) { 119 // TODO support this.getLeft() == null (static method call) 120 throw new UnsupportedOperationException("NYI: this.getLeft() == null"); 121 } 122 123 throw new UnsupportedOperationException("NYI"); 124 } 125 126 private MethodEvaluator getMethodEvaluatorForMethodOfClass(Class cls, String method) { 127 String className = cls.getName(); 128 if (Collection.class.isAssignableFrom(cls)) { 129 className = Collection.class.getName(); // Sub-types go through Collection 130 } 131 else if (Map.class.isAssignableFrom(cls)) { 132 className = Map.class.getName(); // Sub-types go through Map 133 } 134 else if (Date.class.isAssignableFrom(cls)) { 135 className = Date.class.getName(); // Sub-types go through Date 136 } 137 return ExpressionHelper.getMethodEvaluatorForMethodOfClass(getQueryEvaluator().getStoreManager(), 138 getQueryEvaluator().getClassLoaderResolver(), className, method); 139 } 140 141 private Object getCompareToArgument() { 142 if (!(getParent() instanceof ComparisonExpressionEvaluator)) 143 throw new UnsupportedOperationException(this.getExpression().toString() + " needs to be compared to something as it does not have a boolean result! this.getParent() is thus expected to be a ComparisonExpressionEvaluator, but is: " + getParent()); 144 145 ComparisonExpressionEvaluator comparisonExpressionEvaluator = (ComparisonExpressionEvaluator) getParent(); 146 147 Object compareToArgument; 148 if (this == comparisonExpressionEvaluator.getLeft()) 149 compareToArgument = comparisonExpressionEvaluator.getRightCompareToArgument(); 150 else if (this == comparisonExpressionEvaluator.getRight()) 151 compareToArgument = comparisonExpressionEvaluator.getLeftCompareToArgument(); 152 else 153 throw new UnsupportedOperationException("this is neither parent.left nor parent.right!"); 154 155 return compareToArgument; 156 } 157 }