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.method;
019    
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.HashSet;
023    import java.util.Map;
024    import java.util.Set;
025    
026    import javax.jdo.Query;
027    
028    import org.cumulus4j.store.crypto.CryptoContext;
029    import org.cumulus4j.store.model.ClassMeta;
030    import org.cumulus4j.store.model.FieldMeta;
031    import org.cumulus4j.store.model.IndexEntry;
032    import org.cumulus4j.store.model.IndexEntryFactory;
033    import org.cumulus4j.store.model.IndexValue;
034    import org.cumulus4j.store.query.QueryEvaluator;
035    import org.cumulus4j.store.query.eval.ExpressionHelper;
036    import org.cumulus4j.store.query.eval.InvokeExpressionEvaluator;
037    import org.cumulus4j.store.query.eval.PrimaryExpressionResolver;
038    import org.cumulus4j.store.query.eval.ResultDescriptor;
039    import org.datanucleus.query.expression.Expression;
040    import org.datanucleus.query.expression.PrimaryExpression;
041    
042    /**
043     * Evaluator for "Collection.size() {oper} {comparisonArg}".
044     */
045    public class CollectionSizeEvaluator extends AbstractMethodEvaluator {
046    
047            /* (non-Javadoc)
048             * @see org.cumulus4j.store.query.method.AbstractMethodEvaluator#requiresComparisonArgument()
049             */
050            @Override
051            public boolean requiresComparisonArgument() {
052                    return true;
053            }
054    
055            /* (non-Javadoc)
056             * @see org.cumulus4j.store.query.method.MethodEvaluator#evaluate(org.cumulus4j.store.query.QueryEvaluator, org.cumulus4j.store.query.eval.InvokeExpressionEvaluator, org.datanucleus.query.expression.Expression, org.cumulus4j.store.query.eval.ResultDescriptor)
057             */
058            @Override
059            public Set<Long> evaluate(QueryEvaluator queryEval,
060                            InvokeExpressionEvaluator invokeExprEval, Expression invokedExpr,
061                            ResultDescriptor resultDesc) {
062                    if (invokeExprEval.getExpression().getArguments().size() != 0)
063                            throw new IllegalStateException("size(...) expects no argument, but there are " +
064                                            invokeExprEval.getExpression().getArguments().size());
065    
066                    if (invokedExpr instanceof PrimaryExpression) {
067                            return new CollectionSizeResolver(invokeExprEval, queryEval, (PrimaryExpression) invokedExpr, compareToArgument, resultDesc.isNegated()).query();
068                    }
069                    else {
070                            if (!invokeExprEval.getLeft().getResultSymbols().contains(resultDesc.getSymbol()))
071                                    return null;
072                            return queryCollectionSize(invokeExprEval, queryEval, resultDesc.getFieldMeta(), compareToArgument, resultDesc.isNegated());
073                    }
074            }
075    
076            private Set<Long> queryCollectionSize(
077                            InvokeExpressionEvaluator invokeExprEval,
078                            QueryEvaluator queryEval,
079                            FieldMeta fieldMeta,
080                            Object compareToArgument, // the yyy in 'indexOf(xxx) >= yyy'
081                            boolean negate
082            ) {
083                    CryptoContext cryptoContext = queryEval.getCryptoContext();
084                    IndexEntryFactory indexEntryFactory = queryEval.getStoreManager().getIndexFactoryRegistry().getIndexEntryFactoryForContainerSize();
085    
086                    Query q = queryEval.getPersistenceManagerForIndex().newQuery(indexEntryFactory.getIndexEntryClass());
087                    q.setFilter(
088                                    "this.keyStoreRefID == :keyStoreRefID && this.fieldMeta_fieldID == :fieldMeta_fieldID && this.indexKey " +
089                                    ExpressionHelper.getOperatorAsJDOQLSymbol(invokeExprEval.getParent().getExpression().getOperator(), negate) +
090                                    " :compareToArgument"
091                    );
092                    Map<String, Object> params = new HashMap<String, Object>(3);
093                    params.put("keyStoreRefID", cryptoContext.getKeyStoreRefID());
094                    params.put("fieldMeta_fieldID", fieldMeta.getFieldID());
095                    params.put("compareToArgument", compareToArgument);
096    
097                    @SuppressWarnings("unchecked")
098                    Collection<? extends IndexEntry> indexEntries = (Collection<? extends IndexEntry>) q.executeWithMap(params);
099    
100                    Set<Long> result = new HashSet<Long>();
101                    for (IndexEntry indexEntry : indexEntries) {
102                            IndexValue indexValue = queryEval.getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry);
103                            result.addAll(indexValue.getDataEntryIDs());
104                    }
105                    q.closeAll();
106                    return result;
107            }
108    
109            private class CollectionSizeResolver extends PrimaryExpressionResolver
110            {
111                    private InvokeExpressionEvaluator invokeExprEval;
112                    private Object compareToArgument;
113                    private boolean negate;
114    
115                    public CollectionSizeResolver(
116                                    InvokeExpressionEvaluator invokeExprEval,
117                                    QueryEvaluator queryEvaluator, PrimaryExpression primaryExpression,
118                                    Object compareToArgument, // the yyy in 'size() >= yyy'
119                                    boolean negate
120                    )
121                    {
122                            super(queryEvaluator, primaryExpression);
123                            this.invokeExprEval = invokeExprEval;
124                            this.compareToArgument = compareToArgument;
125                            this.negate = negate;
126                    }
127    
128                    @Override
129                    protected Set<Long> queryEnd(FieldMeta fieldMeta, ClassMeta classMeta) {
130                            return queryCollectionSize(invokeExprEval, queryEvaluator, fieldMeta, compareToArgument, negate);
131                    }
132            }
133    }