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) 2011-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.calc.impl;
011
012import mondrian.calc.*;
013import mondrian.olap.*;
014
015import java.util.*;
016
017/**
018 * Implementation of {@link TupleList} that stores tuples end-to-end in an
019 * array.
020 *
021 * @author jhyde
022*/
023public class ArrayTupleList extends AbstractEndToEndTupleList
024{
025    private transient Member[] objectData;
026    private int size;
027
028    /**
029     * Creates an empty ArrayTupleList with an initial capacity of 10 tuples.
030     *
031     * @param arity Arity
032     */
033    public ArrayTupleList(int arity) {
034        this(arity, 10 * arity);
035        assert arity > 1 : "Probably better to use a UnaryTupleList";
036    }
037
038    /**
039     * Creates an empty ArrayTupleList.
040     *
041     * @param arity Arity
042     * @param initialCapacity Initial capacity
043     */
044    public ArrayTupleList(int arity, int initialCapacity) {
045        this(arity, new Member[initialCapacity * arity], 0);
046    }
047
048    private ArrayTupleList(int arity, Member[] members, int size) {
049        super(arity);
050        assert members.length % arity == 0;
051        this.objectData = members;
052        this.size = size;
053    }
054
055    @Override
056    protected List<Member> backingList() {
057        return new AbstractList<Member>() {
058            @Override
059            public Member get(int index) {
060                return objectData[index];
061            }
062
063            @Override
064            public int size() {
065                return size * arity;
066            }
067        };
068    }
069
070    @Override
071    public Member get(int slice, int index) {
072        return objectData[index * arity + slice];
073    }
074
075    @Override
076    public List<Member> get(int index) {
077        final int startIndex = index * arity;
078        final List<Member> list =
079            new AbstractList<Member>() {
080                public Member get(int index) {
081                    return objectData[startIndex + index];
082                }
083
084                public int size() {
085                    return arity;
086                }
087            };
088        if (mutable) {
089            return Util.flatList(list);
090        }
091        return list;
092    }
093
094    @Override
095    public List<Member> set(int index, List<Member> element) {
096        assert mutable;
097        for (int i = 0, startIndex = index * arity; i < arity; i++) {
098            objectData[startIndex + i] = element.get(i);
099        }
100        return null; // not compliant with List contract
101    }
102
103    @Override
104    public void addCurrent(TupleCursor tupleIter) {
105        assert mutable;
106        int n = size * arity;
107        ensureCapacity(n + arity);
108        tupleIter.currentToArray(objectData, n);
109        ++size;
110    }
111
112    public int size() {
113        return size;
114    }
115
116    @Override
117    public boolean add(List<Member> members) {
118        assert mutable;
119        if (members.size() != arity) {
120            throw new IllegalArgumentException(
121                "Tuple length does not match arity");
122        }
123        int n = size * arity;
124        ensureCapacity(n + arity);
125        for (int i = 0; i < members.size(); i++) {
126            objectData[n++] = members.get(i);
127        }
128        ++size;
129        return true;
130    }
131
132    @Override
133    public void add(int index, List<Member> members) {
134        assert mutable;
135        if (members.size() != arity) {
136            throw new IllegalArgumentException(
137                "Tuple length does not match arity");
138        }
139        int n = index * arity;
140        ensureCapacity((size + 1) + arity);
141        System.arraycopy(objectData, n, objectData, n + arity, arity);
142        for (Member member : members) {
143            objectData[n++] = member;
144        }
145        ++size;
146    }
147
148    @Override
149    public boolean addAll(int index, Collection<? extends List<Member>> c) {
150        assert mutable;
151        final int size1 = c.size();
152        ensureCapacity(size * arity + size1 * arity);
153        int n = index * arity;
154        System.arraycopy(
155            objectData, n, objectData, n + size1 * arity, size * arity - n);
156        for (List<Member> members : c) {
157            for (Member member : members) {
158                objectData[n++] = member;
159            }
160        }
161        size += size1;
162        return size1 > 0;
163    }
164
165    public void addTuple(Member... members) {
166        assert mutable;
167        if (members.length != arity) {
168            throw new IllegalArgumentException(
169                "Tuple length does not match arity");
170        }
171        ensureCapacity(size * arity + arity);
172        System.arraycopy(members, 0, objectData, size * arity, arity);
173        ++size;
174    }
175
176    @Override
177    public List<Member> remove(int index) {
178        assert mutable;
179        final int n = index * arity;
180        // Strict compliance with List API:
181        // List<Member> previous = get(index);
182        System.arraycopy(objectData, n + arity, objectData, n, arity);
183        --size;
184        return null; // previous;
185    }
186
187    public List<Member> slice(final int column) {
188        if (column < 0 || column >= arity) {
189            throw new IllegalArgumentException();
190        }
191        return new AbstractList<Member>() {
192            @Override
193            public Member get(int index) {
194                return objectData[index * arity + column];
195            }
196
197            @Override
198            public int size() {
199                return size;
200            }
201        };
202    }
203
204    public TupleList cloneList(int capacity) {
205        if (capacity < 0) {
206            // copy of this list with the same contents
207            return new ArrayTupleList(arity, objectData.clone(), size());
208        } else {
209            // empty copy of this list with given capacity
210            return new ArrayTupleList(arity, capacity);
211        }
212    }
213
214    public TupleIterator tupleIteratorInternal() {
215        // Improve the base class implementation of setContext. It is cheaper
216        // to call evaluator.setContext several times than to create a
217        // temporary list or array.
218        return new AbstractTupleListIterator() {
219            public void setContext(Evaluator evaluator) {
220                for (int i = 0, x = lastRet * arity; i < arity; i++) {
221                    evaluator.setContext(objectData[x + i]);
222                }
223            }
224
225            public Member member(int column) {
226                return objectData[lastRet * arity + column];
227            }
228
229            public void currentToArray(Member[] members, int offset) {
230                System.arraycopy(
231                    objectData,
232                    lastRet * arity,
233                    members,
234                    offset,
235                    arity);
236            }
237        };
238    }
239
240    private void ensureCapacity(int minCapacity) {
241        int oldCapacity = objectData.length;
242        if (minCapacity > oldCapacity) {
243            int newCapacity = (oldCapacity * 3) / 2 + 1;
244            if (newCapacity < minCapacity) {
245                newCapacity = minCapacity;
246            }
247            // Up to next multiple of arity.
248            final int rem = newCapacity % arity;
249            newCapacity += (arity - rem);
250            objectData = Util.copyOf(objectData, newCapacity);
251        }
252    }
253}
254
255// End ArrayTupleList.java