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