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-2012 Pentaho and others
008// All Rights Reserved.
009*/
010package mondrian.spi;
011
012import mondrian.olap.Util;
013import mondrian.util.ArraySortedSet;
014
015import java.io.Serializable;
016import java.util.SortedSet;
017
018
019/**
020 * Constrained columns are part of the SegmentHeader and SegmentCache.
021 * They uniquely identify a constrained column within a segment.
022 * Each segment can have many constrained columns. Each column can
023 * be constrained by multiple values at once (similar to a SQL in()
024 * predicate).
025 *
026 * <p>They are immutable and serializable.
027 */
028public class SegmentColumn implements Serializable {
029    private static final long serialVersionUID = -5227838916517784720L;
030    public final String columnExpression;
031    public final int valueCount;
032    public final SortedSet<Comparable> values;
033    private final int hashCode;
034
035    /**
036     * Creates a SegmentColumn.
037     *
038     * @param columnExpression SQL expression for the column. Unique within the
039     *     star schema, including accesses to the same physical column via
040     *     different join paths.
041     *
042     * @param valueCount Number of distinct values of this column in the
043     *     database. For these purposes, null is counted as a value. If there
044     *     are N distinct values of the column, and we have a collection of
045     *     segments that cover N values, then Mondrian assumes that it is safe
046     *     to roll up.
047     *
048     * @param valueList List of values to constrain the
049     *     column to, or null if unconstrained. Values must be
050     *     {@link Comparable} and immutable. For example, Integer, Boolean,
051     *     String or Double.
052     */
053    public SegmentColumn(
054        String columnExpression,
055        int valueCount,
056        SortedSet<Comparable> valueList)
057    {
058        this.columnExpression = columnExpression;
059        this.valueCount = valueCount;
060        this.values = valueList;
061        this.hashCode = computeHashCode();
062    }
063
064    private int computeHashCode() {
065        return Util.hash(
066            this.columnExpression.hashCode(),
067            this.values);
068    }
069
070    /**
071     * Merges this column with another
072     * resulting in another whose values are super set of both.
073     */
074    public SegmentColumn merge(SegmentColumn col) {
075        assert col != null;
076        assert col.columnExpression.equals(this.columnExpression);
077
078        // If any values are wildcard, the merged result is a wildcard.
079        if (this.values == null || col.values == null) {
080            return new SegmentColumn(
081                columnExpression,
082                valueCount,
083                null);
084        }
085
086        return new SegmentColumn(
087            columnExpression,
088            valueCount,
089            ((ArraySortedSet) this.values).merge(
090                (ArraySortedSet) col.values));
091    }
092
093    /**
094     * Returns the column expression of this constrained column.
095     * @return A column expression.
096     */
097    public String getColumnExpression() {
098        return columnExpression;
099    }
100
101    /**
102     * Returns an array of predicate values for this column.
103     * @return An array of object values.
104     */
105    public SortedSet<Comparable> getValues() {
106        return values;
107    }
108
109    @Override
110    public boolean equals(Object obj) {
111        if (!(obj instanceof SegmentColumn)) {
112            return false;
113        }
114        SegmentColumn that = (SegmentColumn) obj;
115        if (this.values == null && that.values == null) {
116            return true;
117        }
118        return this.columnExpression.equals(that.columnExpression)
119            && Util.equals(this.values, that.values);
120    }
121
122    @Override
123    public int hashCode() {
124        return hashCode;
125    }
126
127    /**
128     * Returns the number of distinct values that occur for this column in the
129     * database.
130     *
131     * <p>Mondrian uses this to know that it can safely combine multiple
132     * segments to roll up. For example, if for the "quarter" column, one
133     * segment has values {"Q1", "Q2"} and another has values {"Q3", "Q4"},
134     * and Mondrian knows that there are 4 values, then it can roll up.
135     *
136     * <p>If this method returns a value that is too low, Mondrian may generate
137     * incorrect results. If you don't know the number of values, return -1.</p>
138     *
139     * @return Number of distinct values, including null, that occur for this
140     * column
141     */
142    public int getValueCount() {
143        return valueCount;
144    }
145}
146
147// End SegmentColumn.java