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) 2005-2005 Julian Hyde
008// Copyright (C) 2005-2012 Pentaho and others
009// All Rights Reserved.
010*/
011package mondrian.rolap.agg;
012
013import mondrian.olap.Util;
014import mondrian.rolap.*;
015import mondrian.rolap.sql.SqlQuery;
016
017import java.util.*;
018
019/**
020 * Column context that an Aggregation is computed for.
021 *
022 * <p>Column context has two components:</p>
023 * <ul>
024 * <li>The column constraints which define the dimentionality of an
025 *   Aggregation</li>
026 * <li>An orthogonal context for which the measures are defined. This context
027 *   is sometimes referred to as the compound member predicates, and usually of
028 *   the shape:
029 *      <blockquote>OR(AND(column predicates))</blockquote></li>
030 * </ul>
031 *
032 * <p>Any column is only used in either column context or compound context, not
033 * both.</p>
034 *
035 * @author Rushan Chen
036 */
037public class AggregationKey
038{
039    /**
040     * This is needed because for a Virtual Cube: two CellRequests
041     * could have the same BitKey but have different underlying
042     * base cubes. Without this, one get the result in the
043     * SegmentArrayQuerySpec addMeasure Util.assertTrue being
044     * triggered (which is what happened).
045     */
046    private final RolapStar star;
047
048    private final BitKey constrainedColumnsBitKey;
049
050    /**
051     * List of StarPredicate (representing the predicate
052     * defining the compound member).
053     *
054     * <p>In sorted order of BitKey. This ensures that the map is deternimistic
055     * (otherwise different runs generate SQL statements in different orders),
056     * and speeds up comparison.
057     */
058    final List<StarPredicate> compoundPredicateList;
059
060    private int hashCode;
061
062    /**
063     * Creates an AggregationKey.
064     *
065     * @param request Cell request
066     */
067    public AggregationKey(CellRequest request) {
068        this.constrainedColumnsBitKey = request.getConstrainedColumnsBitKey();
069        this.star = request.getMeasure().getStar();
070        Map<BitKey, StarPredicate> compoundPredicateMap =
071            request.getCompoundPredicateMap();
072        this.compoundPredicateList =
073            compoundPredicateMap == null
074                ? Collections.<StarPredicate>emptyList()
075                : new ArrayList<StarPredicate>(compoundPredicateMap.values());
076    }
077
078    public final int computeHashCode() {
079        return computeHashCode(
080            constrainedColumnsBitKey,
081            star,
082            compoundPredicateList == null
083                ? null
084                : new AbstractList<BitKey>() {
085                    public BitKey get(int index) {
086                        return compoundPredicateList.get(index)
087                            .getConstrainedColumnBitKey();
088                    }
089
090                    public int size() {
091                        return compoundPredicateList.size();
092                    }
093                });
094    }
095
096    public static int computeHashCode(
097        BitKey constrainedColumnsBitKey,
098        RolapStar star,
099        Collection<BitKey> compoundPredicateBitKeys)
100    {
101        int retCode = constrainedColumnsBitKey.hashCode();
102        retCode = Util.hash(retCode, star);
103        return Util.hash(retCode, compoundPredicateBitKeys);
104    }
105
106    public int hashCode() {
107        if (hashCode == 0) {
108            // Compute hash code on first use. It is expensive to compute, and
109            // not always required.
110            hashCode = computeHashCode();
111        }
112        return hashCode;
113    }
114
115    public boolean equals(Object other) {
116        if (!(other instanceof AggregationKey)) {
117            return false;
118        }
119        final AggregationKey that = (AggregationKey) other;
120        return constrainedColumnsBitKey.equals(that.constrainedColumnsBitKey)
121            && star.equals(that.star)
122            && equal(compoundPredicateList, that.compoundPredicateList);
123    }
124
125    /**
126     * Returns whether two lists of compound predicates are equal.
127     *
128     * @param list1 First compound predicate map
129     * @param list2 Second compound predicate map
130     * @return Whether compound predicate maps are equal
131     */
132    static boolean equal(
133        final List<StarPredicate> list1,
134        final List<StarPredicate> list2)
135    {
136        if (list1 == null) {
137            return list2 == null;
138        }
139        if (list2 == null) {
140            return false;
141        }
142        final int size = list1.size();
143        if (size != list2.size()) {
144            return false;
145        }
146        for (int i = 0; i < size; i++) {
147            StarPredicate pred1 = list1.get(i);
148            StarPredicate pred2 = list2.get(i);
149            if (!pred1.equalConstraint(pred2)) {
150                return false;
151            }
152        }
153        return true;
154    }
155
156    public String toString() {
157        return
158            star.getFactTable().getTableName()
159            + " " + constrainedColumnsBitKey.toString()
160            + "\n"
161            + (compoundPredicateList == null
162                ? "{}"
163                : compoundPredicateList.toString());
164    }
165
166    /**
167     * Returns the bitkey of columns that constrain this aggregation.
168     *
169     * @return Bitkey of contraining columns
170     */
171    public final BitKey getConstrainedColumnsBitKey() {
172        return constrainedColumnsBitKey;
173    }
174
175    /**
176     * Returns the star.
177     *
178     * @return Star
179     */
180    public final RolapStar getStar() {
181        return star;
182    }
183
184    /**
185     * Returns the list of compound predicates.
186     *
187     * @return list of predicates
188     */
189    public List<StarPredicate> getCompoundPredicateList() {
190        return compoundPredicateList;
191    }
192
193    /**
194     * Returns a list of compound predicates, expressed as SQL strings.
195     *
196     * @param star Star
197     * @param compoundPredicateList Predicate list
198     * @return list of predicate strings
199     */
200    public static List<String> getCompoundPredicateStringList(
201        RolapStar star,
202        List<StarPredicate> compoundPredicateList)
203    {
204        if (compoundPredicateList.isEmpty()) {
205            return Collections.emptyList();
206        }
207        final List<String> cp = new ArrayList<String>();
208        final StringBuilder buf = new StringBuilder();
209        for (StarPredicate compoundPredicate : compoundPredicateList) {
210            buf.setLength(0);
211            SqlQuery query =
212                new SqlQuery(
213                    star.getSqlQueryDialect());
214            compoundPredicate.toSql(query, buf);
215            cp.add(buf.toString());
216        }
217        return cp;
218    }
219}
220
221// End AggregationKey.java