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) 2007-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.rolap.agg;
011
012import mondrian.olap.Util;
013import mondrian.rolap.*;
014import mondrian.rolap.sql.SqlQuery;
015
016import java.util.*;
017
018/**
019 * Base class for {@link AndPredicate} and {@link OrPredicate}.
020 *
021 * @see mondrian.rolap.agg.ListColumnPredicate
022 *
023 * @author jhyde
024 */
025public abstract class ListPredicate implements StarPredicate {
026    protected final List<StarPredicate> children =
027        new ArrayList<StarPredicate>();
028
029    /**
030     * Hash map of children predicates, keyed off of the hash code of each
031     * child.  Each entry in the map is a list of predicates matching that
032     * hash code.
033     */
034    private HashMap<Integer, List<StarPredicate>> childrenHashMap;
035
036    /**
037     * Pre-computed hash code for this list column predicate
038     */
039    private int hashValue;
040
041    protected final List<RolapStar.Column> columns;
042
043    private BitKey columnBitKey = null;
044
045    protected ListPredicate(List<StarPredicate> predicateList) {
046        childrenHashMap = null;
047        hashValue = 0;
048        // Ensure that columns are sorted by bit-key, for determinacy.
049        final SortedSet<RolapStar.Column> columnSet =
050            new TreeSet<RolapStar.Column>(RolapStar.Column.COMPARATOR);
051        for (StarPredicate predicate : predicateList) {
052            children.add(predicate);
053            columnSet.addAll(predicate.getConstrainedColumnList());
054        }
055        columns = new ArrayList<RolapStar.Column>(columnSet);
056    }
057
058    public List<RolapStar.Column> getConstrainedColumnList() {
059        return columns;
060    }
061
062    public BitKey getConstrainedColumnBitKey() {
063        if (columnBitKey == null) {
064            for (StarPredicate predicate : children) {
065                if (columnBitKey == null) {
066                    columnBitKey =
067                        predicate.getConstrainedColumnBitKey().copy();
068                } else {
069                    columnBitKey =
070                        columnBitKey.or(predicate.getConstrainedColumnBitKey());
071                }
072            }
073        }
074        return columnBitKey;
075    }
076
077    public List<StarPredicate> getChildren() {
078        return children;
079    }
080
081    public int hashCode() {
082        // Don't use the default list hashcode because we want a hash code
083        // that's not order dependent
084        if (hashValue == 0) {
085            hashValue = 37;
086            for (StarPredicate child : children) {
087                int childHashCode = child.hashCode();
088                if (childHashCode != 0) {
089                    hashValue *= childHashCode;
090                }
091            }
092            hashValue ^= children.size();
093        }
094        return hashValue;
095    }
096
097    public boolean equalConstraint(StarPredicate that) {
098        boolean isEqual =
099            that instanceof ListPredicate
100            && getConstrainedColumnBitKey().equals(
101                that.getConstrainedColumnBitKey());
102
103        if (isEqual) {
104            ListPredicate thatPred = (ListPredicate) that;
105            if (getOp() != thatPred.getOp()
106                || getChildren().size() != thatPred.getChildren().size())
107            {
108                isEqual = false;
109            }
110
111            if (isEqual) {
112                // Create a hash map of the children predicates, if not
113                // already done
114                if (childrenHashMap == null) {
115                    childrenHashMap =
116                        new HashMap<Integer, List<StarPredicate>>();
117                    for (StarPredicate thisChild : getChildren()) {
118                        Integer key = new Integer(thisChild.hashCode());
119                        List<StarPredicate> predList = childrenHashMap.get(key);
120                        if (predList == null) {
121                            predList = new ArrayList<StarPredicate>();
122                        }
123                        predList.add(thisChild);
124                        childrenHashMap.put(key, predList);
125                    }
126                }
127
128                // Loop through thatPred's children predicates.  There needs
129                // to be a matching entry in the hash map for each child
130                // predicate.
131                for (StarPredicate thatChild : thatPred.getChildren()) {
132                    List<StarPredicate> predList =
133                        childrenHashMap.get(thatChild.hashCode());
134                    if (predList == null) {
135                        isEqual = false;
136                        break;
137                    }
138                    boolean foundMatch = false;
139                    for (StarPredicate pred : predList) {
140                        if (thatChild.equalConstraint(pred)) {
141                            foundMatch = true;
142                            break;
143                        }
144                    }
145                    if (!foundMatch) {
146                        isEqual = false;
147                        break;
148                    }
149                }
150            }
151        }
152
153        return isEqual;
154    }
155
156    public StarPredicate minus(StarPredicate predicate) {
157        throw Util.needToImplement(this);
158    }
159
160    public void toSql(SqlQuery sqlQuery, StringBuilder buf) {
161        if (children.size() == 1) {
162            children.get(0).toSql(sqlQuery, buf);
163        } else {
164            int k = 0;
165            buf.append("(");
166            for (StarPredicate child : children) {
167                if (k++ > 0) {
168                    buf.append(" ").append(getOp()).append(" ");
169                }
170                child.toSql(sqlQuery, buf);
171            }
172            buf.append(")");
173        }
174    }
175
176    protected abstract String getOp();
177
178    public void describe(StringBuilder buf) {
179        buf.append(getOp()).append("(");
180        int k = 0;
181        for (StarPredicate child : children) {
182            if (k++ > 0) {
183                buf.append(", ");
184            }
185            buf.append(child);
186        }
187        buf.append(')');
188    }
189
190
191    public String toString() {
192        final StringBuilder buf = new StringBuilder();
193        describe(buf);
194        return buf.toString();
195    }
196}
197
198// End ListPredicate.java