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) 2001-2005 Julian Hyde 008// Copyright (C) 2005-2012 Pentaho and others 009// All Rights Reserved. 010// 011// jhyde, 10 August, 2001 012*/ 013package mondrian.rolap; 014 015import java.io.Serializable; 016import 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 most likely cases 029 * where the number of axes is 1, 2, 3 and 4 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 */ 038public interface CellKey extends Serializable { 039 /** 040 * Returns the number of axes. 041 * 042 * @return number of axes 043 */ 044 int size(); 045 046 /** 047 * Returns the axis keys as an array. 048 * 049 * <p>Note: caller should treat the array as immutable. If the contents of 050 * the array are modified, behavior is unspecified. 051 * 052 * @return Array of axis keys 053 */ 054 int[] getOrdinals(); 055 056 /** 057 * This method make a copy of the int array parameter. 058 * Throws a RuntimeException if the int array size is not the 059 * size of the CellKey. 060 * 061 * @param pos Array of axis keys 062 */ 063 void setOrdinals(int[] pos); 064 065 /** 066 * Returns the <code>axis</code>th axis value. 067 * 068 * @param axis Axis ordinal 069 * @return Value of the <code>axis</code>th axis 070 * @throws ArrayIndexOutOfBoundsException if axis is out of range 071 */ 072 int getAxis(int axis); 073 074 /** 075 * Sets a given axis. 076 * 077 * @param axis Axis ordinal 078 * @param value Value 079 * @throws RuntimeException if axis parameter is larger than {@link #size()} 080 */ 081 void setAxis(int axis, int value); 082 083 /** 084 * Returns a mutable copy of this CellKey. 085 * 086 * @return Mutable copy 087 */ 088 CellKey copy(); 089 090 /** 091 * Returns the offset of the cell in a raster-scan order. 092 * 093 * <p>For example, if the axes have lengths {2, 5, 10} then cell (2, 3, 4) 094 * has offset 095 * 096 * <blockquote> 097 * (2 * mulitiplers[0]) + (3 * multipliers[1]) + (4 * multipliers[2])<br/> 098 * = (2 * 50) + (3 * 10) + (4 * 1)<br/> 099 * = 134</blockquote> 100 * 101 * <p>The multipliers are the product of the lengths of all later axes, in 102 * this case (50, 10, 1). 103 * 104 * @param axisMultipliers For each axis, the product of the lengths of later 105 * axes 106 * @return The raster-scan ordinal of this cell 107 */ 108 int getOffset(int[] axisMultipliers); 109 110 public abstract class Generator { 111 /** 112 * Creates a CellKey with a given number of axes. 113 * 114 * @param size Number of axes 115 * @return new CellKey 116 */ 117 public static CellKey newCellKey(int size) { 118 switch (size) { 119 case 0: 120 return Zero.INSTANCE; 121 case 1: 122 return new One(0); 123 case 2: 124 return new Two(0, 0); 125 case 3: 126 return new Three(0, 0, 0); 127 case 4: 128 return new Four(0, 0, 0, 0); 129 default: 130 return new Many(new int[size]); 131 } 132 } 133 134 /** 135 * Creates a CellKey populated with the given coordinates. 136 * 137 * @param pos Coordinate array 138 * @return CellKey 139 */ 140 public static CellKey newCellKey(int[] pos) { 141 switch (pos.length) { 142 case 0: 143 return Zero.INSTANCE; 144 case 1: 145 return new One(pos[0]); 146 case 2: 147 return new Two(pos[0], pos[1]); 148 case 3: 149 return new Three(pos[0], pos[1], pos[2]); 150 case 4: 151 return new Four(pos[0], pos[1], pos[2], pos[3]); 152 default: 153 return new Many(pos.clone()); 154 } 155 } 156 157 /** 158 * Creates a CellKey implemented by an array to hold the coordinates, 159 * regardless of the size. 160 * 161 * <p>This is used for testing only. The CellKey will fail to compare 162 * equal to naturally created keys of size 0, 1, 2 or 3. 163 * 164 * @param size Number of coordinates 165 * @return CallKey 166 */ 167 static CellKey newManyCellKey(int size) { 168 return new Many(new int[size]); 169 } 170 171 public static int getOffset( 172 int[] ordinals, 173 int[] axisMultipliers) 174 { 175 // TODO: We know that axisMultiplers[ordinals.length - 1] == 1. Can 176 // we avoid multiplying by it? 177 int offset = 0; 178 for (int i = 0; i < ordinals.length; i++) { 179 offset += ordinals[i] * axisMultipliers[i]; 180 } 181 return offset; 182 } 183 } 184 185 public class Zero implements CellKey { 186 private static final long serialVersionUID = 6063541581473797367L; 187 private static final int[] EMPTY_INT_ARRAY = new int[0]; 188 public static final Zero INSTANCE = new Zero(); 189 190 /** 191 * Use singleton {@link #INSTANCE}. 192 */ 193 private Zero() { 194 } 195 196 public Zero copy() { 197 // no need to make copy since there is no state 198 return this; 199 } 200 201 public int getOffset(int[] axisMultipliers) { 202 return 0; 203 } 204 205 public boolean equals(Object o) { 206 return o == this; 207 } 208 209 public int hashCode() { 210 return 11; 211 } 212 213 public int size() { 214 return 0; 215 } 216 217 public int[] getOrdinals() { 218 return EMPTY_INT_ARRAY; 219 } 220 221 public void setOrdinals(int[] pos) { 222 if (pos.length != 0) { 223 throw new IllegalArgumentException(); 224 } 225 } 226 227 public int getAxis(int axis) { 228 throw new ArrayIndexOutOfBoundsException(axis); 229 } 230 231 public void setAxis(int axis, int value) { 232 throw new ArrayIndexOutOfBoundsException(axis); 233 } 234 } 235 236 public class One implements CellKey { 237 private static final long serialVersionUID = 2160238882970820960L; 238 private int ordinal0; 239 240 /** 241 * Creates a One. 242 * 243 * @param ordinal0 Ordinate #0 244 */ 245 private One(int ordinal0) { 246 this.ordinal0 = ordinal0; 247 } 248 249 public int size() { 250 return 1; 251 } 252 253 public int[] getOrdinals() { 254 return new int[] {ordinal0}; 255 } 256 257 public void setOrdinals(int[] pos) { 258 if (pos.length != 1) { 259 throw new IllegalArgumentException(); 260 } 261 ordinal0 = pos[0]; 262 } 263 264 public int getAxis(int axis) { 265 switch (axis) { 266 case 0: 267 return ordinal0; 268 default: 269 throw new ArrayIndexOutOfBoundsException(axis); 270 } 271 } 272 273 public void setAxis(int axis, int value) { 274 switch (axis) { 275 case 0: 276 ordinal0 = value; 277 break; 278 default: 279 throw new ArrayIndexOutOfBoundsException(axis); 280 } 281 } 282 283 public One copy() { 284 return new One(ordinal0); 285 } 286 287 public int getOffset(int[] axisMultipliers) { 288 return ordinal0; 289 } 290 291 public boolean equals(Object o) { 292 // here we cheat, we know that all CellKey's will be the same size 293 if (o instanceof One) { 294 One other = (One) o; 295 return (this.ordinal0 == other.ordinal0); 296 } else { 297 return false; 298 } 299 } 300 301 public String toString() { 302 return "(" + ordinal0 + ")"; 303 } 304 305 public int hashCode() { 306 return 17 + ordinal0; 307 } 308 } 309 310 public class Two implements CellKey { 311 private static final long serialVersionUID = 1901188836648369359L; 312 private int ordinal0; 313 private int ordinal1; 314 315 /** 316 * Creates a Two. 317 * 318 * @param ordinal0 Ordinate #0 319 * @param ordinal1 Ordinate #1 320 */ 321 private Two(int ordinal0, int ordinal1) { 322 this.ordinal0 = ordinal0; 323 this.ordinal1 = ordinal1; 324 } 325 326 public String toString() { 327 return "(" + ordinal0 + ", " + ordinal1 + ")"; 328 } 329 330 public Two copy() { 331 return new Two(ordinal0, ordinal1); 332 } 333 334 public int getOffset(int[] axisMultipliers) { 335 return ordinal0 * axisMultipliers[0] 336 + ordinal1; 337 } 338 339 public boolean equals(Object o) { 340 if (o instanceof Two) { 341 Two other = (Two) o; 342 return (other.ordinal0 == this.ordinal0) 343 && (other.ordinal1 == this.ordinal1); 344 } else { 345 return false; 346 } 347 } 348 349 public int hashCode() { 350 int h0 = 17 + ordinal0; 351 return h0 * 37 + ordinal1; 352 } 353 354 public int size() { 355 return 2; 356 } 357 358 public int[] getOrdinals() { 359 return new int[] {ordinal0, ordinal1}; 360 } 361 362 public void setOrdinals(int[] pos) { 363 if (pos.length != 2) { 364 throw new IllegalArgumentException(); 365 } 366 ordinal0 = pos[0]; 367 ordinal1 = pos[1]; 368 } 369 370 public int getAxis(int axis) { 371 switch (axis) { 372 case 0: 373 return ordinal0; 374 case 1: 375 return ordinal1; 376 default: 377 throw new ArrayIndexOutOfBoundsException(axis); 378 } 379 } 380 381 public void setAxis(int axis, int value) { 382 switch (axis) { 383 case 0: 384 ordinal0 = value; 385 break; 386 case 1: 387 ordinal1 = value; 388 break; 389 default: 390 throw new ArrayIndexOutOfBoundsException(axis); 391 } 392 } 393 } 394 395 class Three implements CellKey { 396 private static final long serialVersionUID = -2645858781233421151L; 397 private int ordinal0; 398 private int ordinal1; 399 private int ordinal2; 400 401 /** 402 * Creates a Three. 403 * 404 * @param ordinal0 Ordinate #0 405 * @param ordinal1 Ordinate #1 406 * @param ordinal2 Ordinate #2 407 */ 408 private Three(int ordinal0, int ordinal1, int ordinal2) { 409 this.ordinal0 = ordinal0; 410 this.ordinal1 = ordinal1; 411 this.ordinal2 = ordinal2; 412 } 413 414 public String toString() { 415 return "(" + ordinal0 + ", " + ordinal1 + ", " + ordinal2 + ")"; 416 } 417 418 public Three copy() { 419 return new Three(ordinal0, ordinal1, ordinal2); 420 } 421 422 public int getOffset(int[] axisMultipliers) { 423 return ordinal0 * axisMultipliers[0] 424 + ordinal1 * axisMultipliers[1] 425 + ordinal2; 426 } 427 428 public boolean equals(Object o) { 429 // here we cheat, we know that all CellKey's will be the same size 430 if (o instanceof Three) { 431 Three other = (Three) o; 432 return (other.ordinal0 == this.ordinal0) 433 && (other.ordinal1 == this.ordinal1) 434 && (other.ordinal2 == this.ordinal2); 435 } else { 436 return false; 437 } 438 } 439 440 public int hashCode() { 441 int h0 = 17 + ordinal0; 442 int h1 = h0 * 37 + ordinal1; 443 return h1 * 37 + ordinal2; 444 } 445 446 public int getAxis(int axis) { 447 switch (axis) { 448 case 0: 449 return ordinal0; 450 case 1: 451 return ordinal1; 452 case 2: 453 return ordinal2; 454 default: 455 throw new ArrayIndexOutOfBoundsException(axis); 456 } 457 } 458 459 public void setAxis(int axis, int value) { 460 switch (axis) { 461 case 0: 462 ordinal0 = value; 463 break; 464 case 1: 465 ordinal1 = value; 466 break; 467 case 2: 468 ordinal2 = value; 469 break; 470 default: 471 throw new ArrayIndexOutOfBoundsException(axis); 472 } 473 } 474 475 public int size() { 476 return 3; 477 } 478 479 public int[] getOrdinals() { 480 return new int[] {ordinal0, ordinal1, ordinal2}; 481 } 482 483 public void setOrdinals(int[] pos) { 484 if (pos.length != 3) { 485 throw new IllegalArgumentException(); 486 } 487 ordinal0 = pos[0]; 488 ordinal1 = pos[1]; 489 ordinal2 = pos[2]; 490 } 491 } 492 493 class Four implements CellKey { 494 private static final long serialVersionUID = -2645858781233421151L; 495 private int ordinal0; 496 private int ordinal1; 497 private int ordinal2; 498 private int ordinal3; 499 500 /** 501 * Creates a Four. 502 */ 503 private Four( 504 int ordinal0, 505 int ordinal1, 506 int ordinal2, 507 int ordinal3) 508 { 509 this.ordinal0 = ordinal0; 510 this.ordinal1 = ordinal1; 511 this.ordinal2 = ordinal2; 512 this.ordinal3 = ordinal3; 513 } 514 515 public String toString() { 516 return 517 "(" + ordinal0 + ", " + ordinal1 518 + ", " + ordinal2 + ", " + ordinal3 + ")"; 519 } 520 521 public Four copy() { 522 return new Four(ordinal0, ordinal1, ordinal2, ordinal3); 523 } 524 525 public int getOffset(int[] axisMultipliers) { 526 return ordinal0 * axisMultipliers[0] 527 + ordinal1 * axisMultipliers[1] 528 + ordinal2 * axisMultipliers[2] 529 + ordinal3; 530 } 531 532 public boolean equals(Object o) { 533 // here we cheat, we know that all CellKey's will be the same size 534 if (o instanceof Four) { 535 Four other = (Four) o; 536 return (other.ordinal0 == this.ordinal0) 537 && (other.ordinal1 == this.ordinal1) 538 && (other.ordinal2 == this.ordinal2) 539 && (other.ordinal3 == this.ordinal3); 540 } else { 541 return false; 542 } 543 } 544 545 public int hashCode() { 546 int h0 = 17 + ordinal0; 547 int h1 = h0 * 37 + ordinal1; 548 int h2 = h1 * 37 + ordinal2; 549 return h2 * 37 + ordinal3; 550 } 551 552 public int getAxis(int axis) { 553 switch (axis) { 554 case 0: 555 return ordinal0; 556 case 1: 557 return ordinal1; 558 case 2: 559 return ordinal2; 560 case 3: 561 return ordinal3; 562 default: 563 throw new ArrayIndexOutOfBoundsException(axis); 564 } 565 } 566 567 public void setAxis(int axis, int value) { 568 switch (axis) { 569 case 0: 570 ordinal0 = value; 571 break; 572 case 1: 573 ordinal1 = value; 574 break; 575 case 2: 576 ordinal2 = value; 577 break; 578 case 3: 579 ordinal3 = value; 580 break; 581 default: 582 throw new ArrayIndexOutOfBoundsException(axis); 583 } 584 } 585 586 public int size() { 587 return 4; 588 } 589 590 public int[] getOrdinals() { 591 return new int[] {ordinal0, ordinal1, ordinal2, ordinal3}; 592 } 593 594 public void setOrdinals(int[] pos) { 595 if (pos.length != 4) { 596 throw new IllegalArgumentException(); 597 } 598 ordinal0 = pos[0]; 599 ordinal1 = pos[1]; 600 ordinal2 = pos[2]; 601 ordinal3 = pos[3]; 602 } 603 } 604 605 public class Many implements CellKey { 606 private static final long serialVersionUID = 3438398157192694834L; 607 private final int[] ordinals; 608 609 /** 610 * Creates a Many. 611 * @param ordinals Ordinates 612 */ 613 protected Many(int[] ordinals) { 614 this.ordinals = ordinals; 615 } 616 617 public final int size() { 618 return this.ordinals.length; 619 } 620 621 public final void setOrdinals(int[] pos) { 622 if (ordinals.length != pos.length) { 623 throw new IllegalArgumentException(); 624 } 625 System.arraycopy(pos, 0, this.ordinals, 0, ordinals.length); 626 } 627 628 public final int[] getOrdinals() { 629 return this.ordinals; 630 } 631 632 public void setAxis(int axis, int value) { 633 this.ordinals[axis] = value; 634 } 635 636 public int getAxis(int axis) { 637 return this.ordinals[axis]; 638 } 639 640 public String toString() { 641 StringBuilder buf = new StringBuilder(); 642 buf.append('('); 643 for (int i = 0; i < ordinals.length; i++) { 644 if (i > 0) { 645 buf.append(','); 646 } 647 buf.append(ordinals[i]); 648 } 649 buf.append(')'); 650 return buf.toString(); 651 } 652 653 public Many copy() { 654 return new Many(this.ordinals.clone()); 655 } 656 657 public int getOffset(int[] axisMultipliers) { 658 return Generator.getOffset(ordinals, axisMultipliers); 659 } 660 661 public int hashCode() { 662 int h = 17; 663 for (int ordinal : ordinals) { 664 h = (h * 37) + ordinal; 665 } 666 return h; 667 } 668 669 public boolean equals(Object o) { 670 if (o instanceof Many) { 671 Many that = (Many) o; 672 return Arrays.equals(this.ordinals, that.ordinals); 673 } 674 return false; 675 } 676 } 677} 678 679// End CellKey.java