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