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 }