001 /* 002 // $Id: //open/mondrian/src/main/mondrian/rolap/CellKey.java#11 $ 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) 2001-2002 Kana Software, Inc. 007 // Copyright (C) 2001-2007 Julian Hyde and others 008 // All Rights Reserved. 009 // You must accept the terms of that agreement to use this software. 010 // 011 // jhyde, 10 August, 2001 012 */ 013 014 package mondrian.rolap; 015 016 import java.util.Arrays; 017 018 /** 019 * A <code>CellKey<code> is used as a key in maps which access cells by their 020 * position. 021 * 022 * <p>CellKey is also used within 023 * {@link mondrian.rolap.agg.SparseSegmentDataset} to store values within 024 * aggregations. 025 * 026 * <p>It is important that CellKey is memory-efficient, and that the 027 * {@link Object#hashCode} and {@link Object#equals} methods are extremely 028 * efficient. There are particular implementations for the 029 * most likely cases where the number of axes is 1, 2 and 3 as well as a general 030 * implementation. 031 * 032 * <p>To create a key, call the 033 * {@link mondrian.rolap.CellKey.Generator#newCellKey(int[])} method. 034 * 035 * @author jhyde 036 * @since 10 August, 2001 037 * @version $Id: //open/mondrian/src/main/mondrian/rolap/CellKey.java#11 $ 038 */ 039 public interface CellKey { 040 /** 041 * Returns the number of axes. 042 * 043 * @return number of axes 044 */ 045 int size(); 046 047 /** 048 * Returns the axis keys as an array. 049 * 050 * <p>Note: caller should treat the array as immutable. If the contents of 051 * the array are modified, behavior is unspecified. 052 * 053 * @return Array of axis keys 054 */ 055 int[] getOrdinals(); 056 057 /** 058 * This method make a copy of the int array parameter. 059 * Throws a RuntimeException if the int array size is not the 060 * size of the CellKey. 061 * 062 * @param pos Array of axis keys 063 */ 064 void setOrdinals(int[] pos); 065 066 /** 067 * Returns the <code>axis</code>th axis value. 068 * 069 * @param axis Axis ordinal 070 * @return Value of the <code>axis</code>th axis 071 * @throws ArrayIndexOutOfBoundsException if axis is out of range 072 */ 073 int getAxis(int axis); 074 075 /** 076 * Sets a given axis. 077 * 078 * @param axis Axis ordinal 079 * @param value Value 080 * @throws RuntimeException if axis parameter is larger than {@link #size()} 081 */ 082 void setAxis(int axis, int value); 083 084 /** 085 * Returns a mutable copy of this CellKey. 086 * 087 * @return Mutable copy 088 */ 089 CellKey copy(); 090 091 public class Generator { 092 /** 093 * Creates a CellKey with a given number of axes. 094 * 095 * @param size Number of axes 096 * @return new CellKey 097 */ 098 public static CellKey newCellKey(int size) { 099 switch (size) { 100 case 0: 101 return Zero.INSTANCE; 102 case 1: 103 return new One(0); 104 case 2: 105 return new Two(0, 0); 106 case 3: 107 return new Three(0, 0, 0); 108 default: 109 return new Many(new int[size]); 110 } 111 } 112 113 /** 114 * Creates a CellKey populated with the given coordinates. 115 * 116 * @param pos Coordinate array 117 * @return CellKey 118 */ 119 public static CellKey newCellKey(int[] pos) { 120 switch (pos.length) { 121 case 0: 122 return Zero.INSTANCE; 123 case 1: 124 return new One(pos[0]); 125 case 2: 126 return new Two(pos[0], pos[1]); 127 case 3: 128 return new Three(pos[0], pos[1], pos[2]); 129 default: 130 return new Many(pos.clone()); 131 } 132 } 133 134 /** 135 * Creates a CellKey based on a reference to the given coordinate 136 * array. Whenever the contents of the coordinate array change, the 137 * CellKey will also. 138 * 139 * @param pos Coordinate array 140 * @return CellKey 141 */ 142 public static CellKey newRefCellKey(int[] pos) { 143 // don't clone pos! 144 return new Many(pos); 145 } 146 147 /** 148 * Creates a CellKey implemented by an array to hold the coordinates, 149 * regardless of the size. This is used for testing only. 150 * 151 * @param size Number of coordinates 152 * @return CallKey 153 */ 154 static CellKey newManyCellKey(int size) { 155 return new Many(new int[size]); 156 } 157 } 158 159 public class Zero implements CellKey { 160 private static final int[] EMPTY_INT_ARRAY = new int[0]; 161 public static final Zero INSTANCE = new Zero(); 162 163 /** 164 * Use singleton {@link #INSTANCE}. 165 */ 166 private Zero() { 167 } 168 169 public Zero copy() { 170 // no need to make copy since there is no state 171 return this; 172 } 173 174 public boolean equals(Object o) { 175 if (o instanceof Zero) { 176 return true; 177 } else if (o instanceof Many) { 178 Many many = (Many) o; 179 return many.ordinals.length == 0; 180 } else { 181 return false; 182 } 183 } 184 185 public int hashCode() { 186 return 11; 187 } 188 189 public int size() { 190 return 0; 191 } 192 193 public int[] getOrdinals() { 194 return EMPTY_INT_ARRAY; 195 } 196 197 public void setOrdinals(int[] pos) { 198 if (pos.length != 0) { 199 throw new IllegalArgumentException(); 200 } 201 } 202 203 public int getAxis(int axis) { 204 throw new ArrayIndexOutOfBoundsException(axis); 205 } 206 207 public void setAxis(int axis, int value) { 208 throw new ArrayIndexOutOfBoundsException(axis); 209 } 210 } 211 212 public class One implements CellKey { 213 private int ordinal0; 214 215 /** 216 * Creates a One. 217 * 218 * @param ordinal0 Ordinate #0 219 */ 220 private One(int ordinal0) { 221 this.ordinal0 = ordinal0; 222 } 223 224 public int size() { 225 return 1; 226 } 227 228 public int[] getOrdinals() { 229 return new int[] {ordinal0}; 230 } 231 232 public void setOrdinals(int[] pos) { 233 if (pos.length != 1) { 234 throw new IllegalArgumentException(); 235 } 236 ordinal0 = pos[0]; 237 } 238 239 public int getAxis(int axis) { 240 switch (axis) { 241 case 0: 242 return ordinal0; 243 default: 244 throw new ArrayIndexOutOfBoundsException(axis); 245 } 246 } 247 248 public void setAxis(int axis, int value) { 249 switch (axis) { 250 case 0: 251 ordinal0 = value; 252 break; 253 default: 254 throw new ArrayIndexOutOfBoundsException(axis); 255 } 256 } 257 258 public One copy() { 259 return new One(ordinal0); 260 } 261 262 public boolean equals(Object o) { 263 // here we cheat, we know that all CellKey's will be the same size 264 if (o instanceof One) { 265 One other = (One) o; 266 return (this.ordinal0 == other.ordinal0); 267 } else if (o instanceof Many) { 268 Many many = (Many) o; 269 return many.ordinals.length == 1 && 270 many.ordinals[0] == this.ordinal0; 271 } else { 272 return false; 273 } 274 } 275 276 public String toString() { 277 return "(" + ordinal0 + ")"; 278 } 279 280 public int hashCode() { 281 return 17 + ordinal0; 282 } 283 } 284 285 public class Two implements CellKey { 286 private int ordinal0; 287 private int ordinal1; 288 289 /** 290 * Creates a Two. 291 * 292 * @param ordinal0 Ordinate #0 293 * @param ordinal1 Ordinate #1 294 */ 295 private Two(int ordinal0, int ordinal1) { 296 this.ordinal0 = ordinal0; 297 this.ordinal1 = ordinal1; 298 } 299 300 public String toString() { 301 return "(" + ordinal0 + ", " + ordinal1 + ")"; 302 } 303 304 public Two copy() { 305 return new Two(ordinal0, ordinal1); 306 } 307 308 public boolean equals(Object o) { 309 if (o instanceof Two) { 310 Two other = (Two) o; 311 return (other.ordinal0 == this.ordinal0) && 312 (other.ordinal1 == this.ordinal1); 313 } else if (o instanceof Many) { 314 Many many = (Many) o; 315 return many.ordinals.length == 2 && 316 many.ordinals[0] == this.ordinal0 && 317 many.ordinals[1] == this.ordinal1; 318 } else { 319 return false; 320 } 321 } 322 323 public int hashCode() { 324 int h0 = 17 + ordinal0; 325 return h0 * 37 + ordinal1; 326 } 327 328 public int size() { 329 return 2; 330 } 331 332 public int[] getOrdinals() { 333 return new int[] {ordinal0, ordinal1}; 334 } 335 336 public void setOrdinals(int[] pos) { 337 if (pos.length != 2) { 338 throw new IllegalArgumentException(); 339 } 340 ordinal0 = pos[0]; 341 ordinal1 = pos[1]; 342 } 343 344 public int getAxis(int axis) { 345 switch (axis) { 346 case 0: 347 return ordinal0; 348 case 1: 349 return ordinal1; 350 default: 351 throw new ArrayIndexOutOfBoundsException(axis); 352 } 353 } 354 355 public void setAxis(int axis, int value) { 356 switch (axis) { 357 case 0: 358 ordinal0 = value; 359 break; 360 case 1: 361 ordinal1 = value; 362 break; 363 default: 364 throw new ArrayIndexOutOfBoundsException(axis); 365 } 366 } 367 } 368 369 class Three implements CellKey { 370 private int ordinal0; 371 private int ordinal1; 372 private int ordinal2; 373 374 /** 375 * Creates a Three. 376 * 377 * @param ordinal0 Ordinate #0 378 * @param ordinal1 Ordinate #1 379 * @param ordinal2 Ordinate #2 380 */ 381 private Three(int ordinal0, int ordinal1, int ordinal2) { 382 this.ordinal0 = ordinal0; 383 this.ordinal1 = ordinal1; 384 this.ordinal2 = ordinal2; 385 } 386 387 public String toString() { 388 return "(" + ordinal0 + ", " + ordinal1 + ", " + ordinal2 + ")"; 389 } 390 391 public Three copy() { 392 return new Three(ordinal0, ordinal1, ordinal2); 393 } 394 395 public boolean equals(Object o) { 396 // here we cheat, we know that all CellKey's will be the same size 397 if (o instanceof Three) { 398 Three other = (Three) o; 399 return (other.ordinal0 == this.ordinal0) && 400 (other.ordinal1 == this.ordinal1) && 401 (other.ordinal2 == this.ordinal2); 402 } else if (o instanceof Many) { 403 Many many = (Many) o; 404 return many.ordinals.length == 3 && 405 many.ordinals[0] == this.ordinal0 && 406 many.ordinals[1] == this.ordinal1 && 407 many.ordinals[2] == this.ordinal2; 408 } else { 409 return false; 410 } 411 } 412 413 public int hashCode() { 414 int h0 = 17 + ordinal0; 415 int h1 = h0 * 37 + ordinal1; 416 return h1 * 37 + ordinal2; 417 } 418 419 public int getAxis(int axis) { 420 switch (axis) { 421 case 0: 422 return ordinal0; 423 case 1: 424 return ordinal1; 425 case 2: 426 return ordinal2; 427 default: 428 throw new ArrayIndexOutOfBoundsException(axis); 429 } 430 } 431 432 public void setAxis(int axis, int value) { 433 switch (axis) { 434 case 0: 435 ordinal0 = value; 436 break; 437 case 1: 438 ordinal1 = value; 439 break; 440 case 2: 441 ordinal2 = value; 442 break; 443 default: 444 throw new ArrayIndexOutOfBoundsException(axis); 445 } 446 } 447 448 public int size() { 449 return 3; 450 } 451 452 public int[] getOrdinals() { 453 return new int[] {ordinal0, ordinal1, ordinal2}; 454 } 455 456 public void setOrdinals(int[] pos) { 457 if (pos.length != 3) { 458 throw new IllegalArgumentException(); 459 } 460 ordinal0 = pos[0]; 461 ordinal1 = pos[1]; 462 ordinal2 = pos[2]; 463 } 464 } 465 466 public class Many implements CellKey { 467 private final int[] ordinals; 468 469 /** 470 * Creates a Many. 471 * @param ordinals Ordinates 472 */ 473 protected Many(int[] ordinals) { 474 this.ordinals = ordinals; 475 } 476 477 public final int size() { 478 return this.ordinals.length; 479 } 480 481 public final void setOrdinals(int[] pos) { 482 if (ordinals.length != pos.length) { 483 throw new IllegalArgumentException(); 484 } 485 System.arraycopy(pos, 0, this.ordinals, 0, ordinals.length); 486 } 487 public final int[] getOrdinals() { 488 return this.ordinals; 489 } 490 491 public void setAxis(int axis, int value) { 492 this.ordinals[axis] = value; 493 } 494 495 public int getAxis(int axis) { 496 return this.ordinals[axis]; 497 } 498 499 public String toString() { 500 StringBuilder buf = new StringBuilder(); 501 buf.append('('); 502 for (int i = 0; i < ordinals.length; i++) { 503 if (i > 0) { 504 buf.append(','); 505 } 506 buf.append(ordinals[i]); 507 } 508 buf.append(')'); 509 return buf.toString(); 510 } 511 512 public Many copy() { 513 return new Many(this.ordinals.clone()); 514 } 515 516 public int hashCode() { 517 int h = 17; 518 for (int ordinal : ordinals) { 519 h = (h * 37) + ordinal; 520 } 521 return h; 522 } 523 524 public boolean equals(Object o) { 525 if (o instanceof Many) { 526 Many that = (Many) o; 527 return Arrays.equals(this.ordinals, that.ordinals); 528 } else { 529 // Use symmetric logic in One, Two, Three. 530 return o instanceof CellKey && o.equals(this); 531 } 532 } 533 } 534 } 535 536 // End CellKey.java