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