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 java.util.HashSet;
021    import java.util.Set;
022    
023    import org.cumulus4j.store.crypto.CryptoContext;
024    import org.cumulus4j.store.query.QueryEvaluator;
025    import org.cumulus4j.store.query.QueryHelper;
026    import org.datanucleus.query.expression.DyadicExpression;
027    import org.datanucleus.query.expression.Expression;
028    import org.datanucleus.util.NucleusLogger;
029    
030    /**
031     * <p>
032     * Evaluator handling the boolean operation "||" (OR).
033     * </p>
034     * <p>
035     * This evaluator works just like the {@link AndExpressionEvaluator} with the only difference
036     * that it unites the partial results instead of intersecting them.
037     * </p>
038     * <p>
039     * If the {@link ResultDescriptor} indicates a {@link ResultDescriptor#isNegated() negation}, this evaluator
040     * delegates to the {@link AndExpressionEvaluator}, because a query like
041     * "!( a > 5 || b <= 12 )" is internally converted to "a <= 5 &amp;&amp; b > 12" for performance reasons.
042     * See {@link NotExpressionEvaluator} as well as
043     * <a target="_blank" href="http://en.wikipedia.org/wiki/De_Morgan%27s_laws">De Morgan's laws</a> in wikipedia for details.
044     * </p>
045     *
046     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
047     * @see AndExpressionEvaluator
048     */
049    public class OrExpressionEvaluator
050    extends AbstractExpressionEvaluator<DyadicExpression>
051    {
052            private AndExpressionEvaluator negatedExpressionEvaluator;
053    
054            public OrExpressionEvaluator(QueryEvaluator queryEvaluator, AbstractExpressionEvaluator<?> parent, DyadicExpression expression) {
055                    super(queryEvaluator, parent, expression);
056            }
057    
058            public OrExpressionEvaluator(AndExpressionEvaluator negatedExpressionEvaluator)
059            {
060                    this(negatedExpressionEvaluator.getQueryEvaluator(), negatedExpressionEvaluator.getParent(), negatedExpressionEvaluator.getExpression());
061                    this.negatedExpressionEvaluator = negatedExpressionEvaluator;
062            }
063    
064            @Override
065            public AbstractExpressionEvaluator<? extends Expression> getLeft() {
066                    if (negatedExpressionEvaluator != null)
067                            return negatedExpressionEvaluator.getLeft();
068    
069                    return super.getLeft();
070            }
071    
072            @Override
073            public AbstractExpressionEvaluator<? extends Expression> getRight() {
074                    if (negatedExpressionEvaluator != null)
075                            return negatedExpressionEvaluator.getRight();
076    
077                    return super.getRight();
078            }
079    
080            @Override
081            protected Set<Long> _queryResultDataEntryIDs(ResultDescriptor resultDescriptor)
082            {
083                    if (resultDescriptor.isNegated())
084                            return new AndExpressionEvaluator(this)._queryResultDataEntryIDsIgnoringNegation(resultDescriptor);
085                    else
086                            return _queryResultDataEntryIDsIgnoringNegation(resultDescriptor);
087            }
088    
089            protected Set<Long> _queryResultDataEntryIDsIgnoringNegation(ResultDescriptor resultDescriptor)
090            {
091                    if (getLeft() == null)
092                            throw new IllegalStateException("getLeft() == null");
093    
094                    if (getRight() == null)
095                            throw new IllegalStateException("getRight() == null");
096    
097                    Set<Long> leftResult = null;
098                    boolean leftEvaluated = true;
099                    try {
100                            leftResult = getLeft().queryResultDataEntryIDs(resultDescriptor);
101                    }
102                    catch (UnsupportedOperationException uoe) {
103                            leftEvaluated = false;
104                            getQueryEvaluator().setIncomplete();
105                            NucleusLogger.QUERY.debug("Unsupported operation in LEFT : "+getLeft().getExpression() + " so deferring evaluation to in-memory");
106                    }
107    
108                    Set<Long> rightResult = null;
109                    boolean rightEvaluated = true;
110                    try {
111                            rightResult = getRight().queryResultDataEntryIDs(resultDescriptor);
112                    }
113                    catch (UnsupportedOperationException uoe) {
114                            rightEvaluated = false;
115                            getQueryEvaluator().setIncomplete();
116                            NucleusLogger.QUERY.debug("Unsupported operation in RIGHT : "+getRight().getExpression() + " so deferring evaluation to in-memory");
117                    }
118    
119                    if (leftEvaluated && !rightEvaluated) {
120                            CryptoContext cryptoContext = getQueryEvaluator().getCryptoContext();
121                            rightResult = QueryHelper.getAllDataEntryIdsForCandidate(cryptoContext,
122                                            getQueryEvaluator().getPersistenceManagerForData(), getQueryEvaluator().getQuery().getCandidateClass(), getQueryEvaluator().getQuery().isSubclasses());
123                    }
124                    else if (!leftEvaluated && rightEvaluated) {
125                            CryptoContext cryptoContext = getQueryEvaluator().getCryptoContext();
126                            leftResult = QueryHelper.getAllDataEntryIdsForCandidate(cryptoContext,
127                                            getQueryEvaluator().getPersistenceManagerForData(), getQueryEvaluator().getQuery().getCandidateClass(), getQueryEvaluator().getQuery().isSubclasses());
128                    }
129                    else if (!leftEvaluated && !rightEvaluated) {
130                            CryptoContext cryptoContext = getQueryEvaluator().getCryptoContext();
131                            leftResult = QueryHelper.getAllDataEntryIdsForCandidate(cryptoContext,
132                                            getQueryEvaluator().getPersistenceManagerForData(), getQueryEvaluator().getQuery().getCandidateClass(), getQueryEvaluator().getQuery().isSubclasses());
133                    }
134    
135                    if (leftResult != null && rightResult != null) {
136                            Set<Long> result = new HashSet<Long>(leftResult.size() + rightResult.size());
137                            result.addAll(leftResult);
138                            result.addAll(rightResult);
139                            return result;
140                    }
141                    else if (leftResult != null)
142                            return leftResult;
143                    else
144                            return rightResult;
145            }
146    }