001 /*
002 // $Id: //open/mondrian-release/3.0/src/main/mondrian/rolap/CellKey.java#2 $
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-release/3.0/src/main/mondrian/rolap/CellKey.java#2 $
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