/******************************************************************************* * Copyright (c) 2010 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.databoard; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.Executor; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.ArrayAccessor; import org.simantics.databoard.accessor.binary.BinaryArray; import org.simantics.databoard.accessor.binary.BinaryObject; import org.simantics.databoard.accessor.binary.BinaryStreamArray; import org.simantics.databoard.accessor.binary.BinaryVariableWidthStreamArray; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.file.FileAccessor; import org.simantics.databoard.accessor.file.FileArrayAccessor; import org.simantics.databoard.accessor.file.FileLibrary; import org.simantics.databoard.accessor.file.FileVariantAccessor; import org.simantics.databoard.accessor.impl.AccessorParams; import org.simantics.databoard.accessor.impl.DirectoryMap; import org.simantics.databoard.accessor.java.JavaObject; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingConstructionException; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.serialization.SerializerConstructionException; import org.simantics.databoard.type.ArrayType; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.util.binary.BinaryFile; import org.simantics.databoard.util.binary.BinaryMemory; import org.simantics.databoard.util.binary.Blob; import org.simantics.databoard.util.binary.RandomAccessBinary; /** * This is a facade class for accessor services. * * @author Toni Kalajainen */ public class Accessors { /** * Open an accessor to byte data. * * @param binary * @param type * @param params * @return an accessor * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T getAccessor(RandomAccessBinary binary, Datatype type, AccessorParams params) throws AccessorConstructionException { return (T) BinaryObject.createAccessor(binary, type, params); } /** * Open an accessor to byte data. * * @param binary * @param type * @return an accessor * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T getAccessor(RandomAccessBinary binary, Datatype type) throws AccessorConstructionException { return (T) BinaryObject.createAccessor(binary, type, AccessorParams.DEFAULT); } /** * Open an accessor to byte data. * * @param binary * @param type * @return an accessor * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T getAccessor(byte[] binary, Datatype type) throws AccessorConstructionException { RandomAccessBinary rab = new BinaryMemory( ByteBuffer.wrap(binary) ); return (T) BinaryObject.createAccessor(rab, type, AccessorParams.DEFAULT); } /** * Open an accessor to a Java Object. *

* Accessor is disposed by leaving it to garbage collector. *

* Do not modify the object outside the accessor as long as you use the accessor. * Exterioir modifications will mix up listening the mechanism. * Also, do not create more than one accessor to one Object. *

* You must provide mutual exclusion locking mechanism to the whole Object Model * in AccessorParams, if you intend to use the accessors in multi-thread environment. * * @since 0.5 * @param binding * @param value * @return accessor * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T getAccessor(Binding binding, Object value, AccessorParams params) throws AccessorConstructionException { return (T) JavaObject.createAccessor(null, binding, value, params); } /** * Open an accessor to a Java Object. *

* Accessor is disposed by leaving it to garbage collector. *

* Do not modify the object outside the accessor as long as you use the accessor. * Exterioir modifications will mix up listening the mechanism. * Also, do not create more than one accessor to one Object. *

* The accessor is not multi-thread accessible. If you intend to use it in * concurrent multi-thread environment, use the other method where you can * pass lock objects in AccessorParams. * * @since 0.5 * @param binding * @param value * @return accessor * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T getAccessor(Binding binding, Object value) throws AccessorConstructionException { return (T) JavaObject.createAccessor(null, binding, value, AccessorParams.DEFAULT); } /** * Open an accessor to a Varint Object. *

* Accessor is disposed by leaving it to garbage collector. *

* Do not modify the object outside the accessor as long as you use the accessor. * Exterioir modifications will mix up listening the mechanism. * Also, do not create more than one accessor to one Object. *

* The accessor is not multi-thread accessible. If you intend to use it in * concurrent multi-thread environment, use the other method where you can * pass lock objects in AccessorParams. * * @since 0.5 * @param variant * @return accessor * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T getAccessor(Variant variant) throws AccessorConstructionException { return (T) JavaObject.createAccessor(null, variant.getBinding(), variant.getValue(), AccessorParams.DEFAULT); } /** * Open an accessor to a Varint Object. *

* Accessor is disposed by leaving it to garbage collector. *

* Do not modify the object outside the accessor as long as you use the accessor. * Exterioir modifications will mix up listening the mechanism. * Also, do not create more than one accessor to one Object. *

* The accessor is not multi-thread accessible. If you intend to use it in * concurrent multi-thread environment, use the other method where you can * pass lock objects in AccessorParams. * * @since 0.5 * @param binding * @param ref child reference * @return accessor * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T getAccessor(Variant variant, ChildReference ref) throws AccessorConstructionException { Accessor a = getAccessor( variant ); return a.getComponent( ref ); } /** * Open Accessor to a Java Object. This version reads the type using reflection. *

* Accessor is disposed by leaving it to garbage collector. *

* Do not modify the object outside the accessor as long as you use the accessor. * Exterioir modifications will mix up listening the mechanism. * Also, do not create more than one accessor to one Object. *

* The accessor is not multi-thread accessible. If you intend to use it in * concurrent multi-thread environment, use the other method where you can * pass lock objects in AccessorParams. * * @since 0.5 * @param value * @return accessor * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T getAccessor(Object value) throws AccessorConstructionException { try { Binding binding = Bindings.getBinding(value.getClass()); return (T) JavaObject.createAccessor(null, binding, value, AccessorParams.DEFAULT); } catch (BindingConstructionException e) { throw new AccessorConstructionException(e); } } /** * Open an accessor to a binary file (.dbb). The file is always a variant. * The accessor must be closed by invoking {@link FileAccessor#close()} at * root or any sub-accessor. *

* To share accessors of the same file use {@link FileLibrary} utility. * * @since 0.5 * @param file * @return file accessor * @throws AccessorConstructionException */ public static FileVariantAccessor openAccessor(File file) throws AccessorConstructionException { try { BinaryFile bf = new BinaryFile(file); FileVariantAccessor result = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, AccessorParams.DEFAULT); return result; } catch (IOException e) { throw new AccessorConstructionException(e); } } /** * Open a stream file (.stm). Stream file is an array of elements, with no * header. Element size is constant. *

* To create an empty stream file, just create an empty file. * * @param file * @param type expected array type * @return accessor * @throws AccessorConstructionException */ public static FileArrayAccessor openStream(File file, ArrayType type) throws AccessorConstructionException { return openStream(file, type, "rw"); } /** * Open a stream file (.stm). Stream file is an array of elements, with no * header. Element size is constant. *

* To create an empty stream file, just create an empty file. * * @param file * @param type expected array type * @param mode Mode "r" or "rw" * @return accessor * @throws AccessorConstructionException */ public static FileArrayAccessor openStream(File file, ArrayType type, String mode) throws AccessorConstructionException { return openStream(file, type, mode, null); } /** * Open a stream file (.stm). Stream file is an array of elements, with no * header. Element size is constant. *

* To create an empty stream file, just create an empty file. * * @param file * @param type expected array type * @param mode Mode "r" or "rw" * @param index accessor to long array that keeps the index of the variable width stream * @return accessor * @throws AccessorConstructionException */ public static FileArrayAccessor openStream(File file, ArrayType type, String mode, ArrayAccessor index) throws AccessorConstructionException { try { BinaryFile bf = new BinaryFile(file, mode); Blob blob = new Blob(bf); Binding b = Bindings.getBinding(type.componentType); Serializer s = Bindings.getSerializer(b); if ( s.getConstantSize() != null ) { return new BinaryStreamArray(null, blob, type, AccessorParams.DEFAULT); } else { if ( index == null ) { return new BinaryArray(null, blob, type, AccessorParams.DEFAULT); } else { return new BinaryVariableWidthStreamArray(null, blob, type, AccessorParams.DEFAULT, index); } } } catch (IOException e) { throw new AccessorConstructionException(e); } catch (AccessorException e) { throw new AccessorConstructionException(e); } catch (SerializerConstructionException e) { throw new AccessorConstructionException(e); } } /** * Create a new binary file with an empty value and open an accessor. * If file already exists, it is overwritten. * It is recommended that the file extension is .dbb (Databoard Binary) *

* The caller must close the file with {@link FileAccessor#close()}. * * @since 0.5 * @param file * @return an accessor to the root variant node */ public static FileVariantAccessor createFile(File file) throws AccessorConstructionException { try { file.createNewFile(); BinaryFile bf = new BinaryFile(file); FileVariantAccessor result = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, AccessorParams.DEFAULT); // Write initial value try { Binding vb = Bindings.getBinding(void.class); Object vv = vb.createDefault(); result.setContentValue(vb, vv); } catch (AccessorException e) { // Close file try { result.close(); } catch (AccessorException e1) {} // Rethrow exception throw new AccessorConstructionException(e); } catch (BindingConstructionException e) { // Close file try { result.close(); } catch (AccessorException e1) {} // Rethrow exception throw new AccessorConstructionException(e); } catch (BindingException e) { // Close file try { result.close(); } catch (AccessorException e1) {} // Rethrow exception throw new AccessorConstructionException(e); } return result; } catch (IOException e) { throw new AccessorConstructionException(e); } } /** * Create a binary file (.dbb) and open an accessor. The file is always a variant. *

* File is closed with {@link FileAccessor#close()} at root or any * sub-accessor. *

* To share accessors of the same file use {@link FileLibrary} utility. * * @param * @param file * @param type * @return an accessor to the value node * @throws AccessorConstructionException */ @SuppressWarnings("unchecked") public static T createFile(File file, Datatype type) throws AccessorConstructionException { boolean existed; BinaryFile bf; try { existed = file.createNewFile(); bf = new BinaryFile(file); } catch (IOException e1) { throw new AccessorConstructionException(e1); } try { FileVariantAccessor result = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, AccessorParams.DEFAULT); Binding binding = Bindings.getMutableBinding(type); Object value = binding.createDefault(); result.setContentValue(binding, value); return (T) result.getContentAccessor(); } catch (BindingException e) { if (!existed) file.delete(); throw new AccessorConstructionException(e); } catch (AccessorException e) { if (!existed) file.delete(); throw new AccessorConstructionException(e); } } /** * Open a Map(Variant, Variant) accessor to a directory. * Map entries are binary files (.dbb), and the file name is the key with the following encoding: * * Filenames have the following encoding: * S.dbb String types * Control characters " : < > | ? * \ / % [0..31] [128..] are escaped as % * " "-space is _ * I.dbb Integer types * L.dbb Long types * H.dbb All other cases the value as binary * * The caller must close (FolderMap#close()) the accessor after usage. * This releases file handles and a directory polling thread. * * FolderMap is not concurrent-use-safe. Its files are not either. * The user must ensure that the files are not used concurrently from * different threads. * * @param directory * @return MapAccessor */ public static DirectoryMap openDirectory(File directory) { return new DirectoryMap(directory); } /** * Get an executor for current thread * * @return executor */ public static Executor getCurrentThread() { return Accessors.getCurrentThread(); } static Executor CURRENT_THREAD = new Executor() { @Override public void execute(Runnable command) { command.run(); } }; }