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.substring(pos1 [, pos2]) {oper} {compareTo}". 045 */ 046 public class StringSubstringEvaluator 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.substring(...) 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), 073 compareToArgument, resultDesc.isNegated()).query(); 074 } 075 else { 076 if (!invokeExprEval.getLeft().getResultSymbols().contains(resultDesc.getSymbol())) 077 return null; 078 079 return queryEvaluate(invokeExprEval, queryEval, resultDesc.getFieldMeta(), invokeArgs[0], 080 (invokeArgs.length > 1 ? invokeArgs[1] : null), compareToArgument, resultDesc.isNegated()); 081 } 082 } 083 084 private Set<Long> queryEvaluate( 085 InvokeExpressionEvaluator invokeExprEval, 086 QueryEvaluator queryEval, 087 FieldMeta fieldMeta, 088 Object invokeArg1, // the xxx1 in 'substring(xxx1)' 089 Object invokeArg2, // the xxx2 in 'substring(xxx1, xxx2)' 090 Object compareToArgument, // the yyy in 'substring(...) >= yyy' 091 boolean negate 092 ) { 093 CryptoContext cryptoContext = queryEval.getCryptoContext(); 094 ExecutionContext executionContext = queryEval.getExecutionContext(); 095 IndexEntryFactory indexEntryFactory = queryEval.getStoreManager().getIndexFactoryRegistry().getIndexEntryFactory( 096 executionContext, fieldMeta, true 097 ); 098 099 Query q = queryEval.getPersistenceManagerForIndex().newQuery(indexEntryFactory.getIndexEntryClass()); 100 q.setFilter( 101 "this.keyStoreRefID == :keyStoreRefID && this.fieldMeta_fieldID == :fieldMeta_fieldID && " + 102 (invokeArg2 != null ? 103 "this.indexKey.substring(" + invokeArg1 + "," + invokeArg2 +") " : 104 "this.indexKey.substring(" + invokeArg1 + ") ") + 105 ExpressionHelper.getOperatorAsJDOQLSymbol(invokeExprEval.getParent().getExpression().getOperator(), negate) + 106 " :compareToArgument" 107 ); 108 Map<String, Object> params = new HashMap<String, Object>(2); 109 params.put("keyStoreRefID", cryptoContext.getKeyStoreRefID()); 110 params.put("fieldMeta_fieldID", fieldMeta.getFieldID()); 111 params.put("compareToArgument", compareToArgument); 112 113 @SuppressWarnings("unchecked") 114 Collection<? extends IndexEntry> indexEntries = (Collection<? extends IndexEntry>) q.executeWithMap(params); 115 116 Set<Long> result = new HashSet<Long>(); 117 for (IndexEntry indexEntry : indexEntries) { 118 IndexValue indexValue = queryEval.getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry); 119 result.addAll(indexValue.getDataEntryIDs()); 120 } 121 q.closeAll(); 122 return result; 123 } 124 125 private class MethodResolver extends PrimaryExpressionResolver 126 { 127 private InvokeExpressionEvaluator invokeExprEval; 128 private Object invokePos1; 129 private Object invokePos2; 130 private Object compareToArgument; 131 private boolean negate; 132 133 public MethodResolver( 134 InvokeExpressionEvaluator invokeExprEval, 135 QueryEvaluator queryEvaluator, PrimaryExpression primaryExpression, 136 Object invokeArg1, // the xxx in 'substring(xxx) >= yyy' 137 Object invokeArg2, // the xxx in 'substring(xxx1, xxx2) >= yyy' 138 Object compareToArgument, // the yyy in 'substring(xxx) >= yyy' 139 boolean negate 140 ) 141 { 142 super(queryEvaluator, primaryExpression); 143 this.invokeExprEval = invokeExprEval; 144 this.invokePos1 = invokeArg1; 145 this.invokePos2 = invokeArg2; 146 this.compareToArgument = compareToArgument; 147 this.negate = negate; 148 } 149 150 @Override 151 protected Set<Long> queryEnd(FieldMeta fieldMeta, ClassMeta classMeta) { 152 return queryEvaluate(invokeExprEval, queryEvaluator, fieldMeta, invokePos1, invokePos2, compareToArgument, negate); 153 } 154 } 155 }