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 }