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.toUpperCase() {oper} {compareTo}".
044 */
045 public class StringToUpperCaseEvaluator 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() != 0)
063 throw new IllegalStateException("String.toUpperCase(...) expects exactly no arguments, but there are " +
064 invokeExprEval.getExpression().getArguments().size());
065
066 if (invokedExpr instanceof PrimaryExpression) {
067 return new MethodResolver(invokeExprEval, queryEval, (PrimaryExpression) invokedExpr,
068 compareToArgument, resultDesc.isNegated()).query();
069 }
070 else {
071 if (!invokeExprEval.getLeft().getResultSymbols().contains(resultDesc.getSymbol()))
072 return null;
073
074 return queryEvaluate(invokeExprEval, queryEval, resultDesc.getFieldMeta(),
075 compareToArgument, resultDesc.isNegated());
076 }
077 }
078
079 private Set<Long> queryEvaluate(
080 InvokeExpressionEvaluator invokeExprEval,
081 QueryEvaluator queryEval,
082 FieldMeta fieldMeta,
083 Object compareToArgument, // the yyy in 'toUpperCase() >= yyy'
084 boolean negate
085 ) {
086 CryptoContext cryptoContext = queryEval.getCryptoContext();
087 ExecutionContext executionContext = queryEval.getExecutionContext();
088 IndexEntryFactory indexEntryFactory = queryEval.getStoreManager().getIndexFactoryRegistry().getIndexEntryFactory(
089 executionContext, fieldMeta, true
090 );
091
092 Query q = queryEval.getPersistenceManagerForIndex().newQuery(indexEntryFactory.getIndexEntryClass());
093 q.setFilter(
094 "this.fieldMeta == :fieldMeta && " +
095 "this.indexKey.toUpperCase() " +
096 ExpressionHelper.getOperatorAsJDOQLSymbol(invokeExprEval.getParent().getExpression().getOperator(), negate) +
097 " :compareToArgument"
098 );
099 Map<String, Object> params = new HashMap<String, Object>(2);
100 params.put("fieldMeta", fieldMeta);
101 params.put("compareToArgument", compareToArgument);
102
103 @SuppressWarnings("unchecked")
104 Collection<? extends IndexEntry> indexEntries = (Collection<? extends IndexEntry>) q.executeWithMap(params);
105
106 Set<Long> result = new HashSet<Long>();
107 for (IndexEntry indexEntry : indexEntries) {
108 IndexValue indexValue = queryEval.getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry);
109 result.addAll(indexValue.getDataEntryIDs());
110 }
111 q.closeAll();
112 return result;
113 }
114
115 private class MethodResolver extends PrimaryExpressionResolver
116 {
117 private InvokeExpressionEvaluator invokeExprEval;
118 private Object compareToArgument;
119 private boolean negate;
120
121 public MethodResolver(
122 InvokeExpressionEvaluator invokeExprEval,
123 QueryEvaluator queryEvaluator, PrimaryExpression primaryExpression,
124 Object compareToArgument, // the yyy in 'toUpperCase() == yyy'
125 boolean negate
126 )
127 {
128 super(queryEvaluator, primaryExpression);
129 this.invokeExprEval = invokeExprEval;
130 this.compareToArgument = compareToArgument;
131 this.negate = negate;
132 }
133
134 @Override
135 protected Set<Long> queryEnd(FieldMeta fieldMeta) {
136 return queryEvaluate(invokeExprEval, queryEvaluator, fieldMeta, compareToArgument, negate);
137 }
138 }
139 }