001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/agg/MemberTuplePredicate.java#6 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2007-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.rolap.agg;
011    
012    import mondrian.olap.Util;
013    import mondrian.rolap.RolapCube;
014    import mondrian.rolap.RolapCubeLevel;
015    import mondrian.rolap.RolapCubeMember;
016    import mondrian.rolap.StarPredicate;
017    import mondrian.rolap.RolapStar;
018    import mondrian.rolap.RolapLevel;
019    import mondrian.rolap.RolapMember;
020    import mondrian.rolap.BitKey;
021    import mondrian.rolap.sql.SqlQuery;
022    
023    import java.util.List;
024    import java.util.ArrayList;
025    import java.util.Arrays;
026    
027    /**
028     * Predicate which constrains a column to a particular member, or a range
029     * above or below a member, or a range between two members.
030     *
031     * @author jhyde
032     * @version $Id: //open/mondrian/src/main/mondrian/rolap/agg/MemberTuplePredicate.java#6 $
033     */
034    public class MemberTuplePredicate implements StarPredicate {
035        private final Bound[] bounds;
036        private final List<RolapStar.Column> columnList;
037        private BitKey columnBitKey;
038    
039        /**
040         * Creates a MemberTuplePredicate which evaluates to true for a given
041         * range of members.
042         *
043         * <p>The range can be open above or below, but at least one bound is
044         * required.
045         *
046         * @param baseCube base cube for virtual members
047         * @param lower Member which forms the lower bound, or null if range is
048         *   open below
049         * @param lowerStrict Whether lower bound of range is strict
050         * @param upper Member which forms the upper bound, or null if range is
051         *   open above
052         * @param upperStrict Whether upper bound of range is strict
053         */
054        public MemberTuplePredicate(
055            RolapCube baseCube,
056            RolapMember lower,
057            boolean lowerStrict,
058            RolapMember upper,
059            boolean upperStrict)
060        {
061            columnBitKey = null;
062            this.columnList =
063                computeColumnList(lower != null ? lower : upper, baseCube);
064    
065            if (lower == null) {
066                assert upper != null;
067                bounds = new Bound[] {
068                    new Bound(upper, upperStrict ? RelOp.LT : RelOp.LE)
069                };
070            } else if (upper == null) {
071                bounds = new Bound[] {
072                    new Bound(lower, lowerStrict ? RelOp.GT : RelOp.GE)
073                };
074            } else {
075                bounds = new Bound[] {
076                    new Bound(lower, lowerStrict ? RelOp.GT : RelOp.GE),
077                    new Bound(upper, upperStrict ? RelOp.LT : RelOp.LE)
078                };
079            }
080        }
081    
082        /**
083         * Creates a MemberTuplePredicate which evaluates to true for a given
084         * member.
085         *
086         * @param baseCube base cube for virtual members
087         * @param member Member
088         */
089        public MemberTuplePredicate(RolapCube baseCube, RolapCubeMember member) {
090            this.columnList = computeColumnList(member, baseCube);
091    
092            this.bounds = new Bound[] {
093                new Bound(member, RelOp.EQ)
094            };
095        }
096    
097        public int hashCode() {
098            return this.columnList.hashCode() * 31 +
099                Arrays.hashCode(this.bounds) * 31;
100        }
101    
102        public boolean equals(Object obj) {
103            if (obj instanceof MemberTuplePredicate) {
104                MemberTuplePredicate that =
105                    (MemberTuplePredicate) obj;
106                return this.columnList.equals(that.columnList) &&
107                    Arrays.equals(this.bounds, that.bounds);
108            } else {
109                return false;
110            }
111        }
112    
113        private List<RolapStar.Column> computeColumnList(
114            RolapMember member,
115            RolapCube baseCube)
116        {
117            List<RolapStar.Column> columnList = new ArrayList<RolapStar.Column>();
118            while (true) {
119                RolapLevel level = member.getLevel();
120                RolapStar.Column column = null;
121                if (level instanceof RolapCubeLevel) {
122                    column = ((RolapCubeLevel)level)
123                                    .getBaseStarKeyColumn(baseCube);
124                } else {
125                    (new Exception()).printStackTrace();
126                }
127    
128                if (columnBitKey == null) {
129                    columnBitKey =
130                        BitKey.Factory.makeBitKey(
131                                column.getStar().getColumnCount());
132                    columnBitKey.clear();
133                }
134                columnBitKey.set(column.getBitPosition());
135                columnList.add(0, column);
136                if (level.isUnique()) {
137                    return columnList;
138                }
139                member = member.getParentMember();
140            }
141        }
142    
143        /**
144         * Returns a list of constrained columns.
145         *
146         * @return List of constrained columns
147         */
148        public List<RolapStar.Column> getConstrainedColumnList() {
149            return columnList;
150        }
151    
152        public BitKey getConstrainedColumnBitKey() {
153            return columnBitKey;
154        }
155    
156        public boolean equalConstraint(StarPredicate that) {
157            throw new UnsupportedOperationException();
158        }
159    
160        public StarPredicate minus(StarPredicate predicate) {
161            throw new UnsupportedOperationException();
162        }
163    
164        public StarPredicate or(StarPredicate predicate) {
165            throw new UnsupportedOperationException();
166        }
167    
168        public StarPredicate and(StarPredicate predicate) {
169            throw new UnsupportedOperationException();
170        }
171    
172        /**
173         * Evaluates a constraint against a list of values.
174         *
175         * @param valueList List of values, one for each constrained column
176         * @return Whether constraint holds for given set of values
177         */
178        public boolean evaluate(List<Object> valueList) {
179            for (Bound bound : bounds) {
180                for (int k = 0; k < bound.values.length; ++k) {
181                    Object value = valueList.get(k);
182                    if (value == WILDCARD) {
183                        return false;
184                    }
185                    Object boundValue = bound.values[k];
186                    RelOp relOp = bound.relOps[k];
187                    int c = Util.compareKey(value, boundValue);
188                    switch (relOp) {
189                    case GT:
190                        if (c > 0) {
191                            break;
192                        } else {
193                            return false;
194                        }
195                    case GE:
196                        if (c > 0) {
197                            return true;
198                        } else if (c == 0) {
199                            break;
200                        } else {
201                            return false;
202                        }
203                    case LT:
204                        if (c < 0) {
205                            break;
206                        } else {
207                            return false;
208                        }
209                    case LE:
210                        if (c < 0) {
211                            return true;
212                        } if (c == 0) {
213                            break;
214                        } else {
215                            return false;
216                        }
217                    }
218                }
219            }
220            return true;
221        }
222    
223        public void describe(StringBuilder buf) {
224            int k = 0;
225            for (Bound bound : bounds) {
226                if (k++ > 0) {
227                    buf.append(" AND ");
228                }
229                buf.append(bound.relOps[bound.relOps.length - 1].getOp());
230                buf.append(' ');
231                buf.append(bound.member);
232            }
233        }
234    
235        private enum RelOp {
236            LT("<"),
237            LE("<="),
238            GT(">"),
239            GE(">="),
240            EQ("=");
241    
242            private final String op;
243    
244            RelOp(String op) {
245                this.op = op;
246            }
247    
248            String getOp() {
249                return op;
250            }
251    
252            /**
253             * If this is a strict operator (LT, GT) returns the non-strict
254             * equivalent (LE, GE); otherwise returns this operator.
255             *
256             * @return less strict version of this operator
257             */
258            public RelOp desctrict() {
259                switch (this) {
260                case GT:
261                    return RelOp.GE;
262                case LT:
263                    return RelOp.LE;
264                default:
265                    return this;
266                }
267            }
268        }
269    
270        private static class Bound {
271            private final RolapMember member;
272            private final Object[] values;
273            private final RelOp[] relOps;
274    
275            Bound(RolapMember member, RelOp relOp) {
276                this.member = member;
277                List<Object> valueList = new ArrayList<Object>();
278                List<RelOp> relOpList = new ArrayList<RelOp>();
279                while (true) {
280                    valueList.add(0, member.getKey());
281                    relOpList.add(0, relOp);
282                    if (member.getLevel().isUnique()) {
283                        break;
284                    }
285                    member = member.getParentMember();
286                    relOp = relOp.desctrict();
287                }
288                this.values = valueList.toArray(new Object[valueList.size()]);
289                this.relOps = relOpList.toArray(new RelOp[relOpList.size()]);
290            }
291    
292    
293            public int hashCode() {
294                int h = member.hashCode();
295                h = h * 31 + Arrays.hashCode(values);
296                h = h * 31 + Arrays.hashCode(relOps);
297                return h;
298            }
299    
300            public boolean equals(Object obj) {
301                if (obj instanceof Bound) {
302                    Bound that = (Bound) obj;
303                    return this.member.equals(that.member) &&
304                        Arrays.equals(this.values, that.values) &&
305                        Arrays.equals(this.relOps, that.relOps);
306                } else {
307                    return false;
308                }
309            }
310        }
311    
312        public void toSql(SqlQuery sqlQuery, StringBuilder buf) {
313            throw Util.needToImplement(this);
314        }
315    }
316    
317    // End MemberTuplePredicate.java