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