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 }