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 }