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