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.RolapUtil; 015import mondrian.rolap.StarColumnPredicate; 016import mondrian.util.ArraySortedSet; 017import mondrian.util.Pair; 018 019import java.util.*; 020 021/** 022 * Collection of values of one of the columns that parameterizes a 023 * {@link Segment}. 024 */ 025public class SegmentAxis { 026 027 /** 028 * Constraint on the keys in this Axis. Never null. 029 */ 030 final StarColumnPredicate predicate; 031 032 /** 033 * Whether predicate is always true. 034 */ 035 private final boolean predicateAlwaysTrue; 036 037 private final Set<Object> predicateValues; 038 039 /** 040 * Map holding the position of each key value. 041 * 042 * <p>TODO: Hold keys in a sorted array, then deduce ordinal by doing 043 * binary search. 044 */ 045 private final Map<Comparable, Integer> mapKeyToOffset; 046 047 /** 048 * Actual key values retrieved. 049 */ 050 private final Comparable[] keys; 051 052 private static final Integer ZERO = Integer.valueOf(0); 053 private static final Integer ONE = Integer.valueOf(1); 054 private static final Comparable[] NO_COMPARABLES = new Comparable[0]; 055 056 /** 057 * Internal constructor. 058 */ 059 private SegmentAxis( 060 StarColumnPredicate predicate, 061 Comparable[] keys, 062 boolean safe) 063 { 064 this.predicate = predicate; 065 this.predicateAlwaysTrue = 066 predicate instanceof LiteralStarPredicate 067 && ((LiteralStarPredicate) predicate).getValue(); 068 this.predicateValues = predicateValueSet(predicate); 069 if (keys.length == 0) { 070 // Optimize the case where axis is empty. Not that infrequent: 071 // it records that mondrian has looked in the database and found 072 // nothing. 073 this.keys = NO_COMPARABLES; 074 this.mapKeyToOffset = Collections.emptyMap(); 075 } else { 076 this.keys = keys; 077 mapKeyToOffset = 078 new HashMap<Comparable, Integer>(keys.length * 3 / 2); 079 for (int i = 0; i < keys.length; i++) { 080 mapKeyToOffset.put(keys[i], i); 081 } 082 } 083 assert predicate != null; 084 assert safe || Util.isSorted(Arrays.asList(keys)); 085 } 086 087 private static Set<Object> predicateValueSet( 088 StarColumnPredicate predicate) 089 { 090 if (!(predicate instanceof ListColumnPredicate)) { 091 return null; 092 } 093 ListColumnPredicate listColumnPredicate = 094 (ListColumnPredicate) predicate; 095 final List<StarColumnPredicate> predicates = 096 listColumnPredicate.getPredicates(); 097 if (predicates.size() < 10) { 098 return null; 099 } 100 final HashSet<Object> set = new HashSet<Object>(); 101 for (StarColumnPredicate subPredicate : predicates) { 102 if (subPredicate instanceof ValueColumnPredicate) { 103 ValueColumnPredicate valueColumnPredicate = 104 (ValueColumnPredicate) subPredicate; 105 valueColumnPredicate.values(set); 106 } else { 107 return null; 108 } 109 } 110 return set; 111 } 112 113 /** 114 * Creates a SegmentAxis populated with an array of key values. The key 115 * values must be sorted. 116 * 117 * @param predicate Predicate defining which keys should appear on 118 * axis. (If a key passes the predicate but 119 * is not in the list, every cell with that 120 * key is assumed to have a null value.) 121 * @param keys Keys 122 */ 123 SegmentAxis(StarColumnPredicate predicate, Comparable[] keys) { 124 this(predicate, keys, false); 125 } 126 127 /** 128 * Creates a SegmentAxis populated with a set of key values. 129 * 130 * @param predicate Predicate defining which keys should appear on 131 * axis. (If a key passes the predicate but 132 * is not in the list, every cell with that 133 * key is assumed to have a null value.) 134 * @param keySet Set of distinct key values, sorted 135 * @param hasNull Whether the axis contains the null value, in addition 136 * to the values in <code>valueSet</code> 137 */ 138 public SegmentAxis( 139 StarColumnPredicate predicate, 140 SortedSet<Comparable> keySet, 141 boolean hasNull) 142 { 143 this(predicate, toArray(keySet, hasNull), true); 144 } 145 146 private static Comparable[] toArray( 147 SortedSet<Comparable> keySet, 148 boolean hasNull) 149 { 150 int size = keySet.size(); 151 if (hasNull) { 152 size++; 153 } 154 Comparable[] keys = keySet.toArray(new Comparable[size]); 155 if (hasNull) { 156 keys[size - 1] = RolapUtil.sqlNullValue; 157 } 158 return keys; 159 } 160 161 final StarColumnPredicate getPredicate() { 162 return predicate; 163 } 164 165 final Comparable[] getKeys() { 166 return keys; 167 } 168 169 final int getOffset(Comparable key) { 170 if (keys.length == 1) { 171 return keys[0].equals(key) ? 0 : -1; 172 } 173 Integer ordinal = mapKeyToOffset.get(key); 174 if (ordinal == null) { 175 return -1; 176 } 177 return ordinal; 178 } 179 180 /** 181 * Returns whether this axis contains a given key, or would contain it 182 * if it existed. 183 * 184 * <p>For example, if this axis is unconstrained, then this method 185 * returns <code>true</code> for any value. 186 * 187 * @param key Key 188 * @return Whether this axis would contain <code>key</code> 189 */ 190 public final boolean wouldContain(Object key) { 191 return predicateAlwaysTrue 192 || (predicateValues != null 193 ? predicateValues.contains(key) 194 : predicate.evaluate(key)); 195 } 196 197 /** 198 * Returns how many of this SegmentAxis's keys match a given constraint. 199 * 200 * @param predicate Predicate 201 * @return How many keys match constraint 202 */ 203 public int getMatchCount(StarColumnPredicate predicate) { 204 int matchCount = 0; 205 for (Object key : keys) { 206 if (predicate.evaluate(key)) { 207 ++matchCount; 208 } 209 } 210 return matchCount; 211 } 212 213 @SuppressWarnings({"unchecked"}) 214 public Pair<SortedSet<Comparable>, Boolean> getValuesAndIndicator() { 215 if (keys.length > 0 216 && keys[keys.length - 1] == RolapUtil.sqlNullValue) 217 { 218 return (Pair) Pair.of( 219 new ArraySortedSet(keys, 0, keys.length - 1), 220 Boolean.TRUE); 221 } else { 222 return (Pair) Pair.of( 223 new ArraySortedSet(keys), 224 Boolean.FALSE); 225 } 226 } 227} 228 229// End SegmentAxis.java