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.Collection;
015
016/**
017 * Predicate constraining a column to be greater than or less than a given
018 * bound, or between a pair of bounds.
019 *
020 * @author jhyde
021 * @since Nov 26, 2006
022 */
023public class RangeColumnPredicate extends AbstractColumnPredicate {
024    private final boolean lowerInclusive;
025    private final ValueColumnPredicate lowerBound;
026    private final boolean upperInclusive;
027    private final ValueColumnPredicate upperBound;
028
029    /**
030     * Creates a RangeColumnPredicate.
031     *
032     * @param column Constrained column
033     * @param lowerInclusive Whether range includes the lower bound;
034     *   must be false if not bounded below
035     * @param lowerBound Lower bound, or null if not bounded below
036     * @param upperInclusive Whether range includes the upper bound;
037     *   must be false if not bounded above
038     * @param upperBound Upper bound, or null if not bounded above
039     */
040    public RangeColumnPredicate(
041        RolapStar.Column column,
042        boolean lowerInclusive,
043        ValueColumnPredicate lowerBound,
044        boolean upperInclusive,
045        ValueColumnPredicate upperBound)
046    {
047        super(column);
048        assert lowerBound == null
049            || lowerBound.getConstrainedColumn() == column;
050        assert !(lowerBound == null && lowerInclusive);
051        assert upperBound == null
052            || upperBound.getConstrainedColumn() == column;
053        assert !(upperBound == null && upperInclusive);
054        this.lowerInclusive = lowerInclusive;
055        this.lowerBound = lowerBound;
056        this.upperInclusive = upperInclusive;
057        this.upperBound = upperBound;
058    }
059
060    public int hashCode() {
061        int h = lowerInclusive ? 2 : 1;
062        h = 31 * h + lowerBound.hashCode();
063        h = 31 * h + (upperInclusive ? 2 : 1);
064        h = 31 * h + upperBound.hashCode();
065        return h;
066    }
067
068    public boolean equals(Object obj) {
069        if (obj instanceof RangeColumnPredicate) {
070            RangeColumnPredicate that =
071                (RangeColumnPredicate) obj;
072            return this.lowerInclusive == that.lowerInclusive
073                && this.lowerBound.equals(that.lowerBound)
074                && this.upperInclusive == that.upperInclusive
075                && this.upperBound.equals(that.upperBound);
076        } else {
077            return false;
078        }
079    }
080
081    public void values(Collection<Object> collection) {
082        // Besides the end points, don't know what values may be in the range.
083        // FIXME: values() is only a half-useful method. Replace it?
084        throw new UnsupportedOperationException();
085    }
086
087    public boolean evaluate(Object value) {
088        if (lowerBound != null) {
089            int c =
090                ((Comparable<Object>) lowerBound.getValue()).compareTo(value);
091            if (lowerInclusive ? c > 0 : c >= 0) {
092                return false;
093            }
094        }
095        if (upperBound != null) {
096            int c =
097                ((Comparable<Object>) upperBound.getValue()).compareTo(value);
098            if (upperInclusive ? c < 0 : c <= 0) {
099                return false;
100            }
101        }
102        return true;
103    }
104
105    public void describe(StringBuilder buf) {
106        buf.append("Range(");
107        if (lowerBound == null) {
108            buf.append("unbounded");
109        } else {
110            lowerBound.describe(buf);
111            if (lowerInclusive) {
112                buf.append(" inclusive");
113            }
114        }
115        buf.append(" to ");
116        if (upperBound == null) {
117            buf.append("unbounded");
118        } else {
119            upperBound.describe(buf);
120            if (upperInclusive) {
121                buf.append(" inclusive");
122            }
123        }
124        buf.append(")");
125    }
126
127    public Overlap intersect(StarColumnPredicate predicate) {
128        throw new UnsupportedOperationException();
129    }
130
131    public boolean mightIntersect(StarPredicate other) {
132        if (other instanceof ValueColumnPredicate) {
133            return evaluate(((ValueColumnPredicate) other).getValue());
134        } else {
135            // It MIGHT intersect. (Might not.)
136            // todo: Handle case 'other instanceof RangeColumnPredicate'
137            return true;
138        }
139    }
140
141    public StarColumnPredicate minus(StarPredicate predicate) {
142        assert predicate != null;
143        // todo: Implement some common cases, such as Range minus Range, and
144        // Range minus true/false
145        return new MinusStarPredicate(
146            this, (StarColumnPredicate) predicate);
147    }
148
149    public StarColumnPredicate cloneWithColumn(RolapStar.Column column) {
150        return new RangeColumnPredicate(
151            column, lowerInclusive, lowerBound, upperInclusive, upperBound);
152    }
153
154    public ValueColumnPredicate getLowerBound() {
155        return lowerBound;
156    }
157
158    public boolean getLowerInclusive() {
159        return lowerInclusive;
160    }
161
162    public ValueColumnPredicate getUpperBound() {
163        return upperBound;
164    }
165
166    public boolean getUpperInclusive() {
167        return upperInclusive;
168    }
169}
170
171// End RangeColumnPredicate.java