]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryObject.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryObject.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryObject.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryObject.java
new file mode 100644 (file)
index 0000000..149d993
--- /dev/null
@@ -0,0 +1,561 @@
+/*******************************************************************************\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.accessor.binary;
+
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.Collection;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.concurrent.Executor;\r
+import java.util.concurrent.locks.Lock;\r
+\r
+import org.simantics.databoard.accessor.Accessor;\r
+import org.simantics.databoard.accessor.CloseableAccessor;\r
+import org.simantics.databoard.accessor.ParametrisedAccessor;\r
+import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
+import org.simantics.databoard.accessor.error.AccessorException;\r
+import org.simantics.databoard.accessor.error.ReferenceException;\r
+import org.simantics.databoard.accessor.event.Event;\r
+import org.simantics.databoard.accessor.event.InvalidatedEvent;\r
+import org.simantics.databoard.accessor.file.FileAccessor;\r
+import org.simantics.databoard.accessor.impl.AccessorParams;\r
+import org.simantics.databoard.accessor.impl.ListenerEntry;\r
+import org.simantics.databoard.accessor.interestset.InterestSet;\r
+import org.simantics.databoard.accessor.reference.ChildReference;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.adapter.AdapterConstructionException;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.serialization.SerializerConstructionException;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.type.BooleanType;\r
+import org.simantics.databoard.type.ByteType;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.DoubleType;\r
+import org.simantics.databoard.type.FloatType;\r
+import org.simantics.databoard.type.IntegerType;\r
+import org.simantics.databoard.type.LongType;\r
+import org.simantics.databoard.type.MapType;\r
+import org.simantics.databoard.type.OptionalType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.type.StringType;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.databoard.type.VariantType;\r
+import org.simantics.databoard.util.binary.BinaryFile;\r
+import org.simantics.databoard.util.binary.Blob;\r
+import org.simantics.databoard.util.binary.RandomAccessBinary;\r
+
+/**
+ * BinaryObject is an accessor to a binary object, usually a random access file.
+ * BinaryObject cannot handle files of recursive types.  
+ * <p>
+ * The file can be opened once. It may not be modified by any other instance other than
+ * accessor while accessors are beign used. You must not create more than one
+ * instance of BinaryObjects for a file.\r
+ * <p>\r
+ * \r
+ * \r
+ * @see BinaryArray\r
+ * @see BinaryBoolean\r
+ * @see BinaryByte\r
+ * @see BinaryDouble\r
+ * @see BinaryFloat\r
+ * @see BinaryInteger\r
+ * @see BinaryLong\r
+ * @see BinaryMap\r
+ * @see BinaryOptional\r
+ * @see BinaryRecord\r
+ * @see BinaryString\r
+ * @see BinaryUnion\r
+ * @see BinaryVariant\r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public abstract class BinaryObject implements Accessor, FileAccessor, CloseableAccessor, ParametrisedAccessor {
+    
+       /** Hard link to the parent object, <code>null</code> if this is root */
+       protected Accessor parent;      
+       /** Listeners */
+       protected ListenerEntry listeners = null;
+       /** Random access binary object */
+       protected Blob b;
+       /** Type */
+       protected Datatype type;\r
+       /** File, optional */\r
+       protected File file;
+       /** Accessor params */\r
+       protected AccessorParams params;\r
+
+       BinaryObject(Accessor parent, Blob blob, Datatype type, AccessorParams params) {
+               this.parent = parent;
+               this.b = blob;
+               this.type = type;\r
+               this.params = params;\r
+               \r
+               if (parent!=null && parent instanceof BinaryObject) {\r
+                       file = ((BinaryObject)parent).file();\r
+               } else {\r
+                       RandomAccessBinary sourceBinary = b.getSource();\r
+                       if (sourceBinary instanceof BinaryFile) {\r
+                               BinaryFile bf = (BinaryFile) sourceBinary;\r
+                               file = bf.file();\r
+                       }\r
+               }\r
+       }
+       
+       public Datatype type() {
+               return type;
+       }
+                       
+       public void flush() throws AccessorException {
+               try {
+                       b.flush();
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               }
+       }\r
+       \r
+       @Override\r
+       public void reset() throws AccessorException {\r
+               try {\r
+                       b.reset();\r
+               } catch (IOException e) {\r
+                       throw new AccessorException(e);\r
+               }\r
+       }\r
+       \r
+       public RandomAccessBinary getSource() {\r
+               RandomAccessBinary result = b;\r
+               while (result instanceof Blob) result = ((Blob)result).getParent();\r
+               return result;\r
+       }
+\r
+       /**\r
+        * Close the random access file beneath\r
+        */
+       public void close() throws AccessorException {
+               writeLock();\r
+               try {
+                       if (parent!=null) {
+                               ((FileAccessor) parent).close();
+                               return;
+                       }
+                       
+                       // Root Object\r
+                       if (b==null) return;\r
+                       RandomAccessBinary rab = getSource();
+                       rab.flush();                    
+                       rab.close();\r
+                       b = null;
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }\r
+       \r
+       public boolean isOpen() {\r
+               return b.isOpen();\r
+       }
+       
+       /**
+        * Get file if the binary object is based on binary file. 
+        * 
+        * @return file or <code>null</code>
+        */
+       public File file() {\r
+               return file;
+       }
+       
+       public RandomAccessBinary getBinary() {
+               return b;
+       }
+       \r
+       @Override\r
+       public AccessorParams getParams() {\r
+               return params;\r
+       }\r
+       
+       @Override
+       public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {
+               listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);
+       }
+               
+       protected ListenerEntry detachListener(Listener listener) throws AccessorException {
+               ListenerEntry e = listeners;
+               ListenerEntry p = null;
+               while (e!=null) {
+                       // Found match
+                       if (e.listener == listener) {
+                               // The match was the first entry of the linked list
+                               if (p==null) {
+                                       listeners = e.next;
+                                       return e;
+                               }
+                               // Some other entry, unlink e
+                               p.next = e.next;
+                               return e;
+                       }
+                       p = e;
+                       e = e.next;
+               }
+               return null;            
+       }
+       
+       @Override
+       public void removeListener(Listener listener) throws AccessorException {
+               detachListener(listener);
+       }       
+       
+       /**
+        * Write a new value and flush the buffer.
+        * 
+        * @param binding
+        * @param newValue
+        */
+       @Override
+       public void setValue(Binding binding, Object newValue)
+       throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();\r
+               try {
+                       setValueNoflush(binding, newValue);
+                       b.flush();
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+       
+       /**
+        * Write a new value and don't flush the buffer
+        * 
+        * @param binding
+        * @param newValue
+        * @throws AccessorException
+        */
+       public abstract void setValueNoflush(Binding binding, Object newValue) throws AccessorException;
+\r
+       public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
+               try {\r
+                       Accessor a = getComponent(path);\r
+                       a.setValue(binding, obj);\r
+                       return true;\r
+               } catch (ReferenceException re) {\r
+                       return false;\r
+               } catch (AccessorConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               }\r
+       }\r
+       
+       @Override
+       public Object getValue(Binding binding) throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Serializer s = params.serializerScheme.getSerializer( binding );
+                       b.position(0L);
+                       return s.deserialize(b, null);
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+       \r
+       public void getValue(Binding binding, Object obj) throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();\r
+               try {\r
+                       Serializer s = params.serializerScheme.getSerializer( binding );\r
+                       b.position(0L);\r
+                       s.deserializeTo(b, null, obj);\r
+               } catch (IOException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
+               try {\r
+                       Accessor a = getComponent(path);\r
+                       a.getValue(binding, obj);\r
+                       return true;\r
+               } catch (ReferenceException re) {\r
+                       return false;\r
+               } catch (AccessorConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               }\r
+       }       \r
+       \r
+       public Object getValue(ChildReference path, Binding binding) throws AccessorException {\r
+               try {\r
+                       Accessor a = getComponent(path);\r
+                       return a.getValue(binding);\r
+               } catch (ReferenceException re) {\r
+                       return null;\r
+               } catch (AccessorConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               }\r
+       }\r
+       \r
+       Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {\r
+               return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);\r
+       }       \r
+       
+       /**
+        * Send notification that this accessor has been detached from the parent
+        */
+       void invalidatedNotification() {
+               ListenerEntry le = listeners;
+               while (le!=null) {                      
+                       InterestSet is = le.getInterestSet();
+                       if (is.inNotifications()) {
+                               InvalidatedEvent e = new InvalidatedEvent();
+                               emitEvent(le, e);
+                       }
+                       le = le.next;
+               }               
+       }       
+       
+       /**
+        * Apply a change set that has events for the particular accessor. 
+        * There are no sub-accessor events. Does not flush buffer.
+        * 
+        * @param cs
+        * @param makeRollback
+        * @return rollback-event
+        * @throws AccessorException
+        */
+       abstract Event applyLocal(Event e, boolean makeRollback) throws AccessorException;
+       
+       @Override
+       public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {
+               assert b.isOpen();\r
+               writeLock();\r
+               try {
+                       boolean makeRollback = rollback != null;
+                       for (Event e : cs) {
+                               // Accessor
+                               BinaryObject a = e.reference == null ? this : (BinaryObject) getComponent(e.reference);
+                               // Apply changes                                
+                               Event rbe = a.applyLocal(e, makeRollback);
+                               if (makeRollback) {
+                                       rbe.reference = e.reference;
+                                       rollback.addFirst( rbe );
+                               }
+                       }
+                       // Flush after successful transaction
+                       try {
+                               b.flush();
+                       } catch (IOException e1) {
+                               throw new AccessorException(e1);
+                       }
+               } catch (AccessorConstructionException ae) {
+                       // Attempt to flush, don't report if fails
+                       // The contract is that all events that did go thru, created a rollback
+                       // and those must be flushed
+                       try {
+                               b.flush();
+                       } catch (IOException e1) {
+                       }
+                       throw new AccessorException(ae);
+               } finally {\r
+                       writeUnlock();\r
+               }
+               
+       }       
+       
+       @Override
+       public String toString() {              
+//             try {
+                       Datatype type = type();
+//                     Binding binding = params.bindingScheme.getBinding(type);
+//                     Object instance = getValue(binding);
+//                     return "Accessor("+binding.printValueDefinition(instance, true)+")";\r
+                       return this.getClass()+"("+type+")";
+//             } catch (AccessorException e) {
+//                     return "Accessor(error="+e.getMessage()+")";
+//             } catch (BindingException e) {
+//                     return "Accessor(error="+e.getMessage()+")";
+//             } catch (IOException e) {
+//                     return "Accessor(error="+e.getMessage()+")";
+//             } catch (BindingConstructionException e) {\r
+//                     return "Accessor(error="+e.getMessage()+")";\r
+//             }
+       }               
+       
+       public static BinaryObject createAccessor(RandomAccessBinary binary, Datatype type, AccessorParams params) throws AccessorConstructionException
+       {
+               try {
+                       Blob blob = binary instanceof Blob ? (Blob) binary : new Blob(binary);
+
+                       if (type instanceof BooleanType) {
+                               return new BinaryBoolean(null, blob, (BooleanType) type, params);
+                       }
+                       if (type instanceof ByteType) {
+                               return new BinaryByte(null, blob, (ByteType)type, params);
+                       }
+                       if (type instanceof IntegerType) {
+                               return new BinaryInteger(null, blob, (IntegerType)type, params);
+                       }
+                       if (type instanceof LongType) {
+                               return new BinaryLong(null, blob, (LongType)type, params);
+                       }
+                       if (type instanceof FloatType) {
+                               return new BinaryFloat(null, blob, (FloatType)type, params);
+                       }
+                       if (type instanceof DoubleType) {
+                               return new BinaryDouble(null, blob, (DoubleType)type, params);
+                       }
+                       if (type instanceof StringType) {
+                               return new BinaryString(null, blob, (StringType)type, params);
+                       }                       
+                       if (type instanceof OptionalType) {
+                               return new BinaryOptional(null, blob, (OptionalType)type, params);
+                       }                       
+                       if (type instanceof UnionType) {
+                               return new BinaryUnion(null, blob, (UnionType)type, params);
+                       }                       
+                       if (type instanceof RecordType) {
+                               return new BinaryRecord(null, blob, (RecordType)type, params);
+                       }                       
+                       if (type instanceof VariantType) {
+                               return new BinaryVariant(null, blob, (VariantType)type, params);
+                       }                       
+                       if (type instanceof MapType) {
+                               return new BinaryMap(null, blob, (MapType)type, params);
+                       }                       
+                       if (type instanceof ArrayType) {
+                               return new BinaryArray(null, blob, (ArrayType)type, params);\r
+                       }                                               
+                       \r
+                       throw new AccessorConstructionException("Can not create accessor to "+type);
+               } catch (IOException e) {
+                       throw new AccessorConstructionException(e);
+               }
+       }
+
+       BinaryObject createSubAccessor(Datatype type, long position, long length, AccessorParams params) 
+       throws AccessorConstructionException {
+               Blob sb = b.createSubBlob(position, length);            
+               
+               if (type instanceof BooleanType) {
+                       return new BinaryBoolean(this, sb, (BooleanType) type, params);
+               }
+               if (type instanceof ByteType) {
+                       return new BinaryByte(this, sb, (ByteType)type, params);
+               }
+               if (type instanceof IntegerType) {
+                       return new BinaryInteger(this, sb, (IntegerType)type, params);
+               }
+               if (type instanceof LongType) {
+                       return new BinaryLong(this, sb, (LongType)type, params);
+               }
+               if (type instanceof FloatType) {
+                       return new BinaryFloat(this, sb, (FloatType)type, params);
+               }
+               if (type instanceof DoubleType) {
+                       return new BinaryDouble(this, sb, (DoubleType)type, params);
+               }
+               if (type instanceof StringType) {
+                       return new BinaryString(this, sb, (StringType)type, params);
+               }
+               if (type instanceof OptionalType) {
+                       return new BinaryOptional(this, sb, (OptionalType)type, params);
+               }
+               if (type instanceof UnionType) {
+                       return new BinaryUnion(this, sb, (UnionType)type, params);
+               }
+               if (type instanceof VariantType) {
+                       return new BinaryVariant(this, sb, (VariantType)type, params);
+               }
+               if (type instanceof ArrayType) {
+                       return new BinaryArray(this, sb, (ArrayType)type, params);
+               }
+               if (type instanceof MapType) {
+                       return new BinaryMap(this, sb, (MapType)type, params);
+               }
+               if (type instanceof RecordType) {
+                       return new BinaryRecord(this, sb, (RecordType)type, params);
+               }
+               throw new AccessorConstructionException("Can not create accessor to "+type);
+       }       
+       \r
+       protected void emitEvent(ListenerEntry le, Event e) {           \r
+               e.reference = ChildReference.concatenate(le.path, e.reference);\r
+               le.emitEvent(e);\r
+       }       \r
+\r
+       protected void emitEvents(ListenerEntry le, Collection<Event> events) {\r
+               for (Event e : events)\r
+                       e.reference = ChildReference.concatenate(le.path, e.reference);\r
+               le.emitEvents(events);\r
+       }       \r
+       \r
+       /**\r
+        * Get lock if available. \r
+        * \r
+        * @return lock or <tt>null</tt>\r
+        */\r
+       public Lock getReadLock() {\r
+               return params.readLock;\r
+       }\r
+       \r
+       /**\r
+        * Get lock if available. \r
+        * \r
+        * @return lock or <tt>null</tt>\r
+        */\r
+       public Lock getWriteLock() {\r
+               return params.writeLock;\r
+       }\r
+       \r
+\r
+       /**\r
+        * Lock the lock if there is a lock.\r
+        */\r
+       protected void readLock() {\r
+               if (params.readLock!=null) params.readLock.lock();\r
+       }\r
+       \r
+       /**\r
+        * Unlock the lock if one exists\r
+        */\r
+       protected void readUnlock() {\r
+               if (params.readLock!=null) params.readLock.unlock();\r
+       }\r
+\r
+       /**\r
+        * Lock the lock if there is a lock.\r
+        */\r
+       protected void writeLock() {\r
+               if (params.writeLock!=null) params.writeLock.lock();\r
+       }\r
+       \r
+       /**\r
+        * Unlock the lock if one exists\r
+        */\r
+       protected void writeUnlock() {\r
+               if (params.writeLock!=null) params.writeLock.unlock();\r
+       }\r
+       \r
+       
+}
+