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) 2002-2005 Julian Hyde
008// Copyright (C) 2005-2013 Pentaho and others
009// All Rights Reserved.
010//
011// jhyde, 21 March, 2002
012*/
013package mondrian.rolap.agg;
014
015import mondrian.rolap.CellKey;
016
017import java.util.Iterator;
018import java.util.Map;
019
020/**
021 * A <code>DenseSegmentDataset</code> is a means of storing segment values
022 * which is suitable when most of the combinations of keys have a value
023 * present.
024 *
025 * <p>The storage requirements are as follows. Table requires 1 word per
026 * cell.</p>
027 *
028 * @author jhyde
029 * @since 21 March, 2002
030 */
031abstract class DenseSegmentDataset implements SegmentDataset {
032    private final SegmentAxis[] axes;
033    protected final int[] axisMultipliers;
034
035    /**
036     * Creates a DenseSegmentDataset.
037     *
038     * @param axes Segment axes, containing actual column values
039     */
040    DenseSegmentDataset(SegmentAxis[] axes) {
041        this.axes = axes;
042        this.axisMultipliers = computeAxisMultipliers();
043    }
044
045    private int[] computeAxisMultipliers() {
046        final int[] axisMultipliers = new int[axes.length];
047        int multiplier = 1;
048        for (int i = axes.length - 1; i >= 0; --i) {
049            final SegmentAxis axis = axes[i];
050            axisMultipliers[i] = multiplier;
051            multiplier *= axis.getKeys().length;
052        }
053        return axisMultipliers;
054    }
055
056    public final double getBytes() {
057        // assume a slot, key, and value are each 4 bytes
058        return getSize() * 12;
059    }
060
061    public Iterator<Map.Entry<CellKey, Object>> iterator() {
062        return new DenseSegmentDatasetIterator();
063    }
064
065    protected abstract Object getObject(int i);
066
067    protected final int getOffset(int[] keys) {
068        return CellKey.Generator.getOffset(keys, axisMultipliers);
069    }
070
071    protected final int getOffset(Object[] keys) {
072        int offset = 0;
073outer:
074        for (int i = 0; i < keys.length; i++) {
075            SegmentAxis axis = axes[i];
076            Object[] ks = axis.getKeys();
077            final int axisLength = ks.length;
078            offset *= axisLength;
079            Object value = keys[i];
080            for (int j = 0; j < axisLength; j++) {
081                if (ks[j].equals(value)) {
082                    offset += j;
083                    continue outer;
084                }
085            }
086            return -1; // not found
087        }
088        return offset;
089    }
090
091    public Object getObject(CellKey pos) {
092        throw new UnsupportedOperationException();
093    }
094
095    public int getInt(CellKey pos) {
096        throw new UnsupportedOperationException();
097    }
098
099    public double getDouble(CellKey pos) {
100        throw new UnsupportedOperationException();
101    }
102
103    protected abstract int getSize();
104
105    /**
106     * Iterator over a DenseSegmentDataset.
107     *
108     * <p>This is a 'cheap' implementation
109     * which doesn't allocate a new Entry every step: it just returns itself.
110     * The Entry must therefore be used immediately, before calling
111     * {@link #next()} again.
112     */
113    private class DenseSegmentDatasetIterator implements
114        Iterator<Map.Entry<CellKey, Object>>,
115        Map.Entry<CellKey, Object>
116    {
117        private final int last = getSize() - 1;
118        private int i = -1;
119        private final int[] ordinals;
120
121        DenseSegmentDatasetIterator() {
122            ordinals = new int[axes.length];
123            ordinals[ordinals.length - 1] = -1;
124        }
125
126        public boolean hasNext() {
127            return i < last;
128        }
129
130        public Map.Entry<CellKey, Object> next() {
131            ++i;
132            int k = ordinals.length - 1;
133            while (k >= 0) {
134                if (ordinals[k] < axes[k].getKeys().length - 1) {
135                    ++ordinals[k];
136                    break;
137                } else {
138                    ordinals[k] = 0;
139                    --k;
140                }
141            }
142            return this;
143        }
144
145        // implement Iterator
146        public void remove() {
147            throw new UnsupportedOperationException();
148        }
149
150        // implement Entry
151        public CellKey getKey() {
152            return CellKey.Generator.newCellKey(ordinals);
153        }
154
155        // implement Entry
156        public Object getValue() {
157            return getObject(i);
158        }
159
160        // implement Entry
161        public Object setValue(Object value) {
162            throw new UnsupportedOperationException();
163        }
164    }
165}
166
167// End DenseSegmentDataset.java