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