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.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.ExecutionContext;
040 import org.datanucleus.query.expression.Expression;
041 import org.datanucleus.query.expression.PrimaryExpression;
042
043 /**
044 * Evaluator for "String.indexOf(str [,from]) {oper} {compareTo}".
045 */
046 public class StringIndexOfEvaluator extends AbstractMethodEvaluator {
047
048 /* (non-Javadoc)
049 * @see org.cumulus4j.store.query.method.AbstractMethodEvaluator#requiresComparisonArgument()
050 */
051 @Override
052 public boolean requiresComparisonArgument() {
053 return true;
054 }
055
056 /* (non-Javadoc)
057 * @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)
058 */
059 @Override
060 public Set<Long> evaluate(QueryEvaluator queryEval,
061 InvokeExpressionEvaluator invokeExprEval, Expression invokedExpr,
062 ResultDescriptor resultDesc) {
063 if (invokeExprEval.getExpression().getArguments().size() < 1 || invokeExprEval.getExpression().getArguments().size() > 2)
064 throw new IllegalStateException("String.indexOf(...) expects 1 or 2 arguments, but there are " +
065 invokeExprEval.getExpression().getArguments().size());
066
067 // Evaluate the invoke argument
068 Object[] invokeArgs = ExpressionHelper.getEvaluatedInvokeArguments(queryEval, invokeExprEval.getExpression());
069
070 if (invokedExpr instanceof PrimaryExpression) {
071 return new MethodResolver(invokeExprEval, queryEval, (PrimaryExpression) invokedExpr, invokeArgs[0],
072 (invokeArgs.length > 1 ? invokeArgs[1] : null), compareToArgument, resultDesc.isNegated()).query();
073 }
074 else {
075 if (!invokeExprEval.getLeft().getResultSymbols().contains(resultDesc.getSymbol()))
076 return null;
077
078 return queryEvaluate(invokeExprEval, queryEval, resultDesc.getFieldMeta(), invokeArgs[0],
079 (invokeArgs.length > 1 ? invokeArgs[1] : null), compareToArgument, resultDesc.isNegated());
080 }
081 }
082
083 private Set<Long> queryEvaluate(
084 InvokeExpressionEvaluator invokeExprEval,
085 QueryEvaluator queryEval,
086 FieldMeta fieldMeta,
087 Object invokeArg1, // the xxx in 'indexOf(xxx)'
088 Object invokeArg2, // the xxx2 in 'indexOf(xxx1, xxx2)'
089 Object compareToArgument, // the yyy in 'indexOf(xxx) >= yyy'
090 boolean negate
091 ) {
092 CryptoContext cryptoContext = queryEval.getCryptoContext();
093 ExecutionContext executionContext = queryEval.getExecutionContext();
094 IndexEntryFactory indexEntryFactory = queryEval.getStoreManager().getIndexFactoryRegistry().getIndexEntryFactory(
095 executionContext, fieldMeta, true
096 );
097
098 Query q = queryEval.getPersistenceManagerForIndex().newQuery(indexEntryFactory.getIndexEntryClass());
099 q.setFilter(
100 "this.keyStoreRefID == :keyStoreRefID && this.fieldMeta_fieldID == :fieldMeta_fieldID && " +
101 (invokeArg2 != null ?
102 "this.indexKey.indexOf(:invokeArg,:invokeFrom) " : "this.indexKey.indexOf(:invokeArg) ") +
103 ExpressionHelper.getOperatorAsJDOQLSymbol(invokeExprEval.getParent().getExpression().getOperator(), negate) +
104 " :compareToArgument"
105 );
106 Map<String, Object> params = new HashMap<String, Object>(3);
107 params.put("keyStoreRefID", cryptoContext.getKeyStoreRefID());
108 params.put("fieldMeta_fieldID", fieldMeta.getFieldID());
109 params.put("invokeArg", invokeArg1);
110 if (invokeArg2 != null)
111 {
112 params.put("invokeFrom", invokeArg2);
113 }
114 params.put("compareToArgument", compareToArgument);
115
116 @SuppressWarnings("unchecked")
117 Collection<? extends IndexEntry> indexEntries = (Collection<? extends IndexEntry>) q.executeWithMap(params);
118
119 Set<Long> result = new HashSet<Long>();
120 for (IndexEntry indexEntry : indexEntries) {
121 IndexValue indexValue = queryEval.getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry);
122 result.addAll(indexValue.getDataEntryIDs());
123 }
124 q.closeAll();
125 return result;
126 }
127
128 private class MethodResolver extends PrimaryExpressionResolver
129 {
130 private InvokeExpressionEvaluator invokeExprEval;
131 private Object invokeArg;
132 private Object invokeFrom;
133 private Object compareToArgument;
134 private boolean negate;
135
136 public MethodResolver(
137 InvokeExpressionEvaluator invokeExprEval,
138 QueryEvaluator queryEvaluator, PrimaryExpression primaryExpression,
139 Object invokeArg1, // the xxx1 in 'indexOf(xxx1) >= yyy'
140 Object invokeArg2, // the xxx2 in 'indexOf(xxx1, xxx2) >= yyy'
141 Object compareToArgument, // the yyy in 'indexOf(xxx) >= yyy'
142 boolean negate
143 )
144 {
145 super(queryEvaluator, primaryExpression);
146 this.invokeExprEval = invokeExprEval;
147 this.invokeArg = invokeArg1;
148 this.invokeFrom = invokeArg2;
149 this.compareToArgument = compareToArgument;
150 this.negate = negate;
151 }
152
153 @Override
154 protected Set<Long> queryEnd(FieldMeta fieldMeta, ClassMeta classMeta) {
155 return queryEvaluate(invokeExprEval, queryEvaluator, fieldMeta, invokeArg, invokeFrom, compareToArgument, negate);
156 }
157 }
158 }