1 /*******************************************************************************
2 * Copyright (c) 2010 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.file;
14 import gnu.trove.list.array.TLongArrayList;
15 import gnu.trove.map.hash.TObjectIntHashMap;
18 import java.io.IOException;
19 import java.util.AbstractList;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.List;
23 import java.util.RandomAccess;
25 import org.simantics.databoard.binding.Binding;
26 import org.simantics.databoard.binding.error.RuntimeBindingException;
27 import org.simantics.databoard.serialization.SerializationException;
28 import org.simantics.databoard.serialization.Serializer;
29 import org.simantics.databoard.serialization.SerializerConstructionException;
30 import org.simantics.databoard.serialization.SerializerScheme;
31 import org.simantics.databoard.util.binary.RandomAccessBinary;
32 import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;
35 * BlobList is a {@link RandomAccessBinary} backend implementation of a List
36 * collection. add() and get() operations serialize and deserialize objects
39 * Set, remove, insert and add operations flush() modifications to before return.<p>
41 * Each operation may throw {@link RuntimeIOException}, if there is IOException
42 * in the {@link RandomAccessBinary}<p>
44 * Entry position index is on open if the file has variable width
45 * data type (eg. String). The entire file is scanned through.<p>
47 * TODO lazy index. Append alone (add()) doesn't require scan.
49 * @see FileList File based implementation
50 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
52 public class RandomAccessBinaryList<T> extends AbstractList<T> implements IFileList<T>, RandomAccess {
55 RandomAccessBinary blob;
58 SerializerScheme format;
64 Serializer serializer;
70 List<Object> identities = new ArrayList<Object>();
73 TObjectIntHashMap<Object> identities2 = new TObjectIntHashMap<Object>();
76 boolean closed = false;
79 * Create new random access list backed by a file
83 * @param startPos The position of the first sample in file
84 * @param format serialization format
86 * @throws SerializerConstructionException could not create serializer, never thrown with BinarySerializationFormat
87 * @throws SerializationException Error with the file, could not build entry index
89 public RandomAccessBinaryList(RandomAccessBinary blob, Binding binding, long startPos, SerializerScheme format)
90 throws IOException, SerializerConstructionException, SerializationException
94 this.binding = binding;
95 serializer = format.getSerializer(binding);
97 Integer sampleSize = serializer.getConstantSize();
99 // Variable width sample
100 if (sampleSize==null)
102 table = new Table(startPos);
103 blob.position(startPos);
104 long length = blob.length();
108 serializer.skip(blob);
109 pos = blob.position();
113 // Fixed width sample
115 long fileSize = blob.length();
116 long count = (fileSize - startPos) / sampleSize;
117 if (count>Integer.MAX_VALUE) throw new IllegalArgumentException("The blob is too large");
118 table = new Constant(startPos, sampleSize, (int) count);
124 public int size() throws RuntimeIOException
126 return table.size()-1;
129 public boolean isOpen()
135 * Flushes the caches and closes the file handle.
147 } catch (IOException ignored) {
152 public Binding getBinding() {
156 @SuppressWarnings("unchecked")
158 public T get(int index) throws RuntimeIOException {
159 if (index<0 || index>=size())
160 throw new IndexOutOfBoundsException();
162 blob.position( table.get(index) );
164 return (T) serializer.deserialize(blob, identities);
165 } catch (IOException e) {
166 throw new RuntimeIOException(e);
170 public void add(int index, T element) throws RuntimeIOException, RuntimeBindingException {
171 if (index<0 || index>size())
172 throw new IndexOutOfBoundsException();
177 blob.position( table.get(index) );
179 serializer.serialize(blob, identities2, element);
180 table.add(blob.position());
186 // Make some room in-file
188 long len = serializer.getSize(element, identities2);
189 long pos = table.get(index);
192 blob.insertBytes(len, ByteSide.Left);
197 serializer.serialize(blob, identities2, element);
199 assert(pos+len == blob.position());
203 table.insert(index, pos);
204 table.adjust(index+1, table.size(), len);
208 } catch (IOException e) {
209 throw new RuntimeIOException(e);
214 * Replace the whole content with the content of another collection
216 * @param c collection
217 * @throws RuntimeIOException
219 public void setAll(Collection<? extends T> c) throws RuntimeIOException
224 table.remove(1, table.size()-1);
225 long zerosize = table.get(0);
226 blob.position(zerosize);
227 blob.setLength(zerosize);
230 } catch (IOException e) {
231 throw new RuntimeIOException(e);
235 @SuppressWarnings("unchecked")
236 public T set(int index, T element) throws RuntimeIOException, RuntimeBindingException {
237 if (index<0 || index>=size())
238 throw new IndexOutOfBoundsException();
241 long startPos = table.get(index);
242 long oldEndPos = table.get(index+1);
243 long oldSize = oldEndPos - startPos;
246 blob.position( startPos );
248 T result = (T) serializer.deserialize(blob, identities);
249 assert(blob.position() == oldEndPos);
253 long newSize = serializer.getSize(element, identities2);
255 long diff = newSize - oldSize;
258 blob.position(startPos);
259 blob.insertBytes(diff, ByteSide.Left);
262 blob.position(startPos);
263 blob.insertBytes(-diff, ByteSide.Left);
268 blob.position( startPos );
269 serializer.serialize(blob, identities2, element);
270 assert(startPos+newSize == blob.position());
274 table.adjust(index+1, table.size(), diff);
280 } catch (IOException e) {
281 throw new RuntimeIOException(e);
286 public void removeRange(int fromIndex, int toIndex) throws RuntimeIOException {
287 if (fromIndex<0 || toIndex<0 || fromIndex>toIndex || toIndex>size())
288 throw new IndexOutOfBoundsException();
290 int count = toIndex - fromIndex;
291 if (count==0) return;
292 long startPos = table.get(fromIndex);
293 long endPos = table.get(toIndex);
294 long length = endPos - startPos;
296 blob.position(startPos);
297 blob.removeBytes(length, ByteSide.Left);
298 table.remove(fromIndex+1, count);
299 table.adjust(fromIndex+1, table.size(), -length);
302 } catch (IOException e) {
303 throw new RuntimeIOException(e);
307 @SuppressWarnings("unchecked")
309 public T remove(int index) throws RuntimeIOException {
310 if (index<0 || index>=size())
311 throw new IndexOutOfBoundsException();
313 long startPos = table.get(index);
314 long endPos = table.get(index+1);
315 long length = endPos - startPos;
317 blob.position(startPos);
319 T result = (T) serializer.deserialize(blob, identities);
320 blob.position(startPos);
321 blob.removeBytes(length, ByteSide.Left);
323 table.remove(index+1, 1);
324 table.adjust(index+1, table.size(), -length);
328 } catch (IOException e) {
329 throw new RuntimeIOException(e);
334 public boolean addAll(Collection<? extends T> c) throws RuntimeIOException {
340 public boolean addAll(int index, Collection<? extends T> c) throws RuntimeIOException, RuntimeBindingException {
341 if (index<0 || index>size())
342 throw new IndexOutOfBoundsException();
347 blob.position( table.get(index) );
349 for (T element : c) {
350 serializer.serialize(blob, identities2, element);
351 table.add(blob.position());
359 long startPos = table.get(index);
360 long endPos = startPos;
362 for (T element : c) {
364 long len = serializer.getSize(element, identities2);
367 table.insert(index+i, endPos);
372 blob.position(startPos);
373 blob.insertBytes(endPos-startPos, ByteSide.Left);
376 blob.position(startPos);
377 for (T element : c) {
379 serializer.serialize(blob, identities2, element);
386 } catch (IOException e) {
387 throw new RuntimeIOException(e);
394 * Get the number of position entries. (= sample count + 1)
398 void add(long position);
399 void set(int index, long position);
400 void insert(int index, long position);
401 void remove(int index, int count);
407 * @param toIndex end index (exclusive)
408 * @param diff position adjustment
410 void adjust(int fromIndex, int toIndex, long diff);
413 private static class Table implements Index {
415 /** Table of file positions of each index. There are size() + 1 entries in the table. if null sample size is fixed */
416 TLongArrayList table = new TLongArrayList(32);
423 public void add(long position) {
428 public void insert(int index, long position) {
429 table.insert(index, position);
433 public void set(int index, long position) {
434 table.set(index, position);
438 public void remove(int index, int length) {
439 table.remove(index, length);
443 public void adjust(int fromIndex, int toIndex, long diff) {
445 for (int index = fromIndex; index<toIndex; index++)
446 table.set(index, table.get(index) + diff);
450 public long get(int index) {
451 return table.get(index);
461 private static class Constant implements Index {
465 Constant(long start, int sampleSize, int count) {
467 this.sampleSize = sampleSize;
471 public void add(long position) {
472 assert( (position-start) % sampleSize == 0);
476 public void set(int index, long position) {
477 assert( position == start + index * sampleSize);
480 public void adjust(int fromIndex, int toIndex, long diff) {
483 public long get(int index) {
484 return start + index*sampleSize;
487 public void insert(int index, long position) {
488 assert( position == start + index * sampleSize );
492 public void remove(int index, int count) {
502 public File getFile() {