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 }