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