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 }