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