]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/file/RandomAccessBinaryList.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / file / RandomAccessBinaryList.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/file/RandomAccessBinaryList.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/file/RandomAccessBinaryList.java
new file mode 100644 (file)
index 0000000..8d45a80
--- /dev/null
@@ -0,0 +1,509 @@
+/*******************************************************************************\r
+ *  Copyright (c) 2010 Association for Decentralized Information Management in\r
+ *  Industry THTH ry.\r
+ *  All rights reserved. This program and the accompanying materials\r
+ *  are made available under the terms of the Eclipse Public License v1.0\r
+ *  which accompanies this distribution, and is available at\r
+ *  http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ *  Contributors:\r
+ *      VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.file;
+
+import gnu.trove.list.array.TLongArrayList;\r
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.AbstractList;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.List;\r
+import java.util.RandomAccess;\r
+\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.serialization.SerializationException;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.serialization.SerializerConstructionException;\r
+import org.simantics.databoard.serialization.SerializerScheme;\r
+import org.simantics.databoard.util.binary.RandomAccessBinary;\r
+import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;\r
+
+/**
+ * BlobList is a {@link RandomAccessBinary} backend implementation of a List 
+ * collection. add() and get() operations serialize and deserialize objects 
+ * from binary format.  
+ * 
+ * Set, remove, insert and add operations flush() modifications to before return.<p>
+ * 
+ * Each operation may throw {@link RuntimeIOException}, if there is IOException
+ * in the {@link RandomAccessBinary}<p> 
+ * 
+ * Entry position index is on open if the file has variable width
+ * data type (eg. String). The entire file is scanned through.<p>
+ * 
+ * TODO lazy index. Append alone (add()) doesn't require scan.
+ *
+ * @see FileList File based implementation 
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class RandomAccessBinaryList<T> extends AbstractList<T> implements IFileList<T>, RandomAccess {
+       
+       /** Reader */
+       RandomAccessBinary blob;
+       
+       /** Format */
+       SerializerScheme format;
+       
+       /** Binding */ 
+       Binding binding;
+       
+       /** Serializer */
+       Serializer serializer;
+       
+       /** Offset table */
+       Index table;
+       
+       /** identities */
+       List<Object> identities = new ArrayList<Object>();
+       
+       /** identities */
+       TObjectIntHashMap<Object> identities2 = new TObjectIntHashMap<Object>();
+       
+       /** Status */
+       boolean closed = false;
+               
+       /**
+        * Create new random access list backed by a file
+        * 
+        * @param blob blob
+        * @param binding
+        * @param startPos The position of the first sample in file
+        * @param format serialization format
+        * @throws IOException 
+        * @throws SerializerConstructionException could not create serializer, never thrown with BinarySerializationFormat
+        * @throws SerializationException Error with the file, could not build entry index
+        */
+       public RandomAccessBinaryList(RandomAccessBinary blob, Binding binding, long startPos, SerializerScheme format) 
+       throws IOException, SerializerConstructionException, SerializationException 
+       {               
+               this.format = format;                   
+               this.blob = blob;
+               this.binding = binding;                         
+               serializer = format.getSerializer(binding);
+               
+               Integer sampleSize = serializer.getConstantSize();
+               
+               // Variable width sample
+               if (sampleSize==null) 
+               {
+                       table = new Table(startPos); 
+                       blob.position(startPos);
+                       long length = blob.length();
+                       long pos = startPos;
+                       while (pos<length)
+                       {
+                               serializer.skip(blob);
+                               pos = blob.position();
+                               table.add(pos);
+                       }
+               } else
+               // Fixed width sample
+               {
+                       long fileSize = blob.length();
+                       long count = (fileSize - startPos) / sampleSize;
+                       if (count>Integer.MAX_VALUE) throw new IllegalArgumentException("The blob is too large");
+                       table = new Constant(startPos, sampleSize, (int) count);
+               }
+       }
+       
+               
+       @Override
+       public int size() throws RuntimeIOException
+       {
+               return table.size()-1;
+       }
+       
+       public boolean isOpen()
+       {
+               return !closed;
+       }
+       
+       /**
+        * Flushes the caches and closes the file handle. 
+        */
+       public void close()
+       {
+               synchronized(this) {
+                       if (closed) return;
+                       closed = true;
+               }
+               
+               try {
+                       blob.flush();
+                       blob.close();
+               } catch (IOException ignored) {
+               }
+       }
+
+       @Override
+       public Binding getBinding() {
+               return binding;
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public T get(int index) throws RuntimeIOException {
+               if (index<0 || index>=size())
+                       throw new IndexOutOfBoundsException();
+               try {
+                       blob.position( table.get(index) );
+                       identities.clear();
+                       return (T) serializer.deserialize(blob, identities);
+               } catch (IOException e) {
+                       throw new RuntimeIOException(e);
+               }
+       }
+       
+       public void add(int index, T element) throws RuntimeIOException, RuntimeBindingException {
+               if (index<0 || index>size())
+                       throw new IndexOutOfBoundsException();
+               
+               // Append
+               try {
+                       if (index==size()) {
+                               blob.position( table.get(index) );
+                               identities2.clear();
+                               serializer.serialize(blob, identities2, element);
+                               table.add(blob.position());
+                               modCount++;                                     
+                               blob.flush();
+                       } else                  
+                       // Insert
+                       {                       
+                               // Make some room in-file
+                               identities2.clear();
+                               long len = serializer.getSize(element, identities2);                            
+                               long pos = table.get(index);
+                               blob.flush();
+                               blob.position(pos);
+                               blob.insertBytes(len, ByteSide.Left);
+                               
+                               // Write
+                               identities2.clear();
+                               blob.position(pos);
+                               serializer.serialize(blob, identities2, element);
+                               
+                               assert(pos+len == blob.position());
+                               blob.flush();
+                               
+                               // Update table
+                               table.insert(index, pos);
+                               table.adjust(index+1, table.size(), len);
+                               modCount++;                                     
+                       }               
+                       blob.flush();
+               } catch (IOException e) {
+                       throw new RuntimeIOException(e);
+               }
+       };
+       
+       /**
+        * Replace the whole content with the content of another collection 
+        * 
+        * @param c collection 
+        * @throws RuntimeIOException
+        */
+       public void setAll(Collection<? extends T> c) throws RuntimeIOException
+       {
+               try {
+                       blob.flush();
+                       if (table.size()>1)
+                               table.remove(1, table.size()-1);
+                       long zerosize = table.get(0);
+                       blob.position(zerosize);
+                       blob.setLength(zerosize);
+                       addAll(c);
+                       blob.flush();
+               } catch (IOException e) {
+                       throw new RuntimeIOException(e);
+               }               
+       }
+       
+    @SuppressWarnings("unchecked")
+       public T set(int index, T element) throws RuntimeIOException, RuntimeBindingException {
+               if (index<0 || index>=size())
+                       throw new IndexOutOfBoundsException();
+               
+               try {
+                       long startPos = table.get(index);
+                       long oldEndPos = table.get(index+1);
+                       long oldSize = oldEndPos - startPos;
+
+                       // Read old
+                       blob.position( startPos );
+                       identities.clear();
+                       T result = (T) serializer.deserialize(blob, identities);
+                       assert(blob.position() == oldEndPos);
+                       
+                       // Calc size of new
+                       identities2.clear();
+                       long newSize = serializer.getSize(element, identities2);                                
+
+                       long diff = newSize - oldSize;
+                       if (diff>0) {
+                               blob.flush();
+                               blob.position(startPos);
+                               blob.insertBytes(diff, ByteSide.Left);
+                       } else if (diff<0) {
+                               blob.flush();
+                               blob.position(startPos);
+                               blob.insertBytes(-diff, ByteSide.Left);
+                       }
+                       
+                       // Write new
+                       identities2.clear();
+                       blob.position( startPos );
+                       serializer.serialize(blob, identities2, element);                       
+                       assert(startPos+newSize == blob.position());
+                       blob.flush();
+                       
+                       // Update table
+                       table.adjust(index+1, table.size(), diff);
+
+                       if (diff!=0)
+                               modCount++;
+                       blob.flush();
+                       return result;
+               } catch (IOException e) {
+                       throw new RuntimeIOException(e);
+               }
+    }  
+       
+       @Override
+       public void removeRange(int fromIndex, int toIndex) throws RuntimeIOException {
+               if (fromIndex<0 || toIndex<0 || fromIndex>toIndex || toIndex>size())
+                       throw new IndexOutOfBoundsException();
+               try {
+                       int count = toIndex - fromIndex;
+                       if (count==0) return;
+                       long startPos = table.get(fromIndex);
+                       long endPos = table.get(toIndex);
+                       long length = endPos - startPos;
+
+                       blob.position(startPos);
+                       blob.removeBytes(length, ByteSide.Left);
+                       table.remove(fromIndex+1, count);
+                       table.adjust(fromIndex+1, table.size(), -length);
+                       modCount++;                                     
+                       blob.flush();
+               } catch (IOException e) {
+                       throw new RuntimeIOException(e);
+               }
+       }
+       
+       @SuppressWarnings("unchecked")
+       @Override
+       public T remove(int index) throws RuntimeIOException {
+               if (index<0 || index>=size())
+                       throw new IndexOutOfBoundsException();
+               try {
+                       long startPos = table.get(index);
+                       long endPos = table.get(index+1);
+                       long length = endPos - startPos;
+                       
+                       blob.position(startPos);
+                       identities.clear();
+                       T result = (T) serializer.deserialize(blob, identities);
+                       blob.position(startPos);
+                       blob.removeBytes(length, ByteSide.Left);                        
+
+                       table.remove(index+1, 1);
+                       table.adjust(index+1, table.size(), -length);
+                       modCount++;
+                       blob.flush();
+                       return result;
+               } catch (IOException e) {
+                       throw new RuntimeIOException(e);
+               }
+       }
+       
+       @Override
+       public boolean addAll(Collection<? extends T> c) throws RuntimeIOException {
+               return addAll(0, c);
+       }
+       
+       
+       @Override
+       public boolean addAll(int index, Collection<? extends T> c) throws RuntimeIOException, RuntimeBindingException {
+               if (index<0 || index>size())
+                       throw new IndexOutOfBoundsException();
+               
+               // Append
+               try {
+                       if (index==size()) {
+                               blob.position( table.get(index) );
+                               identities2.clear();
+                               for (T element : c) {
+                                       serializer.serialize(blob, identities2, element);
+                                       table.add(blob.position());
+                               }
+                               blob.flush();
+                               modCount++;                                     
+                       } else                  
+                       // Insert
+                       {                       
+                               // Calc sizes
+                               long startPos = table.get(index);
+                               long endPos = startPos;
+                               int i=0;
+                               for (T element : c) {
+                                       identities2.clear();
+                                       long len = serializer.getSize(element, identities2);
+                                       endPos += len;
+                                       i++;
+                                       table.insert(index+i, endPos);
+                               }
+                                                               
+                               // Make room
+                               blob.flush();
+                               blob.position(startPos);
+                               blob.insertBytes(endPos-startPos, ByteSide.Left);
+                               
+                               // Write
+                               blob.position(startPos);
+                               for (T element : c) {
+                                       identities2.clear();
+                                       serializer.serialize(blob, identities2, element);
+                               }
+                               blob.flush();
+                               modCount++;
+                       }               
+                       blob.flush();
+                       return !c.isEmpty();
+               } catch (IOException e) {
+                       throw new RuntimeIOException(e);
+               }
+       }
+       
+       interface Index {
+               long get(int index);
+               /** 
+                * Get the number of position entries. (= sample count + 1) 
+                * @return
+                */
+               int size();
+               void add(long position);
+               void set(int index, long position);
+               void insert(int index, long position);
+               void remove(int index, int count);
+               
+               /**
+                * Adjust positions
+                * 
+                * @param fromIndex
+                * @param toIndex end index (exclusive)
+                * @param diff position adjustment
+                */
+               void adjust(int fromIndex, int toIndex, long diff);             
+       }
+
+       private static class Table implements Index {
+               
+               /** Table of file positions of each index. There are size() + 1 entries in the table. if null sample size is fixed */
+               TLongArrayList table = new TLongArrayList(32);
+               
+               Table(long start) {
+                       table.add(start);
+               }
+
+               @Override
+               public void add(long position) {
+                       table.add(position);
+               }
+               
+               @Override
+               public void insert(int index, long position) {
+                       table.insert(index, position);
+               }
+               
+               @Override
+               public void set(int index, long position) {
+                       table.set(index, position);
+               }
+               
+               @Override
+               public void remove(int index, int length) {
+                       table.remove(index, length);
+               }
+
+               @Override
+               public void adjust(int fromIndex, int toIndex, long diff) {
+                       if (diff==0) return;
+                       for (int index = fromIndex; index<toIndex; index++)
+                               table.set(index, table.get(index) + diff);
+               }
+
+               @Override
+               public long get(int index) {
+                       return table.get(index);
+               }
+
+               @Override
+               public int size() {
+                       return table.size();
+               }               
+               
+       }
+       
+       private static class Constant implements Index {
+               long start;
+               long sampleSize;
+               int count;
+               Constant(long start, int sampleSize, int count) {
+                       this.start = start;
+                       this.sampleSize = sampleSize;
+                       this.count = count;
+               }
+               @Override
+               public void add(long position) {
+                       assert( (position-start) % sampleSize == 0);
+                       count++;
+               }
+               @Override
+               public void set(int index, long position) {
+                       assert( position == start + index * sampleSize);
+               }               
+               @Override
+               public void adjust(int fromIndex, int toIndex, long diff) {
+               }
+               @Override
+               public long get(int index) {
+                       return start + index*sampleSize;
+               }
+               @Override
+               public void insert(int index, long position) {
+                       assert( position == start + index * sampleSize );
+                       count++;                        
+               }
+               @Override
+               public void remove(int index, int count) {
+                       this.count -= count;
+               }
+               @Override
+               public int size() {
+                       return count+1;
+               }
+       }
+
+       @Override
+       public File getFile() {
+               return null;
+       }
+       
+
+}
+
+