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