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.substring(pos1 [, pos2]) {oper} {compareTo}". 044 */ 045 public class StringSubstringEvaluator 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.substring(...) 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), 072 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 xxx1 in 'substring(xxx1)' 088 Object invokeArg2, // the xxx2 in 'substring(xxx1, xxx2)' 089 Object compareToArgument, // the yyy in 'substring(...) >= 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.fieldMeta == :fieldMeta && " + 101 (invokeArg2 != null ? 102 "this.indexKey.substring(" + invokeArg1 + "," + invokeArg2 +") " : 103 "this.indexKey.substring(" + invokeArg1 + ") ") + 104 ExpressionHelper.getOperatorAsJDOQLSymbol(invokeExprEval.getParent().getExpression().getOperator(), negate) + 105 " :compareToArgument" 106 ); 107 Map<String, Object> params = new HashMap<String, Object>(2); 108 params.put("fieldMeta", fieldMeta); 109 params.put("compareToArgument", compareToArgument); 110 111 @SuppressWarnings("unchecked") 112 Collection<? extends IndexEntry> indexEntries = (Collection<? extends IndexEntry>) q.executeWithMap(params); 113 114 Set<Long> result = new HashSet<Long>(); 115 for (IndexEntry indexEntry : indexEntries) { 116 IndexValue indexValue = queryEval.getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry); 117 result.addAll(indexValue.getDataEntryIDs()); 118 } 119 q.closeAll(); 120 return result; 121 } 122 123 private class MethodResolver extends PrimaryExpressionResolver 124 { 125 private InvokeExpressionEvaluator invokeExprEval; 126 private Object invokePos1; 127 private Object invokePos2; 128 private Object compareToArgument; 129 private boolean negate; 130 131 public MethodResolver( 132 InvokeExpressionEvaluator invokeExprEval, 133 QueryEvaluator queryEvaluator, PrimaryExpression primaryExpression, 134 Object invokeArg1, // the xxx in 'substring(xxx) >= yyy' 135 Object invokeArg2, // the xxx in 'substring(xxx1, xxx2) >= yyy' 136 Object compareToArgument, // the yyy in 'substring(xxx) >= yyy' 137 boolean negate 138 ) 139 { 140 super(queryEvaluator, primaryExpression); 141 this.invokeExprEval = invokeExprEval; 142 this.invokePos1 = invokeArg1; 143 this.invokePos2 = invokeArg2; 144 this.compareToArgument = compareToArgument; 145 this.negate = negate; 146 } 147 148 @Override 149 protected Set<Long> queryEnd(FieldMeta fieldMeta) { 150 return queryEvaluate(invokeExprEval, queryEvaluator, fieldMeta, invokePos1, invokePos2, compareToArgument, negate); 151 } 152 } 153 }