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