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