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