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    }