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