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.ArrayList;
021    import java.util.Collections;
022    import java.util.HashMap;
023    import java.util.HashSet;
024    import java.util.LinkedList;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    import javax.jdo.identity.LongIdentity;
030    
031    import org.cumulus4j.store.model.DataEntry;
032    import org.cumulus4j.store.query.QueryEvaluator;
033    import org.datanucleus.metadata.AbstractClassMetaData;
034    import org.datanucleus.metadata.AbstractMemberMetaData;
035    import org.datanucleus.query.expression.Expression;
036    import org.datanucleus.query.expression.PrimaryExpression;
037    import org.datanucleus.query.expression.VariableExpression;
038    import org.datanucleus.query.symbol.Symbol;
039    
040    /**
041     * <p>
042     * Abstract base class for all {@link Expression} evaluators.
043     * </p>
044     * <p>
045     * DataNucleus gives the query implementation a tree composed of {@link Expression}s. This tree is nothing more
046     * than an object-oriented representation of the query to be executed. In order to actually query data, there
047     * needs to be evaluation logic applying the <code>Expression</code> to the Cumulus4j data structure. This logic is
048     * implemented in subclasses of <code>AbstractExpressionEvaluator</code>.
049     * </p>
050     *
051     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
052     *
053     * @param <X> the {@link Expression} to be evaluated.
054     */
055    public abstract class AbstractExpressionEvaluator<X extends Expression>
056    {
057            private static final boolean CHECK_RESULT_DATA_ENTRY_IDS = false;
058    
059            private QueryEvaluator queryEvaluator;
060    
061            private AbstractExpressionEvaluator<?> parent;
062    
063            private X expression;
064    
065            private Map<ResultDescriptor, Set<Long>> resultDescriptor2resultDataEntryIDs = new HashMap<ResultDescriptor, Set<Long>>();
066    
067            private Map<ResultDescriptor, List<Object>> resultDescriptor2resultObjects = new HashMap<ResultDescriptor, List<Object>>();
068    
069            /**
070             * Create an <code>AbstractExpressionEvaluator</code> instance.
071             * @param queryEvaluator the evaluator responsible for evaluating the entire query. Must not be <code>null</code>.
072             * @param parent the parent-node in the tree. The <code>AbstractExpressionEvaluator</code>s form a tree analogue to the
073             * tree provided by DataNucleus. This <code>parent</code> is <code>null</code>, if it is the root of the tree.
074             * @param expression the expression that is to be evaluated by this <code>AbstractExpressionEvaluator</code> instance.
075             */
076            public AbstractExpressionEvaluator(QueryEvaluator queryEvaluator, AbstractExpressionEvaluator<?> parent, X expression)
077            {
078                    if (queryEvaluator == null)
079                            throw new IllegalArgumentException("queryEvaluator == null");
080    
081                    if (expression == null)
082                            throw new IllegalArgumentException("expression == null");
083    
084                    this.queryEvaluator = queryEvaluator;
085    
086                    this.parent = parent;
087                    this.expression = expression;
088            }
089    
090            /**
091             * Get the evaluator responsible for evaluating the entire query.
092             * @return the evaluator responsible for evaluating the entire query.
093             */
094            public QueryEvaluator getQueryEvaluator() {
095                    return queryEvaluator;
096            }
097    
098            /**
099             * Get the parent-node in the tree. The <code>AbstractExpressionEvaluator</code>s form a tree analogue to the
100             * tree provided by DataNucleus. This <code>parent</code> is <code>null</code>, if it is the root of the tree.
101             * @return the parent-node in the tree or <code>null</code>, if this is the root.
102             */
103            public AbstractExpressionEvaluator<?> getParent() {
104                    return parent;
105            }
106    
107            /**
108             * Get the expression that is to be evaluated by this <code>AbstractExpressionEvaluator</code>.
109             * @return the expression that is to be evaluated by this <code>AbstractExpressionEvaluator</code>.
110             */
111            public X getExpression() {
112                    return expression;
113            }
114    
115            private AbstractExpressionEvaluator<? extends Expression> left;
116    
117            private AbstractExpressionEvaluator<? extends Expression> right;
118    
119            /**
120             * Get the left branch in the tree structure.
121             * @return the left branch in the tree structure or <code>null</code> if there is none.
122             * @see #setLeft(AbstractExpressionEvaluator)
123             * @see #getRight()
124             */
125            public AbstractExpressionEvaluator<? extends Expression> getLeft() {
126                    return left;
127            }
128            /**
129             * Set the left branch in the tree structure.
130             * @param left the left branch in the tree structure or <code>null</code> if there is none.
131             * @see #getLeft()
132             */
133            public void setLeft(AbstractExpressionEvaluator<? extends Expression> left)
134            {
135                    if (left != null && !this.equals(left.getParent()))
136                            throw new IllegalArgumentException("this != left.parent");
137    
138                    this.left = left;
139            }
140    
141            /**
142             * Get the right branch in the tree structure.
143             * @return the right branch in the tree structure or <code>null</code> if there is none.
144             * @see #setRight(AbstractExpressionEvaluator)
145             * @see #getLeft()
146             */
147            public AbstractExpressionEvaluator<? extends Expression> getRight() {
148                    return right;
149            }
150            /**
151             * Set the right branch in the tree structure.
152             * @param right the right branch in the tree structure or <code>null</code> if there is none.
153             * @see #getRight()
154             */
155            public void setRight(AbstractExpressionEvaluator<? extends Expression> right)
156            {
157                    if (right != null && !this.equals(right.getParent()))
158                            throw new IllegalArgumentException("this != right.parent");
159    
160                    this.right = right;
161            }
162    
163            private Set<Symbol> resultSymbols;
164    
165            /**
166             * <p>
167             * Get the {@link Symbol}s for which {@link #queryResultDataEntryIDs(ResultDescriptor)} (and thus
168             * {@link #queryResultObjects(ResultDescriptor)}) can return a result. For all other {@link Symbol}s,
169             * said methods return <code>null</code>.
170             * </p>
171             * <p>
172             * The implementation in {@link AbstractExpressionEvaluator} delegates to
173             * {@link #_getResultSymbols()} and caches the result.
174             * </p>
175             * <p>
176             * Do <b>not</b> override this method, if you're not absolutely sure you want to
177             * deactivate/override the caching! In most cases, you should override
178             * {@link #_getResultSymbols()} instead.
179             * </p>
180             *
181             * @return the queryable {@link Symbol}s; never <code>null</code>.
182             * @see #_getResultSymbols()
183             */
184            public final Set<Symbol> getResultSymbols()
185            {
186                    if (resultSymbols == null) {
187                            Set<Symbol> s = _getResultSymbols();
188                            if (s == null)
189                                    s = Collections.emptySet();
190    
191                            resultSymbols = s;
192                    }
193    
194                    return resultSymbols;
195            }
196    
197            /**
198             * <p>
199             * Get the {@link Symbol}s for which {@link #queryResultDataEntryIDs(ResultDescriptor)} (and thus
200             * {@link #queryResultObjects(ResultDescriptor)}) can return a result. For all other {@link Symbol}s,
201             * said methods return <code>null</code>.
202             * </p>
203             * <p>
204             * The default implementation in {@link AbstractExpressionEvaluator} collects the result-symbols
205             * from its {@link #getLeft() left} and its {@link #getRight() right} side and returns this combined
206             * <code>Set</code>.
207             * </p>
208             * <p>
209             * This is the actual implementation of {@link #getResultSymbols()} and should be overridden
210             * instead of the non-"_"-prefixed version, in most cases.
211             * </p>
212             *
213             * @return the queryable {@link Symbol}s or <code>null</code> (<code>null</code> is equivalent to an
214             * empty <code>Set</code>).
215             * @see #getResultSymbols()
216             */
217            protected Set<Symbol> _getResultSymbols()
218            {
219                    Set<Symbol> resultSymbols;
220                    if (left != null && right == null)
221                            resultSymbols = left.getResultSymbols();
222                    else if (left == null && right != null)
223                            resultSymbols = right.getResultSymbols();
224                    else if (left == null && right == null)
225                            resultSymbols = Collections.emptySet();
226                    else {
227                            Set<Symbol> result = new HashSet<Symbol>(left.getResultSymbols().size() + right.getResultSymbols().size());
228                            result.addAll(left.getResultSymbols());
229                            result.addAll(right.getResultSymbols());
230                            resultSymbols = Collections.unmodifiableSet(result);
231                    }
232                    return resultSymbols;
233            }
234    
235            /**
236             * <p>
237             * Get those {@link DataEntry#getDataEntryID() dataEntryID}s that match the query
238             * criteria for the specified <code>resultDescriptor</code> or <code>null</code>,
239             * if the given {@link ResultDescriptor#getSymbol() symbol} is not queryable by the
240             * evaluator implementation.
241             * </p>
242             * <p>
243             * This method delegates to {@link #_queryResultDataEntryIDs(ResultDescriptor)} and caches the
244             * result. Thus a second call to this method with the same symbol does not trigger a
245             * second query but instead immediately returns the cached result.
246             * </p>
247             * <p>
248             * If the subclass of {@link AbstractExpressionEvaluator} does not support querying on its
249             * own (e.g. querying a {@link LiteralEvaluator literal} makes no sense at all), this method
250             * throws an {@link UnsupportedOperationException}. The same exception is thrown, if the requested
251             * query functionality is not yet implemented.
252             * </p>
253             *
254             * @param resultDescriptor the descriptor specifying what candidates (usually "this" or a variable) the
255             * caller is interested in as well as modifiers (e.g. {@link ResultDescriptor#isNegated() negation})
256             * affecting the query.
257             * @return those {@link DataEntry#getDataEntryID() dataEntryID}s that match the query
258             * criteria for the specified <code>resultSymbol</code> or <code>null</code>, if the symbol is not
259             * supported (this should be consistent with the implementation of {@link #_getResultSymbols()}).
260             * @throws UnsupportedOperationException if the implementation does not support querying at all
261             * (e.g. because it makes no sense without more context) or if the concrete query situation is not
262             * yet supported.
263             * @see #_queryResultDataEntryIDs(ResultDescriptor)
264             */
265            public final Set<Long> queryResultDataEntryIDs(ResultDescriptor resultDescriptor)
266            throws UnsupportedOperationException
267            {
268                    getQueryEvaluator().pushResultDescriptor(resultDescriptor);
269                    try {
270                            Set<Long> resultDataEntryIDs = resultDescriptor2resultDataEntryIDs.get(resultDescriptor);
271                            if (!resultDescriptor2resultDataEntryIDs.containsKey(resultDescriptor)) {
272                                    resultDataEntryIDs = _queryResultDataEntryIDs(resultDescriptor);
273    
274                                    if (CHECK_RESULT_DATA_ENTRY_IDS)
275                                            checkResultDataEntryIDs(resultDataEntryIDs);
276    
277                                    if (resultDataEntryIDs != null)
278                                            resultDataEntryIDs = Collections.unmodifiableSet(resultDataEntryIDs);
279    
280                                    resultDescriptor2resultDataEntryIDs.put(resultDescriptor, resultDataEntryIDs);
281                            }
282    
283                            return resultDataEntryIDs;
284                    } finally {
285                            ResultDescriptor popResultDescriptor = getQueryEvaluator().popResultDescriptor();
286                            if (resultDescriptor != popResultDescriptor)
287                                    throw new IllegalStateException("resultDescriptor != popResultDescriptor");
288                    }
289            }
290    
291            private void checkResultDataEntryIDs(Set<?> dataEntryIDs) {
292                    if (dataEntryIDs == null)
293                            return;
294    
295                    for (Object object : dataEntryIDs) {
296                            if (!(object instanceof Long))
297                                    throw new IllegalStateException(this.getClass().getName() + ": dataEntryIDs contains object which is not a Long: " + object);
298                    }
299            }
300    
301            /**
302             * Execute a query for the given <code>resultDescriptor</code>. This method should contain
303             * the concrete logic for {@link #queryResultDataEntryIDs(ResultDescriptor)} and must be implemented
304             * by subclasses.
305             *
306             * @param resultDescriptor the descriptor specifying what candidates (usually "this" or a variable) the
307             * caller is interested in as well as modifiers (e.g. {@link ResultDescriptor#isNegated() negation})
308             * affecting the query.
309             * @return those {@link DataEntry#getDataEntryID() dataEntryID}s that match the query
310             * criteria for the specified <code>resultSymbol</code> or <code>null</code>, if the symbol is not
311             * supported (this should be consistent with the implementation of {@link #_getResultSymbols()}).
312             * @throws UnsupportedOperationException if the implementation does not support querying at all
313             * (e.g. because it makes no sense without more context) or if the concrete query situation is not
314             * yet supported.
315             * @see #queryResultDataEntryIDs(ResultDescriptor)
316             */
317            protected abstract Set<Long> _queryResultDataEntryIDs(ResultDescriptor resultDescriptor)
318            throws UnsupportedOperationException;
319    
320            /**
321             * <p>
322             * Get those objects that match the query criteria for the specified <code>resultDescriptor</code>
323             * or <code>null</code>, if the given {@link ResultDescriptor#getSymbol() symbol} is not queryable by the
324             * evaluator implementation.
325             * </p>
326             * <p>
327             * This method delegates to {@link #_queryResultObjects(ResultDescriptor)} and caches the
328             * result. Thus a second call to this method with the same symbol does not trigger a
329             * second query but instead immediately returns the cached result.
330             * </p>
331             * <p>
332             * If the subclass of {@link AbstractExpressionEvaluator} does not support querying on its
333             * own (e.g. querying a {@link LiteralEvaluator literal} makes no sense at all), this method
334             * throws an {@link UnsupportedOperationException}. The same exception is thrown, if the requested
335             * query functionality is not yet implemented.
336             * </p>
337             *
338             * @param resultDescriptor the descriptor specifying what candidates (usually "this" or a variable) the
339             * caller is interested in as well as modifiers (e.g. {@link ResultDescriptor#isNegated() negation})
340             * affecting the query.
341             * @return the objects matching the criteria or <code>null</code>, if the given <code>resultSymbol</code>
342             * is not supported (this should be consistent with the implementation of {@link #_getResultSymbols()}).
343             * @throws UnsupportedOperationException if the implementation does not support querying at all
344             * (e.g. because it makes no sense without more context) or if the concrete query situation is not
345             * yet supported.
346             * @see #_queryResultObjects(ResultDescriptor)
347             */
348            public final List<Object> queryResultObjects(ResultDescriptor resultDescriptor)
349            throws UnsupportedOperationException
350            {
351    
352                    List<Object> resultObjects = resultDescriptor2resultObjects.get(resultDescriptor);
353                    if (!resultDescriptor2resultObjects.containsKey(resultDescriptor)) {
354                            resultObjects = _queryResultObjects(resultDescriptor);
355    
356                            if (resultObjects != null)
357                                    resultObjects = Collections.unmodifiableList(resultObjects);
358    
359                            resultDescriptor2resultObjects.put(resultDescriptor, resultObjects);
360                    }
361    
362                    return resultObjects;
363            }
364    
365            /**
366             * <p>
367             * Get those objects that match the query criteria for the specified <code>resultDescriptor</code>
368             * or <code>null</code>, if the given {@link ResultDescriptor#getSymbol() symbol} is not queryable by the
369             * evaluator implementation.
370             * </p>
371             * <p>
372             * The default implementation of this method in {@link AbstractExpressionEvaluator} calls
373             * {@link #queryResultDataEntryIDs(ResultDescriptor)} and then resolves the corresponding objects
374             * (including decrypting their data).
375             * </p>
376             *
377             * @param resultDescriptor the descriptor specifying what candidates (usually "this" or a variable) the
378             * caller is interested in as well as modifiers (e.g. {@link ResultDescriptor#isNegated() negation})
379             * affecting the query.
380             * @return the objects matching the criteria or <code>null</code>, if the given <code>resultDescriptor</code>
381             * is not supported ({@link ResultDescriptor#getSymbol()} should be consistent with the implementation of
382             * {@link #_getResultSymbols()}).
383             * @throws UnsupportedOperationException
384             * @see #queryResultObjects(ResultDescriptor)
385             */
386            protected List<Object> _queryResultObjects(ResultDescriptor resultDescriptor)
387            throws UnsupportedOperationException
388            {
389                    Set<Long> dataEntryIDs = queryResultDataEntryIDs(resultDescriptor);
390                    if (dataEntryIDs == null)
391                            return null;
392    
393                    List<Object> resultList = new ArrayList<Object>(dataEntryIDs.size());
394    
395                    for (Long dataEntryID : dataEntryIDs) {
396                            LongIdentity id = new LongIdentity(DataEntry.class, dataEntryID);
397                            DataEntry dataEntry = (DataEntry) getQueryEvaluator().getPersistenceManagerForData().getObjectById(id);
398                            Object entity = queryEvaluator.getObjectForDataEntry(dataEntry);
399                            resultList.add(entity);
400                    }
401    
402                    return resultList;
403            }
404    
405            private Class<?> getFieldType(Class<?> clazz, List<String> tuples)
406            {
407                    if (clazz == null)
408                            throw new IllegalArgumentException("clazz == null");
409    
410                    tuples = new LinkedList<String>(tuples);
411                    String nextTuple = tuples.remove(0);
412                    AbstractClassMetaData clazzMetaData = getQueryEvaluator().getStoreManager().getMetaDataManager().getMetaDataForClass(clazz, getQueryEvaluator().getClassLoaderResolver());
413                    if (clazzMetaData == null)
414                            throw new IllegalStateException("No meta-data found for class " + clazz.getName());
415    
416                    AbstractMemberMetaData metaDataForMember = clazzMetaData.getMetaDataForMember(nextTuple);
417                    if (metaDataForMember == null)
418                            throw new IllegalStateException("No meta-data found for field \"" + nextTuple + "\" of class \"" + clazz.getName() + "\"!");
419    
420                    if (tuples.isEmpty())
421                            return metaDataForMember.getType();
422                    else
423                            return getFieldType(metaDataForMember.getType(), tuples);
424            }
425    
426            /**
427             * <p>
428             * Get the field type of the <code>PrimaryExpression</code>. This is always the type of the last element in the list of tuples.
429             * </p>
430             * <p>
431             * For example, if the given <code>PrimaryExpression</code> references
432             * <code>this.rating.name</code> and the field <code>name</code> of the class <code>Rating</code> is a {@link String},
433             * this method returns <code>java.lang.String</code>.
434             * </p>
435             *
436             * @param primaryExpression the <code>PrimaryExpression</code> of which to find out the field's type.
437             * @return the type of the field referenced by the given <code>primaryExpression</code>.
438             */
439            protected Class<?> getFieldType(PrimaryExpression primaryExpression)
440            {
441                    if (primaryExpression.getSymbol() != null)
442                            return getQueryEvaluator().getValueType(primaryExpression.getSymbol());
443    
444                    if (primaryExpression.getLeft() instanceof VariableExpression) {
445                            Symbol classSymbol = ((VariableExpression)primaryExpression.getLeft()).getSymbol();
446                            if (classSymbol == null)
447                                    throw new IllegalStateException("((VariableExpression)primaryExpression.getLeft()).getSymbol() returned null!");
448    
449                            return getFieldType(getQueryEvaluator().getValueType(classSymbol), primaryExpression.getTuples());
450                    }
451                    else
452                            throw new UnsupportedOperationException("NYI");
453            }
454    }