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