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