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) 2011-2011 Pentaho and others 008// All Rights Reserved. 009*/ 010package mondrian.util; 011 012import java.util.List; 013import java.util.Map; 014 015/** 016 * Variation of Luc's SpatialValueTree. 017 * 018 * <p>Notes: 019 * 020 * <p>1. For clarity, I got rid of the of template args. We might add back 021 * say '<Q extends SpatialRegionRequest, 022 * R extends SpatialRegion, 023 * D extends SpatialDimension>' if it saves a lot of casting in client code. 024 * 025 * <p>2. It might be useful to introduce an interface SpatialDimensionality 026 * that represents a set of dimensions. It will certainly be useful for the 027 * implementation; however, it is not clear that it is necessary in the API. 028 * 029 * <p>3. In mondrian a 'region value' might be a struct that contains 030 * a weak reference to a segment in cache, and a reference to a segment 031 * in an external cache. Either of the references can be null. 032 * 033 * <p>4. In a SpatialRegionRequest, it might be useful to coalesce a list 034 * of values into a range (e.g. {2009, 2010, 2011} would be [2009, 2011]). 035 * 036 * <p>Mondrian requests currently have raw values, but converting those raw 037 * values into ranges might allow the tree to be more efficient. 038 * 039 * <p>Ranges could be recognized if we know all allowable values. For example, 040 * if we know the set of states, we can say that {"CA", "CO", "CT" 041 * "DE"} is equivalent to ["CA", "DE"]. 042 * A range could be recognized if the data type is discreet. For example, 043 * {2009, 2010, 2011} is equivalent to [2009, 2011] because year is an int. 044 * 045 * <p>5. For performance, and atomicity of operations, 046 * some of the methods might 047 * contain a callback (e.g. {@link #rollup(java.util.Map)} could take a 048 * functor that is applied when a rollup is found. But we can validate the 049 * design introducing functors just yet. 050 * 051 * @author jhyde 052 */ 053public interface SpatialValueTree2 054{ 055 /** 056 * Returns a list of all the dimensions present in this tree. 057 * 058 * @return A list of dimensions. 059 */ 060 List<SpatialDimension> getDimensions(); 061 062 /** 063 * Stores a region in this tree. 064 * 065 * <p>REVIEW: What is the behavior if there is another region with an equal 066 * {@link SpatialRegionRequest}? 067 * 068 * @param region Region 069 */ 070 void add(SpatialRegion region); 071 072 /** 073 * Removes a region from the tree. 074 * 075 * @param region The region key of the values to clear. 076 */ 077 void clear(SpatialRegion region); 078 079 /** 080 * Looks up all the values registered in nodes intersecting 081 * with the provided region key. 082 * 083 * <p>REVIEW: Does it have to PRECISELY fulfill the request, or can it 084 * be a superset? Does the return value's {@link SpatialRegion#getRequest()} 085 * method return the {@code regionRequest} parameter? (That would be 086 * expensive to implement.) Do we even need this method, or is 087 * 088 * @param regionRequest The region key inside of which to search for 089 * value nodes. 090 * 091 * @return Region fulfilling the request, or null 092 */ 093 SpatialRegion get(SpatialRegionRequest regionRequest); 094 095 /** 096 * Returns a region containing a given cell. 097 * 098 * <p>If there are multiple regions that contain the cell, returns just 099 * one of them. If there are no regions, returns null. 100 * 101 * @param coordinates Coordinates of cell - a value for each constraining 102 * dimension 103 * @return a region that contains the cell, or null if there is no such 104 */ 105 SpatialRegion getRegionContaining( 106 Map<SpatialDimension, Object> coordinates); 107 108 /** 109 * Returns a collection of regions that can be combined to compute a given 110 * cell. 111 * 112 * <p>The regions are not necessarily disjoint. Nor are they necessarily 113 * of the same dimensionality. 114 * 115 * <p>If multiple rollups are possible, gives the set of regions with the 116 * smallest number of cells. (It is cheaper to roll up from regions that are 117 * already highly aggregated.) 118 * 119 * <p>If no rollup is possible, returns null. 120 * 121 * @param dimensions Coordinates of cell 122 * @return List of spatial regions to roll up; or null 123 */ 124 List<SpatialRegion> rollup( 125 Map<SpatialDimension, Object> dimensions); 126 127 public interface SpatialDimension 128 { 129 /** 130 * Ordinal of dimension. Dimension ordinals are unique and contiguous 131 * within a tree. 132 * 133 * @return ordinal of dimension 134 */ 135 int ordinal(); 136 137 /** 138 * Declares that a particular dimension has a finite set of values. With 139 * this information, an implementation may be able to perform rollups 140 * that it would not otherwise. 141 * 142 * <p>For example, if the user asks for cell (year=2010, measure=sales) 143 * and the tree has regions (year=2010, gender=M, measure=sales) and 144 * (year=2010, gender=F, measure=sales) then the tree can compute the 145 * cell only if it knows that the only values of gender are {M, F}. 146 * 147 * <p>Returns null if the set of values is unbounded, not known, or 148 * too large to be any use in optimizing. 149 * 150 * <p>The values are distinct and sorted (per 151 * {@link Comparable}, and all not null. If you wish to represent a 152 * null value, use a dummy object. 153 * 154 * <p>The client must not modify the array. 155 * 156 * @return set of values that this dimension may have 157 */ 158 Object[] getValues(); 159 } 160 161 /** 162 * A request for a region. The request has a number of dimensions, a 163 * subset of the dimensions of the tree, and for each dimension it either 164 * requests a list of values or requests all values. 165 */ 166 public interface SpatialRegionRequest 167 { 168 /** 169 * Provides a list of the dimensions included in this 170 * region. 171 * 172 * @return Dimensions of this region 173 */ 174 List<SpatialDimension> getDimensions(); 175 176 /** 177 * Provides an array of objects describing this region's 178 * bounds within the specified dimension's axis. 179 * 180 * <p>The values are unique and are sorted. The client must not modify 181 * the array. 182 * 183 * <p>A null array means wildcard. The caller wanted all possible 184 * values of this dimension. 185 * 186 * @param dimension Dimension 187 * @return An array of the bounds touched by this region. 188 */ 189 Object[] getValues(SpatialDimension dimension); 190 191 /** 192 * Returns whether a request might contain a particular cell. 193 * 194 * @param coordinates Value for each dimension of cell's coordinates. 195 * 196 * @return Whether cell is within the bounds of this request 197 */ 198 boolean mightContainCell(Map<SpatialDimension, Object> coordinates); 199 } 200 201 public interface SpatialRegion 202 { 203 /** 204 * Returns the specification of this region. 205 * 206 * @return Region request 207 */ 208 SpatialRegionRequest getRequest(); 209 210 /** 211 * Returns the value of a cell. 212 * 213 * <p>Assumes that this region body is valid for the request. (That is, 214 * {@link SpatialRegionRequest#mightContainCell(java.util.Map)} would 215 * return true.) For example, suppose that the region request was 216 * 217 * <blockquote>(gender=M, year=any, measure=sales)</blockquote> 218 * 219 * this region body is 220 * 221 * <blockquote>(gender=M, year={2009, 2010}, measure=sales)</blockquote> 222 * 223 * and the cell coordinates are 224 * 225 * <blockquote>(gender=F, year=2009, measure=sales)</blockquote> 226 * 227 * <p>Because the coordinate 'gender=F' falls outside the region 228 * request, behavior is unspecified. The implementation might 229 * return null, throw an error, or return a value for the cell 230 * (gender=M, year=2009, measure=sales); any of these behaviors would 231 * be valid. 232 * 233 * @param coordinates Value for each dimension 234 * 235 * @return cell value 236 */ 237 Object getCellValue(Map<SpatialDimension, Object> coordinates); 238 239 /** 240 * Version of {@link #getCellValue(java.util.Map)} optimized for 241 * {@code int} values. 242 * 243 * <p>If value is null, writes 'true' into wasNull[0] and returns 0. 244 * Otherwise, does not modify wasNull. 245 * 246 * @param coordinates Value of each dimension 247 * @param wasNull 1-element array to be informed if value was null 248 * @return Value, or 0 if value is null 249 */ 250 int getCellValueInt( 251 Map<SpatialDimension, Object> coordinates, 252 boolean[] wasNull); 253 254 /** 255 * Version of {@link #getCellValue(java.util.Map)} optimized for 256 * {@code double} values. 257 * 258 * <p>If value is null, writes 'true' into wasNull[0] and returns 0. 259 * Otherwise, does not modify wasNull. 260 * 261 * @param coordinates Value of each dimension 262 * @param wasNull 1-element array to be informed if value was null 263 * @return Value, or 0 if value is null 264 */ 265 double getCellValueDouble( 266 Map<SpatialDimension, Object> coordinates, 267 boolean[] wasNull); 268 } 269} 270 271// End SpatialValueTree2.java