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.eval; 019 020 import org.cumulus4j.store.model.FieldMeta; 021 import org.datanucleus.query.symbol.Symbol; 022 023 /** 024 * <p> 025 * Descriptor specifying what kind of result is expected when a query is executed. 026 * This contains the information what candidates a query should search 027 * (usually "this" or a variable) as well as modifiers affecting the query 028 * (e.g. {@link #isNegated() negation}). 029 * </p> 030 * 031 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 032 */ 033 public class ResultDescriptor 034 { 035 private Symbol symbol; 036 private Class<?> resultType; 037 private FieldMeta fieldMeta; 038 private boolean negated; 039 040 /** 041 * Create a <code>ResultDescriptor</code>. 042 * @param symbol the symbol; must not be <code>null</code>. 043 * @param resultType the type of the searched candidates. This can be <code>null</code>, 044 * if {@link Symbol#getValueType()} is not <code>null</code>. If {@link Symbol#getValueType()} 045 * is not <code>null</code>, this argument is ignored. 046 */ 047 public ResultDescriptor(Symbol symbol, Class<?> resultType) 048 { 049 if (symbol == null) 050 throw new IllegalArgumentException("symbol == null"); 051 052 this.symbol = symbol; 053 054 if (symbol.getValueType() != null) 055 this.resultType = symbol.getValueType(); 056 else 057 this.resultType = resultType; 058 059 if (this.resultType == null) 060 throw new IllegalArgumentException("resultType could not be determined!"); 061 } 062 063 /** 064 * Create a <code>ResultDescriptor</code>. 065 * @param symbol the symbol; must not be <code>null</code>. 066 * @param resultType the type of the searched candidates. This can be <code>null</code>, 067 * if {@link Symbol#getValueType()} is not <code>null</code>. If {@link Symbol#getValueType()} 068 * is not <code>null</code>, this argument is ignored. 069 * @param fieldMeta the field to be queried, if there is no FCO candidate. Must be 070 * <code>null</code>, if an FCO is searched. 071 */ 072 public ResultDescriptor(Symbol symbol, Class<?> resultType, FieldMeta fieldMeta) 073 { 074 this(symbol, resultType); 075 this.fieldMeta = fieldMeta; 076 } 077 078 private ResultDescriptor(Symbol symbol, Class<?> resultType, FieldMeta fieldMeta, boolean negated) 079 { 080 this(symbol, resultType, fieldMeta); 081 this.negated = negated; 082 } 083 084 /** 085 * Get the symbol specifying what candidates are searched. 086 * 087 * @return the symbol; never <code>null</code>. 088 */ 089 public Symbol getSymbol() { 090 return symbol; 091 } 092 093 /** 094 * Get the type of the searched candidates. Note, that they might be instances of a subclass. 095 * @return the type; never <code>null</code>. 096 */ 097 public Class<?> getResultType() { 098 return resultType; 099 } 100 101 /** 102 * Get the {@link FieldMeta} to query, if there is no FCO candidate. For example, when 103 * querying for a joined <code>Set<String>.contains(variable)</code>. 104 * This is <code>null</code> when querying for an FCO (then the context is clear from the symbol). 105 * @return the {@link FieldMeta} to query or <code>null</code>. 106 */ 107 public FieldMeta getFieldMeta() { 108 return fieldMeta; 109 } 110 111 /** 112 * <p> 113 * Whether the result is the negation of the actual criteria. 114 * </p> 115 * <p> 116 * It is quite expensive to evaluate a negation (JDOQL "!") by first querying the normal (non-negated) 117 * result and then negating it by querying ALL candidates and finally filtering the normal result 118 * out. Therefore, we instead push the negation down the expression-evaluator-tree into the leafs. 119 * Thus {@link NotExpressionEvaluator} simply calls {@link #negate()} and passes the negated 120 * <code>ResultDescriptor</code> down the evaluator-tree. 121 * All nodes in the tree therefore have to take this flag into account. 122 * </p> 123 * 124 * @return whether the result is the negation of the actual criteria. 125 */ 126 public boolean isNegated() { 127 return negated; 128 } 129 130 /** 131 * Create a negation of this <code>ResultDescriptor</code>. The result will be a copy of this 132 * instance with all fields having the same value except for the {@link #isNegated() negated} flag 133 * which will have the opposite value. 134 * @return a negation of this <code>ResultDescriptor</code>. 135 */ 136 public ResultDescriptor negate() 137 { 138 return new ResultDescriptor(symbol, resultType, fieldMeta, !negated); 139 } 140 141 @Override 142 public int hashCode() { 143 final int prime = 31; 144 int result = 1; 145 result = prime * result + ((symbol == null) ? 0 : symbol.hashCode()); 146 result = prime * result + ((resultType == null) ? 0 : resultType.hashCode()); 147 result = prime * result + ((fieldMeta == null) ? 0 : fieldMeta.hashCode()); 148 result = prime * result + (negated ? 1231 : 1237); 149 return result; 150 } 151 152 @Override 153 public boolean equals(Object obj) { 154 if (this == obj) return true; 155 if (obj == null) return false; 156 if (getClass() != obj.getClass()) 157 return false; 158 159 ResultDescriptor other = (ResultDescriptor) obj; 160 return ( 161 this.symbol == other.symbol || (this.symbol != null && this.symbol.equals(other.symbol)) && 162 this.negated == other.negated && 163 this.resultType == other.resultType || (this.resultType != null && this.resultType.equals(other.resultType)) && 164 this.fieldMeta == other.fieldMeta || (this.fieldMeta != null && this.fieldMeta.equals(other.fieldMeta)) 165 ); 166 } 167 }