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