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}.equalsIgnoreCase(arg)".
045 */
046 public class StringEqualsIgnoreCaseEvaluator extends AbstractMethodEvaluator
047 {
048 /* (non-Javadoc)
049 * @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)
050 */
051 @Override
052 public Set<Long> evaluate(QueryEvaluator queryEval, InvokeExpressionEvaluator invokeExprEval,
053 Expression invokedExpr, ResultDescriptor resultDesc) {
054 if (invokeExprEval.getExpression().getArguments().size() != 1)
055 throw new IllegalStateException("{String}.equalsIgnoreCase(...) expects exactly one argument, but there are " +
056 invokeExprEval.getExpression().getArguments().size());
057
058 // Evaluate the invoke argument
059 Object invokeArgument = ExpressionHelper.getEvaluatedInvokeArgument(queryEval, invokeExprEval.getExpression());
060
061 if (invokedExpr instanceof PrimaryExpression) {
062 return new MethodResolver(queryEval, (PrimaryExpression) invokedExpr, invokeArgument, resultDesc.isNegated()).query();
063 }
064 else {
065 if (!invokeExprEval.getLeft().getResultSymbols().contains(resultDesc.getSymbol()))
066 return null;
067 return queryEvaluate(queryEval, resultDesc.getFieldMeta(), invokeArgument, resultDesc.isNegated());
068 }
069 }
070
071 private Set<Long> queryEvaluate(
072 QueryEvaluator queryEval,
073 FieldMeta fieldMeta,
074 Object invokeArgument, // the xxx in 'equalsIgnoreCase(xxx)'
075 boolean negate
076 ) {
077 CryptoContext cryptoContext = queryEval.getCryptoContext();
078 ExecutionContext executionContext = queryEval.getExecutionContext();
079 IndexEntryFactory indexEntryFactory = queryEval.getStoreManager().getIndexFactoryRegistry().getIndexEntryFactory(
080 executionContext, fieldMeta, true
081 );
082
083 Query q = queryEval.getPersistenceManagerForIndex().newQuery(indexEntryFactory.getIndexEntryClass());
084 q.setFilter(
085 "this.keyStoreRefID == :keyStoreRefID && this.fieldMeta == :fieldMeta && " +
086 (negate ? "!this.indexKey.toUpperCase() == :invokeArg.toUpperCase()" : "this.indexKey.toUpperCase() == :invokeArg.toUpperCase()")
087 );
088 Map<String, Object> params = new HashMap<String, Object>(3);
089 params.put("keyStoreRefID", cryptoContext.getKeyStoreRefID());
090 params.put("fieldMeta", fieldMeta);
091 params.put("invokeArg", invokeArgument);
092
093 @SuppressWarnings("unchecked")
094 Collection<? extends IndexEntry> indexEntries = (Collection<? extends IndexEntry>) q.executeWithMap(params);
095
096 Set<Long> result = new HashSet<Long>();
097 for (IndexEntry indexEntry : indexEntries) {
098 IndexValue indexValue = queryEval.getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry);
099 result.addAll(indexValue.getDataEntryIDs());
100 }
101 q.closeAll();
102 return result;
103 }
104
105 private class MethodResolver extends PrimaryExpressionResolver
106 {
107 private Object invokeArgument;
108 private boolean negate;
109
110 public MethodResolver(
111 QueryEvaluator queryEvaluator, PrimaryExpression primaryExpression,
112 Object invokeArgument, // the xxx in 'equalsIgnorecase(xxx)'
113 boolean negate
114 )
115 {
116 super(queryEvaluator, primaryExpression);
117 this.invokeArgument = invokeArgument;
118 this.negate = negate;
119 }
120
121 @Override
122 protected Set<Long> queryEnd(FieldMeta fieldMeta, ClassMeta classMeta) {
123 return queryEvaluate(queryEvaluator, fieldMeta, invokeArgument, negate);
124 }
125 }
126 }