001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/agg/MinusStarPredicate.java#3 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2006-2007 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.rolap.agg;
011    
012    import mondrian.rolap.StarPredicate;
013    import mondrian.rolap.StarColumnPredicate;
014    import mondrian.rolap.RolapStar;
015    
016    import java.util.*;
017    
018    /**
019     * A <code>StarPredicate</code> which evaluates to true if its
020     * first child evaluates to true and its second child evaluates to false.
021     *
022     * @author jhyde
023     * @version $Id: //open/mondrian/src/main/mondrian/rolap/agg/MinusStarPredicate.java#3 $
024     * @since Nov 6, 2006
025     */
026    public class MinusStarPredicate extends AbstractColumnPredicate {
027        private final StarColumnPredicate plus;
028        private final StarColumnPredicate minus;
029    
030        /**
031         * Creates a MinusStarPredicate.
032         *
033         * @param plus Positive predicate
034         * @param minus Negative predicate
035         * @pre plus != null
036         * @pre minus != null
037         */
038        public MinusStarPredicate(
039            StarColumnPredicate plus,
040            StarColumnPredicate minus)
041        {
042            super(plus.getConstrainedColumn());
043            assert minus != null;
044            this.plus = plus;
045            this.minus = minus;
046        }
047    
048    
049        public boolean equals(Object obj) {
050            if (obj instanceof MinusStarPredicate) {
051                MinusStarPredicate that = (MinusStarPredicate) obj;
052                return this.plus.equals(that.plus) &&
053                    this.minus.equals(that.minus);
054            } else {
055                return false;
056            }
057        }
058    
059        public int hashCode() {
060            return plus.hashCode() * 31 +
061                minus.hashCode();
062        }
063    
064        public RolapStar.Column getConstrainedColumn() {
065            return plus.getConstrainedColumn();
066        }
067    
068        public void values(Collection<Object> collection) {
069            Set<Object> plusValues = new HashSet<Object>();
070            plus.values(plusValues);
071            List<Object> minusValues = new ArrayList<Object>();
072            minus.values(minusValues);
073            plusValues.removeAll(minusValues);
074            collection.addAll(plusValues);
075        }
076    
077        public boolean evaluate(Object value) {
078            return plus.evaluate(value) &&
079                !minus.evaluate(value);
080        }
081    
082        public void describe(StringBuilder buf) {
083            buf.append("(").append(plus).append(" - ").append(minus).append(")");
084        }
085    
086        public Overlap intersect(StarColumnPredicate predicate) {
087            throw new UnsupportedOperationException();
088        }
089    
090        public boolean mightIntersect(StarPredicate other) {
091            // Approximately, this constraint might intersect if it intersects
092            // with the 'plus' side. It's possible that the 'minus' side might
093            // wipe out all of those intersections, but we don't consider that.
094            return plus.mightIntersect(other);
095        }
096    
097        public StarColumnPredicate minus(StarPredicate predicate) {
098            assert predicate != null;
099            if (predicate instanceof ValueColumnPredicate) {
100                ValueColumnPredicate valuePredicate =
101                    (ValueColumnPredicate) predicate;
102                if (!evaluate(valuePredicate.getValue())) {
103                    // Case 3: 'minus' is a list, 'constraint' is a value
104                    // which is not matched by this
105                    return this;
106                }
107            }
108            if (minus instanceof ListColumnPredicate) {
109                ListColumnPredicate minusList = (ListColumnPredicate) minus;
110                RolapStar.Column column = plus.getConstrainedColumn();
111                if (predicate instanceof ListColumnPredicate) {
112                    // Case 1: 'minus' and 'constraint' are both lists.
113                    ListColumnPredicate list =
114                        (ListColumnPredicate) predicate;
115                    List<StarColumnPredicate> unionList =
116                        new ArrayList<StarColumnPredicate>();
117                    unionList.addAll(minusList.getPredicates());
118                    unionList.addAll(list.getPredicates());
119                    return new MinusStarPredicate(
120                        plus,
121                        new ListColumnPredicate(
122                            column,
123                            unionList));
124                }
125                if (predicate instanceof ValueColumnPredicate) {
126                    ValueColumnPredicate valuePredicate =
127                        (ValueColumnPredicate) predicate;
128                    if (!evaluate(valuePredicate.getValue())) {
129                        // Case 3: 'minus' is a list, 'constraint' is a value
130                        // which is not matched by this
131                        return this;
132                    }
133                    // Case 2: 'minus' is a list, 'constraint' is a value.
134                    List<StarColumnPredicate> unionList =
135                        new ArrayList<StarColumnPredicate>();
136                    unionList.addAll(minusList.getPredicates());
137                    unionList.add(
138                        new ValueColumnPredicate(column, valuePredicate.getValue()));
139                    return new MinusStarPredicate(
140                        plus,
141                        new ListColumnPredicate(column, unionList));
142                }
143            }
144            return new MinusStarPredicate(
145                this,
146                (StarColumnPredicate) predicate);
147        }
148    
149        public StarColumnPredicate cloneWithColumn(RolapStar.Column column) {
150            return new MinusStarPredicate(
151                plus.cloneWithColumn(column),
152                minus.cloneWithColumn(column));
153        }
154    }
155    
156    // End MinusStarPredicate.java