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&lt;String&gt;.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    }