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.FieldMetaRole; 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.QueryUtils; 040 import org.datanucleus.query.expression.Expression; 041 import org.datanucleus.query.expression.Literal; 042 import org.datanucleus.query.expression.ParameterExpression; 043 import org.datanucleus.query.expression.PrimaryExpression; 044 import org.datanucleus.query.expression.VariableExpression; 045 import org.datanucleus.store.ExecutionContext; 046 047 /** 048 * Evaluator for "Collection.contains(element)". 049 */ 050 public class CollectionContainsEvaluator extends AbstractMethodEvaluator 051 { 052 /* (non-Javadoc) 053 * @see org.cumulus4j.store.query.method.MethodEvaluator#evaluate(org.cumulus4j.store.query.QueryEvaluator, org.datanucleus.query.expression.InvokeExpression, org.datanucleus.query.expression.Expression, org.cumulus4j.store.query.eval.ResultDescriptor) 054 */ 055 @Override 056 public Set<Long> evaluate(QueryEvaluator queryEval, InvokeExpressionEvaluator invokeExprEval, 057 Expression invokedExpr, ResultDescriptor resultDesc) { 058 059 if (invokeExprEval.getExpression().getArguments().size() != 1) 060 throw new IllegalStateException("contains(...) expects exactly one argument, but there are " + 061 invokeExprEval.getExpression().getArguments().size()); 062 063 if (invokedExpr instanceof PrimaryExpression) { 064 // Evaluate the invoke argument 065 Expression invokeArgExpr = invokeExprEval.getExpression().getArguments().get(0); 066 Object invokeArgument; 067 if (invokeArgExpr instanceof Literal) 068 invokeArgument = ((Literal)invokeArgExpr).getLiteral(); 069 else if (invokeArgExpr instanceof ParameterExpression) 070 invokeArgument = QueryUtils.getValueForParameterExpression(queryEval.getParameterValues(), (ParameterExpression)invokeArgExpr); 071 else if (invokeArgExpr instanceof VariableExpression) 072 return new ExpressionHelper.ContainsVariableResolver( 073 queryEval, (PrimaryExpression) invokedExpr, FieldMetaRole.collectionElement, (VariableExpression) invokeArgExpr, 074 resultDesc.isNegated() 075 ).query(); 076 else 077 throw new UnsupportedOperationException("NYI"); 078 079 return new ExpressionHelper.ContainsConstantResolver( 080 queryEval, (PrimaryExpression) invokedExpr, FieldMetaRole.collectionElement, invokeArgument, 081 resultDesc.isNegated() 082 ).query(); 083 } 084 else if (invokedExpr instanceof ParameterExpression) { 085 Expression invokeArgExpr = invokeExprEval.getExpression().getArguments().get(0); 086 Object paramValue = QueryUtils.getValueForParameterExpression(queryEval.getParameterValues(), (ParameterExpression)invokedExpr); 087 088 if (invokeArgExpr instanceof PrimaryExpression) { 089 return new ParameterContainsPrimaryEvaluator(queryEval, (PrimaryExpression) invokeArgExpr, (Collection)paramValue, resultDesc.isNegated()).query(); 090 } 091 else { 092 throw new UnsupportedOperationException("NYI invocation of Collection.contains on a " + invokedExpr.getClass().getName()); 093 } 094 } 095 else { 096 throw new UnsupportedOperationException("NYI invocation of Collection.contains on a " + invokedExpr.getClass().getName()); 097 } 098 } 099 100 private class ParameterContainsPrimaryEvaluator extends PrimaryExpressionResolver 101 { 102 private Collection invokeCollection; 103 private boolean negate; 104 105 public ParameterContainsPrimaryEvaluator( 106 QueryEvaluator queryEvaluator, PrimaryExpression primaryExpression, 107 Collection invokeCollection, 108 boolean negate 109 ) 110 { 111 super(queryEvaluator, primaryExpression); 112 this.invokeCollection = invokeCollection; 113 this.negate = negate; 114 } 115 116 @Override 117 protected Set<Long> queryEnd(FieldMeta fieldMeta) { 118 CryptoContext cryptoContext = queryEvaluator.getCryptoContext(); 119 ExecutionContext executionContext = queryEvaluator.getExecutionContext(); 120 IndexEntryFactory indexEntryFactory = queryEvaluator.getStoreManager().getIndexFactoryRegistry().getIndexEntryFactory( 121 executionContext, fieldMeta, true 122 ); 123 124 Query q = queryEvaluator.getPersistenceManagerForIndex().newQuery(indexEntryFactory.getIndexEntryClass()); 125 StringBuilder str = new StringBuilder(); 126 str.append("this.fieldMeta == :fieldMeta"); 127 if (!invokeCollection.isEmpty()) { 128 if (negate) { 129 str.append(" && !:paramColl.contains(this.indexKey)"); 130 } 131 else { 132 str.append(" && :paramColl.contains(this.indexKey)"); 133 } 134 } 135 136 q.setFilter(str.toString()); 137 Map<String, Object> params = new HashMap<String, Object>(2); 138 params.put("fieldMeta", fieldMeta); 139 params.put("paramColl", invokeCollection); 140 141 @SuppressWarnings("unchecked") 142 Collection<? extends IndexEntry> indexEntries = (Collection<? extends IndexEntry>) q.executeWithMap(params); 143 144 Set<Long> result = new HashSet<Long>(); 145 for (IndexEntry indexEntry : indexEntries) { 146 IndexValue indexValue = queryEvaluator.getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry); 147 result.addAll(indexValue.getDataEntryIDs()); 148 } 149 q.closeAll(); 150 return result; 151 } 152 } 153 }