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 }