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.length(str) {oper} {compareTo}". 045 */ 046 public class StringLengthEvaluator 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() != 0) 064 throw new IllegalStateException("String.length() expects no arguments, but there are " + 065 invokeExprEval.getExpression().getArguments().size()); 066 067 if (invokedExpr instanceof PrimaryExpression) { 068 return new MethodResolver(invokeExprEval, queryEval, (PrimaryExpression) invokedExpr, 069 compareToArgument, resultDesc.isNegated()).query(); 070 } 071 else { 072 if (!invokeExprEval.getLeft().getResultSymbols().contains(resultDesc.getSymbol())) 073 return null; 074 075 return queryEvaluate(invokeExprEval, queryEval, resultDesc.getFieldMeta(), 076 compareToArgument, resultDesc.isNegated()); 077 } 078 } 079 080 private Set<Long> queryEvaluate( 081 InvokeExpressionEvaluator invokeExprEval, 082 QueryEvaluator queryEval, 083 FieldMeta fieldMeta, 084 Object compareToArgument, // the yyy in 'length() >= yyy' 085 boolean negate 086 ) { 087 CryptoContext cryptoContext = queryEval.getCryptoContext(); 088 ExecutionContext executionContext = queryEval.getExecutionContext(); 089 IndexEntryFactory indexEntryFactory = queryEval.getStoreManager().getIndexFactoryRegistry().getIndexEntryFactory( 090 executionContext, fieldMeta, true 091 ); 092 093 Query q = queryEval.getPersistenceManagerForIndex().newQuery(indexEntryFactory.getIndexEntryClass()); 094 q.setFilter( 095 "this.keyStoreRefID == :keyStoreRefID && this.fieldMeta == :fieldMeta && " + 096 "this.indexKey.length() " + 097 ExpressionHelper.getOperatorAsJDOQLSymbol(invokeExprEval.getParent().getExpression().getOperator(), negate) + 098 " :compareToArgument" 099 ); 100 Map<String, Object> params = new HashMap<String, Object>(2); 101 params.put("keyStoreRefID", cryptoContext.getKeyStoreRefID()); 102 params.put("fieldMeta", fieldMeta); 103 params.put("compareToArgument", compareToArgument); 104 105 @SuppressWarnings("unchecked") 106 Collection<? extends IndexEntry> indexEntries = (Collection<? extends IndexEntry>) q.executeWithMap(params); 107 108 Set<Long> result = new HashSet<Long>(); 109 for (IndexEntry indexEntry : indexEntries) { 110 IndexValue indexValue = queryEval.getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry); 111 result.addAll(indexValue.getDataEntryIDs()); 112 } 113 q.closeAll(); 114 return result; 115 } 116 117 private class MethodResolver extends PrimaryExpressionResolver 118 { 119 private InvokeExpressionEvaluator invokeExprEval; 120 private Object compareToArgument; 121 private boolean negate; 122 123 public MethodResolver( 124 InvokeExpressionEvaluator invokeExprEval, 125 QueryEvaluator queryEvaluator, PrimaryExpression primaryExpression, 126 Object compareToArgument, // the yyy in 'length() >= yyy' 127 boolean negate 128 ) 129 { 130 super(queryEvaluator, primaryExpression); 131 this.invokeExprEval = invokeExprEval; 132 this.compareToArgument = compareToArgument; 133 this.negate = negate; 134 } 135 136 @Override 137 protected Set<Long> queryEnd(FieldMeta fieldMeta, ClassMeta classMeta) { 138 return queryEvaluate(invokeExprEval, queryEvaluator, fieldMeta, compareToArgument, negate); 139 } 140 } 141 }