]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.history/src/org/simantics/history/impl/FileHistory.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.history / src / org / simantics / history / impl / FileHistory.java
index 0ecefd484e88f33b3cea9b1b66298aa8eaf177d3..f3169b60673ede3a26bb72ed10b02784d912c300 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2011 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.history.impl;\r
-\r
-import java.io.DataInput;\r
-import java.io.DataOutput;\r
-import java.io.File;\r
-import java.io.FilenameFilter;\r
-import java.io.IOException;\r
-import java.nio.BufferUnderflowException;\r
-import java.nio.ByteBuffer;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-import java.util.logging.Logger;\r
-\r
-import org.simantics.databoard.Accessors;\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.accessor.ArrayAccessor;\r
-import org.simantics.databoard.accessor.StreamAccessor;\r
-import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
-import org.simantics.databoard.accessor.error.AccessorException;\r
-import org.simantics.databoard.adapter.AdaptException;\r
-import org.simantics.databoard.adapter.Adapter;\r
-import org.simantics.databoard.adapter.AdapterConstructionException;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.error.BindingException;\r
-import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;\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.Component;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.type.NumberType;\r
-import org.simantics.databoard.type.RecordType;\r
-import org.simantics.databoard.util.Bean;\r
-import org.simantics.databoard.util.ObjectUtils;\r
-import org.simantics.databoard.util.URIUtil;\r
-import org.simantics.databoard.util.binary.BinaryMemory;\r
-import org.simantics.databoard.util.binary.ByteBufferWriteable;\r
-import org.simantics.history.HistoryException;\r
-import org.simantics.history.HistoryManager;\r
-import org.simantics.utils.FileUtils;\r
-\r
-/**\r
- * File history uses workarea (directory) to manage items.\r
- * There are two files for every item: stream file and metadata file.\r
- * Metadata file is JSON ascii file.  \r
- *   itemname.data\r
- *   itemname.txt\r
- *  \r
- * @author toni.kalajainen\r
- *\r
- */\r
-public class FileHistory implements HistoryManager {\r
-\r
-       private static final boolean PROFILE = false;\r
-       private static final boolean DEBUG = false;\r
-\r
-       /** Logger */\r
-       static Logger logger = Logger.getLogger(FileHistory.class.getName());\r
-       \r
-       static FilenameFilter txtFilter;\r
-       \r
-       static ArrayType INDEX_TYPE = Bindings.LONG_ARRAY.type();\r
-       \r
-       File workarea;\r
-       \r
-       /** Support async usage of the data */\r
-       public boolean asyncUsage = true;\r
-\r
-       public FileHistory(File workarea) {\r
-               this.workarea = workarea;\r
-       }\r
-       \r
-    public File getWorkarea() {\r
-       return workarea;\r
-    }\r
-       \r
-       @Override\r
-       public void create(Bean... items) throws HistoryException {\r
-               \r
-               try {\r
-                       for (Bean item : items) {\r
-                               if (DEBUG)\r
-                                       System.out.println("create(" + item + ")");\r
-//                             if ( !item.getFieldBinding("format").type().equals( Bindings.getBindingUnchecked(Datatype.class).type() ) )\r
-//                                     System.err.println("Error");\r
-                               \r
-                               // Write meta data\r
-                               writeMetadata( item );\r
-                               \r
-                               // Create stream file\r
-                               String id = (String) item.getField("id");\r
-                               File dataFile = toDataFile( id );\r
-                               dataFile.createNewFile();\r
-                               \r
-                               Datatype type = (Datatype) item.getField("format");\r
-                               if ( isVariableWidth(type) ) {\r
-                                       File indexFile = toIndexFile( id );\r
-                                       indexFile.createNewFile();\r
-                               }\r
-//                             if ( !dataFile.createNewFile() ) {\r
-//                                     throw new HistoryException("Could not create file "+dataFile);\r
-//                             }\r
-                       }               \r
-               } catch (BindingException e) {\r
-                       throw new HistoryException(e);\r
-               } catch (IOException e) {\r
-                       throw new HistoryException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public void delete(String... itemIds) throws HistoryException {\r
-               for (String itemId : itemIds ) {\r
-                       File meta = toMetaFile( itemId );\r
-                       File data = toDataFile( itemId );\r
-                       File index = toIndexFile( itemId );\r
-                       if ( meta.exists() ) {\r
-                               if ( !meta.delete() ) {\r
-                                       throw new HistoryException("Failed to delete "+meta);\r
-                               }\r
-                       }\r
-                       if ( data.exists() ) {\r
-                               if ( !data.delete() ) {\r
-                                       throw new HistoryException("Failed to delete "+data);\r
-                               }\r
-                       }\r
-                       if ( index.exists() ) {\r
-                               if ( !index.delete() );\r
-                       }\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public void modify(Bean... items) throws HistoryException {\r
-               \r
-               try {\r
-                       for ( Bean item : items ) {\r
-                               if (DEBUG)\r
-                                       System.out.println("modify(" + item + ")");\r
-//                             if ( !item.getFieldBinding("format").type().equals( Bindings.getBindingUnchecked(Datatype.class).type() ) )\r
-//                                     System.err.println("Error");\r
-\r
-                               String id = (String) item.getField("id");\r
-                               File metaFile = toMetaFile( id );\r
-                               if ( !metaFile.exists() ) {\r
-                                       create( item );\r
-                               } else {\r
-                                       Bean oldItem = getItem( id );\r
-                                       File dataFile = toDataFile( id );\r
-                                       if ( dataFile.exists() ) {\r
-                                               boolean enabled = item.hasField("enabled") ? (Boolean) item.getFieldUnchecked("enabled") : true;\r
-                                               Datatype oldFormat = (Datatype) oldItem.getField( "format" );\r
-                                               Datatype newFormat = (Datatype) item.getField( "format" );\r
-                                               if (DEBUG)\r
-                                                       System.out.println("formats: " + oldFormat +  " -> " + newFormat);\r
-                                               Datatype unitStrippedOldFormat = stripUnitAnnotations(oldFormat);\r
-                                               Datatype unitStrippedNewFormat = stripUnitAnnotations(newFormat);\r
-                                               if (DEBUG)\r
-                                                       System.out.println("formats after unit strip: " + unitStrippedOldFormat +  " -> " + unitStrippedNewFormat);\r
-                                               if ( enabled && !unitStrippedOldFormat.equals(unitStrippedNewFormat) ) {\r
-                                                       try {\r
-                                                               Binding oldBinding = Bindings.getBeanBinding(unitStrippedOldFormat);\r
-                                                               Binding newBinding = Bindings.getBeanBinding(unitStrippedNewFormat);\r
-                                                               Serializer oldS = Bindings.getSerializer(oldBinding);\r
-                                                               Serializer newS = Bindings.getSerializer(newBinding);\r
-                                                               if (oldS.getConstantSize()==null || newS.getConstantSize()==null || oldS.getConstantSize()!=newS.getConstantSize())\r
-                                                                       throw new HistoryException("Changing of file format is not supported to: "+dataFile);\r
-                                                               Adapter adapter = Bindings.getAdapter(oldBinding, newBinding);\r
-                                                               Object oldSample = oldBinding.createDefault();\r
-                                                               Object newSample = newBinding.createDefault();\r
-                                                               StreamAccessor sa = openStream(id, "rw");\r
-                                                               try {\r
-                                                                       int c = sa.size();\r
-                                                                       for (int i=0; i<c; i++) {\r
-                                                                               sa.get(i, oldBinding, oldSample);\r
-                                                                               newSample = adapter.adapt(oldSample);\r
-                                                                               sa.set(i, newBinding, newSample);\r
-                                                                       }\r
-                                                               } finally {\r
-                                                                       sa.close();\r
-                                                               }\r
-                                                       } catch (AdapterConstructionException e) {\r
-                                                               throw new HistoryException("Changing of file format is not supported to: "+id);\r
-                                                       } catch (SerializerConstructionException e) {\r
-                                                               throw new HistoryException("Changing of file format is not supported to: "+id);\r
-                                                       } catch (AccessorException e) {\r
-                                                               throw new HistoryException("Changing of file format failed to: "+id);\r
-                                                       } catch (AdaptException e) {\r
-                                                               throw new HistoryException("Changing of file format failed to: "+id);\r
-                                                       }\r
-                                               }\r
-                                       } else {\r
-                                               dataFile.createNewFile();\r
-                                       }\r
-\r
-                                       // Write new meta-data if necessary\r
-                                       if (!equalsWithoutState(item, oldItem))\r
-                                               writeMetadata( item );\r
-                               }\r
-                       }\r
-               } catch (BindingException e) {\r
-                       throw new HistoryException( e );\r
-               } catch (IOException e) {\r
-                       throw new HistoryException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public Bean getItem(String itemId) throws HistoryException {\r
-               return getItem( toMetaFile( itemId ) );\r
-       }\r
-       \r
-       void writeMetadata( Bean item ) throws HistoryException {\r
-               \r
-//             long s = System.nanoTime();\r
-               try {\r
-                       String id = (String) item.getField("id");\r
-                       String idEnc  = URIUtil.encodeURI(id);\r
-                       File metaFile = new File(workarea, idEnc + ".txt");\r
-                       Serializer typeSerializer = Bindings.getSerializer( Bindings.getBindingUnchecked(Datatype.class) );\r
-                       Serializer beanSerializer = Bindings.getSerializer( item.getBinding() );\r
-                       int size = typeSerializer.getSize(item.getBinding().type()) +\r
-                                       beanSerializer.getSize(item);\r
-                       byte data[] = new byte[size];\r
-                       DataOutput out = new ByteBufferWriteable( ByteBuffer.wrap(data) );\r
-                       \r
-                       if ( metaFile.exists() && asyncUsage ) {\r
-                               if (DEBUG)\r
-                                       System.out.println("WARNING: FileHistory.writeMetadata: on SLOW path for " + item);\r
-                               File tmpFile = new File(workarea, idEnc + ".tmp");\r
-                               File tmp2File = new File(workarea, idEnc + ".tmp2");\r
-                               tmpFile.delete();\r
-       \r
-                               typeSerializer.serialize(out, item.getBinding().type());\r
-                               beanSerializer.serialize(out, item);\r
-                               FileUtils.writeFile(tmpFile, data);\r
-                               metaFile.renameTo(tmp2File);\r
-                               tmpFile.renameTo(metaFile);\r
-                               tmp2File.delete();\r
-                       } else {\r
-                               typeSerializer.serialize(out, item.getBinding().type());\r
-                               beanSerializer.serialize(out, item);\r
-                               FileUtils.writeFile(metaFile, data);\r
-                       }\r
-\r
-//                     if (PROFILE)\r
-//                             System.out.println("PROFILE: FileHistory.writeMetadata( " + metaFile.getName() + " ) in " + ((System.nanoTime() - s)*1e-6) + " ms");\r
-               } catch (BindingException e) {\r
-                       throw new HistoryException(e);\r
-               } catch (IOException e) {\r
-                       throw new HistoryException(e);\r
-               } catch (SerializerConstructionException e) {\r
-                       throw new HistoryException(e);\r
-               }\r
-       }\r
-\r
-       Bean getItem(File file) throws HistoryException {\r
-//             FileInputStream fis;\r
-//             try {\r
-//                     fis = new FileInputStream(file);\r
-//             } catch (FileNotFoundException e1) {\r
-//                     throw new HistoryException(e1);\r
-//             }\r
-               try {\r
-                       byte[] data = FileUtils.readFile(file);\r
-                       DataInput in = new BinaryMemory(data);  \r
-                       Serializer typeSerializer = Bindings.getSerializer( Bindings.getBindingUnchecked(Datatype.class) );                     \r
-                       Datatype type = (Datatype) typeSerializer.deserialize(in);\r
-                       Binding beanBinding = Bindings.getBeanBinding( type );\r
-                       Serializer s = Bindings.getSerializer( beanBinding );\r
-                       Bean bean = (Bean) s.deserialize(in);\r
-                       /*\r
-                       DataInput in = new InputStreamReadable( fis, file.length() );\r
-                       Serializer typeSerializer = Bindings.getSerializer( Bindings.getBindingUnchecked(Datatype.class) );                     \r
-                       Datatype type = (Datatype) typeSerializer.deserialize(in);\r
-                       Binding beanBinding = Bindings.getBeanBinding( type );\r
-                       Serializer s = Bindings.getSerializer( beanBinding );\r
-                       Bean bean = (Bean) s.deserialize(in);\r
-                       */\r
-                       return bean;\r
-/*                     String txt = new String(data, UTF8.CHARSET);                    \r
-                       DataValueRepository repo = new DataValueRepository();\r
-                       String name = repo.addValueDefinition(txt);\r
-                       MutableVariant value = repo.get(name);\r
-                       Binding beanBinding = Bindings.getBeanBinding( value.type() );\r
-                       return (Bean) value.getValue(beanBinding);*/\r
-               } catch(BufferUnderflowException e) {\r
-                       throw new HistoryException( e );\r
-               } catch(IOException e) {\r
-                       throw new HistoryException( e );\r
-//             } catch (DataTypeSyntaxError e) {\r
-//                     throw new HistoryException( e );\r
-               } catch (RuntimeBindingConstructionException e) {\r
-                       throw new HistoryException( e );\r
-               } catch (SerializerConstructionException e) {\r
-                       throw new HistoryException( e );\r
-               } finally {\r
-//                     try {\r
-//                             fis.close();\r
-//                     } catch (IOException e) {\r
-//                     }\r
-               }\r
-       }\r
-       \r
-       File toMetaFile(String itemId)\r
-       {\r
-               String name = URIUtil.encodeURI(itemId) + ".txt";\r
-               File f = new File(workarea, name);\r
-               return f;\r
-       }\r
-\r
-       File toDataFile(String itemId)\r
-       {\r
-               String name = URIUtil.encodeURI(itemId) + ".data";\r
-               File f = new File(workarea, name);\r
-               return f;\r
-       }\r
-       \r
-       File toIndexFile(String itemId)\r
-       {\r
-               String name = URIUtil.encodeURI(itemId) + ".index";\r
-               File f = new File(workarea, name);\r
-               return f;\r
-       }\r
-       \r
-       boolean isVariableWidth(Datatype type) \r
-       throws HistoryException {\r
-               try {\r
-                       Binding beanBinding = Bindings.getBeanBinding(type);\r
-                       Serializer s = Bindings.getSerializer( beanBinding );\r
-                       return s.getConstantSize() == null;\r
-               } catch (SerializerConstructionException e) {\r
-                       throw new HistoryException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public Bean[] getItems() throws HistoryException {\r
-               List<Bean> result = new ArrayList<Bean>();\r
-               File[] files = workarea.listFiles(txtFilter);\r
-               if ( files != null ) {\r
-                       for (File file : files) {\r
-                               result.add( getItem(file) );\r
-                       }\r
-               }\r
-               return result.toArray( new Bean[ result.size() ] );\r
-       }\r
-\r
-       @Override\r
-       public void close() {\r
-               // Nothing to do.\r
-       }\r
-\r
-       @Override\r
-       public StreamAccessor openStream(String itemId, String mode) throws HistoryException {\r
-               try {\r
-                       Bean bean = getItem(itemId);\r
-                       Datatype format = (Datatype) bean.getField("format");\r
-                       ArrayType arrayType = new ArrayType(format);\r
-                       File dataFile = toDataFile( itemId );\r
-                       if ( isVariableWidth(format) ) {\r
-                               File indexFile = toIndexFile( itemId );\r
-                               ArrayAccessor index = Accessors.openStream(indexFile, INDEX_TYPE, mode);\r
-                               return (StreamAccessor) Accessors.openStream(dataFile, arrayType, mode, index);\r
-                       } else {\r
-                               return (StreamAccessor) Accessors.openStream(dataFile, arrayType, mode);\r
-                       }\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new HistoryException(e);\r
-               } catch (BindingException e) {\r
-                       throw new HistoryException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public boolean exists(String itemId) throws HistoryException {\r
-               return toMetaFile(itemId).exists();\r
-       }\r
-       \r
-       @Override\r
-       public int hashCode() {\r
-               return workarea.hashCode();\r
-       }\r
-       \r
-       @Override\r
-       public boolean equals(Object obj) {\r
-               if ( obj==null ) return false;\r
-               if ( obj instanceof FileHistory == false ) return false;\r
-               FileHistory other = (FileHistory) obj;          \r
-               return other.workarea.equals(workarea);\r
-       }\r
-       \r
-       @Override\r
-       public String toString() {\r
-               return "FileHistory: "+workarea;\r
-       }\r
-\r
-       private boolean equalsWithoutState(Bean i1, Bean i2) {\r
-               Component[] components1 = i1.getBinding().type().getComponents();\r
-               Component[] components2 = i2.getBinding().type().getComponents();\r
-               int components = Math.min(components1.length, components2.length);\r
-               for (int c = 0; c < components; ++c) {\r
-                       Object o1 = i1.getFieldUnchecked(c);\r
-                       Object o2 = i2.getFieldUnchecked(c);\r
-                       if ("collectorState".equals(components1[c].name) && (o1 == null || o2 == null))\r
-                               continue;\r
-                       if (!ObjectUtils.objectEquals(o1, o2))\r
-                               return false;\r
-               }\r
-               return true;\r
-       }\r
-\r
-       static {\r
-               txtFilter = new FilenameFilter() {\r
-                       public boolean accept(File dir, String name) {\r
-                               return name.toLowerCase().endsWith(".txt");\r
-                       }\r
-               };\r
-       }\r
-\r
-       private static Datatype stripUnitAnnotations(Datatype datatype) {\r
-               if (datatype instanceof NumberType) {\r
-                       NumberType nt = (NumberType) datatype;\r
-                       if (nt.getUnit() != null) {\r
-                               Binding dtb = Bindings.getBindingUnchecked(Datatype.class);\r
-                               datatype = nt = (NumberType) Bindings.cloneUnchecked(datatype, dtb, dtb);\r
-                               nt.setUnit(null);\r
-                       }\r
-               } else if (datatype instanceof ArrayType) {\r
-                       ArrayType at = (ArrayType) datatype;\r
-                       Datatype ct = at.componentType();\r
-                       Datatype component = stripUnitAnnotations(ct);\r
-                       if (component != ct) {\r
-                               Binding dtb = Bindings.getBindingUnchecked(Datatype.class);\r
-                               datatype = at = (ArrayType) Bindings.cloneUnchecked(datatype, dtb, dtb);\r
-                               at.setComponentType(component);\r
-                       }\r
-               } else if (datatype instanceof RecordType) {\r
-                       RecordType rt = (RecordType) datatype;\r
-                       int componentCount = rt.getComponentCount();\r
-                       Component[] newComponents = new Component[componentCount];\r
-                       for (int i = 0; i < componentCount; ++i) {\r
-                               Component c = rt.getComponent(i);\r
-                               Datatype ct = c.type;\r
-                               Datatype sct = stripUnitAnnotations(ct);\r
-                               newComponents[i] = new Component(c.name, sct);\r
-                       }\r
-                       return new RecordType(rt.isReferable(), newComponents);\r
-               }\r
-               return datatype;\r
-       }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.history.impl;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.simantics.databoard.Accessors;
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.accessor.ArrayAccessor;
+import org.simantics.databoard.accessor.StreamAccessor;
+import org.simantics.databoard.accessor.error.AccessorConstructionException;
+import org.simantics.databoard.accessor.error.AccessorException;
+import org.simantics.databoard.adapter.AdaptException;
+import org.simantics.databoard.adapter.Adapter;
+import org.simantics.databoard.adapter.AdapterConstructionException;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.serialization.SerializerConstructionException;
+import org.simantics.databoard.type.ArrayType;
+import org.simantics.databoard.type.Component;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.NumberType;
+import org.simantics.databoard.type.RecordType;
+import org.simantics.databoard.util.Bean;
+import org.simantics.databoard.util.ObjectUtils;
+import org.simantics.databoard.util.URIUtil;
+import org.simantics.databoard.util.binary.BinaryMemory;
+import org.simantics.databoard.util.binary.ByteBufferWriteable;
+import org.simantics.history.HistoryException;
+import org.simantics.history.HistoryManager;
+import org.simantics.utils.FileUtils;
+
+/**
+ * File history uses workarea (directory) to manage items.
+ * There are two files for every item: stream file and metadata file.
+ * Metadata file is JSON ascii file.  
+ *   itemname.data
+ *   itemname.txt
+ *  
+ * @author toni.kalajainen
+ *
+ */
+public class FileHistory implements HistoryManager {
+
+       private static final boolean PROFILE = false;
+       private static final boolean DEBUG = false;
+
+       /** Logger */
+       static Logger logger = Logger.getLogger(FileHistory.class.getName());
+       
+       static FilenameFilter txtFilter;
+       
+       static ArrayType INDEX_TYPE = Bindings.LONG_ARRAY.type();
+       
+       File workarea;
+       
+       /** Support async usage of the data */
+       public boolean asyncUsage = true;
+
+       public FileHistory(File workarea) {
+               this.workarea = workarea;
+       }
+       
+    public File getWorkarea() {
+       return workarea;
+    }
+       
+       @Override
+       public void create(Bean... items) throws HistoryException {
+               
+               try {
+                       for (Bean item : items) {
+                               if (DEBUG)
+                                       System.out.println("create(" + item + ")");
+//                             if ( !item.getFieldBinding("format").type().equals( Bindings.getBindingUnchecked(Datatype.class).type() ) )
+//                                     System.err.println("Error");
+                               
+                               // Write meta data
+                               writeMetadata( item );
+                               
+                               // Create stream file
+                               String id = (String) item.getField("id");
+                               File dataFile = toDataFile( id );
+                               dataFile.createNewFile();
+                               
+                               Datatype type = (Datatype) item.getField("format");
+                               if ( isVariableWidth(type) ) {
+                                       File indexFile = toIndexFile( id );
+                                       indexFile.createNewFile();
+                               }
+//                             if ( !dataFile.createNewFile() ) {
+//                                     throw new HistoryException("Could not create file "+dataFile);
+//                             }
+                       }               
+               } catch (BindingException e) {
+                       throw new HistoryException(e);
+               } catch (IOException e) {
+                       throw new HistoryException(e);
+               }
+       }
+
+       @Override
+       public void delete(String... itemIds) throws HistoryException {
+               for (String itemId : itemIds ) {
+                       File meta = toMetaFile( itemId );
+                       File data = toDataFile( itemId );
+                       File index = toIndexFile( itemId );
+                       if ( meta.exists() ) {
+                               if ( !meta.delete() ) {
+                                       throw new HistoryException("Failed to delete "+meta);
+                               }
+                       }
+                       if ( data.exists() ) {
+                               if ( !data.delete() ) {
+                                       throw new HistoryException("Failed to delete "+data);
+                               }
+                       }
+                       if ( index.exists() ) {
+                               if ( !index.delete() );
+                       }
+               }
+       }
+
+       @Override
+       public void modify(Bean... items) throws HistoryException {
+               
+               try {
+                       for ( Bean item : items ) {
+                               if (DEBUG)
+                                       System.out.println("modify(" + item + ")");
+//                             if ( !item.getFieldBinding("format").type().equals( Bindings.getBindingUnchecked(Datatype.class).type() ) )
+//                                     System.err.println("Error");
+
+                               String id = (String) item.getField("id");
+                               File metaFile = toMetaFile( id );
+                               if ( !metaFile.exists() ) {
+                                       create( item );
+                               } else {
+                                       Bean oldItem = getItem( id );
+                                       File dataFile = toDataFile( id );
+                                       if ( dataFile.exists() ) {
+                                               boolean enabled = item.hasField("enabled") ? (Boolean) item.getFieldUnchecked("enabled") : true;
+                                               Datatype oldFormat = (Datatype) oldItem.getField( "format" );
+                                               Datatype newFormat = (Datatype) item.getField( "format" );
+                                               if (DEBUG)
+                                                       System.out.println("formats: " + oldFormat +  " -> " + newFormat);
+                                               Datatype unitStrippedOldFormat = stripUnitAnnotations(oldFormat);
+                                               Datatype unitStrippedNewFormat = stripUnitAnnotations(newFormat);
+                                               if (DEBUG)
+                                                       System.out.println("formats after unit strip: " + unitStrippedOldFormat +  " -> " + unitStrippedNewFormat);
+                                               if ( enabled && !unitStrippedOldFormat.equals(unitStrippedNewFormat) ) {
+                                                       try {
+                                                               Binding oldBinding = Bindings.getBeanBinding(unitStrippedOldFormat);
+                                                               Binding newBinding = Bindings.getBeanBinding(unitStrippedNewFormat);
+                                                               Serializer oldS = Bindings.getSerializer(oldBinding);
+                                                               Serializer newS = Bindings.getSerializer(newBinding);
+                                                               if (oldS.getConstantSize()==null || newS.getConstantSize()==null || oldS.getConstantSize()!=newS.getConstantSize())
+                                                                       throw new HistoryException("Changing of file format is not supported to: "+dataFile);
+                                                               Adapter adapter = Bindings.getAdapter(oldBinding, newBinding);
+                                                               Object oldSample = oldBinding.createDefault();
+                                                               Object newSample = newBinding.createDefault();
+                                                               StreamAccessor sa = openStream(id, "rw");
+                                                               try {
+                                                                       int c = sa.size();
+                                                                       for (int i=0; i<c; i++) {
+                                                                               sa.get(i, oldBinding, oldSample);
+                                                                               newSample = adapter.adapt(oldSample);
+                                                                               sa.set(i, newBinding, newSample);
+                                                                       }
+                                                               } finally {
+                                                                       sa.close();
+                                                               }
+                                                       } catch (AdapterConstructionException e) {
+                                                               throw new HistoryException("Changing of file format is not supported to: "+id);
+                                                       } catch (SerializerConstructionException e) {
+                                                               throw new HistoryException("Changing of file format is not supported to: "+id);
+                                                       } catch (AccessorException e) {
+                                                               throw new HistoryException("Changing of file format failed to: "+id);
+                                                       } catch (AdaptException e) {
+                                                               throw new HistoryException("Changing of file format failed to: "+id);
+                                                       }
+                                               }
+                                       } else {
+                                               dataFile.createNewFile();
+                                       }
+
+                                       // Write new meta-data if necessary
+                                       if (!equalsWithoutState(item, oldItem))
+                                               writeMetadata( item );
+                               }
+                       }
+               } catch (BindingException e) {
+                       throw new HistoryException( e );
+               } catch (IOException e) {
+                       throw new HistoryException( e );
+               }
+       }
+
+       @Override
+       public Bean getItem(String itemId) throws HistoryException {
+               return getItem( toMetaFile( itemId ) );
+       }
+       
+       void writeMetadata( Bean item ) throws HistoryException {
+               
+//             long s = System.nanoTime();
+               try {
+                       String id = (String) item.getField("id");
+                       String idEnc  = URIUtil.encodeURI(id);
+                       File metaFile = new File(workarea, idEnc + ".txt");
+                       Serializer typeSerializer = Bindings.getSerializer( Bindings.getBindingUnchecked(Datatype.class) );
+                       Serializer beanSerializer = Bindings.getSerializer( item.getBinding() );
+                       int size = typeSerializer.getSize(item.getBinding().type()) +
+                                       beanSerializer.getSize(item);
+                       byte data[] = new byte[size];
+                       DataOutput out = new ByteBufferWriteable( ByteBuffer.wrap(data) );
+                       
+                       if ( metaFile.exists() && asyncUsage ) {
+                               if (DEBUG)
+                                       System.out.println("WARNING: FileHistory.writeMetadata: on SLOW path for " + item);
+                               File tmpFile = new File(workarea, idEnc + ".tmp");
+                               File tmp2File = new File(workarea, idEnc + ".tmp2");
+                               tmpFile.delete();
+       
+                               typeSerializer.serialize(out, item.getBinding().type());
+                               beanSerializer.serialize(out, item);
+                               FileUtils.writeFile(tmpFile, data);
+                               metaFile.renameTo(tmp2File);
+                               tmpFile.renameTo(metaFile);
+                               tmp2File.delete();
+                       } else {
+                               typeSerializer.serialize(out, item.getBinding().type());
+                               beanSerializer.serialize(out, item);
+                               FileUtils.writeFile(metaFile, data);
+                       }
+
+//                     if (PROFILE)
+//                             System.out.println("PROFILE: FileHistory.writeMetadata( " + metaFile.getName() + " ) in " + ((System.nanoTime() - s)*1e-6) + " ms");
+               } catch (BindingException e) {
+                       throw new HistoryException(e);
+               } catch (IOException e) {
+                       throw new HistoryException(e);
+               } catch (SerializerConstructionException e) {
+                       throw new HistoryException(e);
+               }
+       }
+
+       Bean getItem(File file) throws HistoryException {
+//             FileInputStream fis;
+//             try {
+//                     fis = new FileInputStream(file);
+//             } catch (FileNotFoundException e1) {
+//                     throw new HistoryException(e1);
+//             }
+               try {
+                       byte[] data = FileUtils.readFile(file);
+                       DataInput in = new BinaryMemory(data);  
+                       Serializer typeSerializer = Bindings.getSerializer( Bindings.getBindingUnchecked(Datatype.class) );                     
+                       Datatype type = (Datatype) typeSerializer.deserialize(in);
+                       Binding beanBinding = Bindings.getBeanBinding( type );
+                       Serializer s = Bindings.getSerializer( beanBinding );
+                       Bean bean = (Bean) s.deserialize(in);
+                       /*
+                       DataInput in = new InputStreamReadable( fis, file.length() );
+                       Serializer typeSerializer = Bindings.getSerializer( Bindings.getBindingUnchecked(Datatype.class) );                     
+                       Datatype type = (Datatype) typeSerializer.deserialize(in);
+                       Binding beanBinding = Bindings.getBeanBinding( type );
+                       Serializer s = Bindings.getSerializer( beanBinding );
+                       Bean bean = (Bean) s.deserialize(in);
+                       */
+                       return bean;
+/*                     String txt = new String(data, UTF8.CHARSET);                    
+                       DataValueRepository repo = new DataValueRepository();
+                       String name = repo.addValueDefinition(txt);
+                       MutableVariant value = repo.get(name);
+                       Binding beanBinding = Bindings.getBeanBinding( value.type() );
+                       return (Bean) value.getValue(beanBinding);*/
+               } catch(BufferUnderflowException e) {
+                       throw new HistoryException( e );
+               } catch(IOException e) {
+                       throw new HistoryException( e );
+//             } catch (DataTypeSyntaxError e) {
+//                     throw new HistoryException( e );
+               } catch (RuntimeBindingConstructionException e) {
+                       throw new HistoryException( e );
+               } catch (SerializerConstructionException e) {
+                       throw new HistoryException( e );
+               } finally {
+//                     try {
+//                             fis.close();
+//                     } catch (IOException e) {
+//                     }
+               }
+       }
+       
+       File toMetaFile(String itemId)
+       {
+               String name = URIUtil.encodeURI(itemId) + ".txt";
+               File f = new File(workarea, name);
+               return f;
+       }
+
+       File toDataFile(String itemId)
+       {
+               String name = URIUtil.encodeURI(itemId) + ".data";
+               File f = new File(workarea, name);
+               return f;
+       }
+       
+       File toIndexFile(String itemId)
+       {
+               String name = URIUtil.encodeURI(itemId) + ".index";
+               File f = new File(workarea, name);
+               return f;
+       }
+       
+       boolean isVariableWidth(Datatype type) 
+       throws HistoryException {
+               try {
+                       Binding beanBinding = Bindings.getBeanBinding(type);
+                       Serializer s = Bindings.getSerializer( beanBinding );
+                       return s.getConstantSize() == null;
+               } catch (SerializerConstructionException e) {
+                       throw new HistoryException(e);
+               }
+       }
+
+       @Override
+       public Bean[] getItems() throws HistoryException {
+               List<Bean> result = new ArrayList<Bean>();
+               File[] files = workarea.listFiles(txtFilter);
+               if ( files != null ) {
+                       for (File file : files) {
+                               result.add( getItem(file) );
+                       }
+               }
+               return result.toArray( new Bean[ result.size() ] );
+       }
+
+       @Override
+       public void close() {
+               // Nothing to do.
+       }
+
+       @Override
+       public StreamAccessor openStream(String itemId, String mode) throws HistoryException {
+               try {
+                       Bean bean = getItem(itemId);
+                       Datatype format = (Datatype) bean.getField("format");
+                       ArrayType arrayType = new ArrayType(format);
+                       File dataFile = toDataFile( itemId );
+                       if ( isVariableWidth(format) ) {
+                               File indexFile = toIndexFile( itemId );
+                               ArrayAccessor index = Accessors.openStream(indexFile, INDEX_TYPE, mode);
+                               return (StreamAccessor) Accessors.openStream(dataFile, arrayType, mode, index);
+                       } else {
+                               return (StreamAccessor) Accessors.openStream(dataFile, arrayType, mode);
+                       }
+               } catch (AccessorConstructionException e) {
+                       throw new HistoryException(e);
+               } catch (BindingException e) {
+                       throw new HistoryException(e);
+               }
+       }
+
+       @Override
+       public boolean exists(String itemId) throws HistoryException {
+               return toMetaFile(itemId).exists();
+       }
+       
+       @Override
+       public int hashCode() {
+               return workarea.hashCode();
+       }
+       
+       @Override
+       public boolean equals(Object obj) {
+               if ( obj==null ) return false;
+               if ( obj instanceof FileHistory == false ) return false;
+               FileHistory other = (FileHistory) obj;          
+               return other.workarea.equals(workarea);
+       }
+       
+       @Override
+       public String toString() {
+               return "FileHistory: "+workarea;
+       }
+
+       private boolean equalsWithoutState(Bean i1, Bean i2) {
+               Component[] components1 = i1.getBinding().type().getComponents();
+               Component[] components2 = i2.getBinding().type().getComponents();
+               int components = Math.min(components1.length, components2.length);
+               for (int c = 0; c < components; ++c) {
+                       Object o1 = i1.getFieldUnchecked(c);
+                       Object o2 = i2.getFieldUnchecked(c);
+                       if ("collectorState".equals(components1[c].name) && (o1 == null || o2 == null))
+                               continue;
+                       if (!ObjectUtils.objectEquals(o1, o2))
+                               return false;
+               }
+               return true;
+       }
+
+       static {
+               txtFilter = new FilenameFilter() {
+                       public boolean accept(File dir, String name) {
+                               return name.toLowerCase().endsWith(".txt");
+                       }
+               };
+       }
+
+       private static Datatype stripUnitAnnotations(Datatype datatype) {
+               if (datatype instanceof NumberType) {
+                       NumberType nt = (NumberType) datatype;
+                       if (nt.getUnit() != null) {
+                               Binding dtb = Bindings.getBindingUnchecked(Datatype.class);
+                               datatype = nt = (NumberType) Bindings.cloneUnchecked(datatype, dtb, dtb);
+                               nt.setUnit(null);
+                       }
+               } else if (datatype instanceof ArrayType) {
+                       ArrayType at = (ArrayType) datatype;
+                       Datatype ct = at.componentType();
+                       Datatype component = stripUnitAnnotations(ct);
+                       if (component != ct) {
+                               Binding dtb = Bindings.getBindingUnchecked(Datatype.class);
+                               datatype = at = (ArrayType) Bindings.cloneUnchecked(datatype, dtb, dtb);
+                               at.setComponentType(component);
+                       }
+               } else if (datatype instanceof RecordType) {
+                       RecordType rt = (RecordType) datatype;
+                       int componentCount = rt.getComponentCount();
+                       Component[] newComponents = new Component[componentCount];
+                       for (int i = 0; i < componentCount; ++i) {
+                               Component c = rt.getComponent(i);
+                               Datatype ct = c.type;
+                               Datatype sct = stripUnitAnnotations(ct);
+                               newComponents[i] = new Component(c.name, sct);
+                       }
+                       return new RecordType(rt.isReferable(), newComponents);
+               }
+               return datatype;
+       }
+
+}