]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.graph/src/org/simantics/graph/representation/TransferableGraphFileReader.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.graph / src / org / simantics / graph / representation / TransferableGraphFileReader.java
index ae1ec711ce676e9d3932cbbbc10485601cbb2f1d..c7cc415045c490c7cb2f496a1db7af387a1c8035 100644 (file)
@@ -5,15 +5,16 @@ import java.io.DataInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.simantics.databoard.Bindings;
 import org.simantics.databoard.Datatypes;
+import org.simantics.databoard.binding.Binding;
 import org.simantics.databoard.binding.error.RuntimeDatatypeConstructionException;
 import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.databoard.container.DataContainer;
 import org.simantics.databoard.container.DataContainers;
 import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
 import org.simantics.databoard.serialization.Serializer;
@@ -22,8 +23,48 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 
+/**
+ * It is recommended to use {@link #read(File)} and {@link #read(InputStream)}
+ * for reading to ensure proper resource handling.
+ * 
+ * <p>
+ * It is important to use the correct setting for
+ * {@link #sharedValueContext(boolean)} which depends on how the TG was
+ * serialized to begin with. See {@link #DEFAULT_SHARED_VALUE_CONTEXT} for more
+ * information on this.
+ */
 final public class TransferableGraphFileReader extends ByteFileReader {
 
+    /**
+     * Serializing TransferableGraph1 structures using the default {@link Binding}
+     * will use shared context for serializing the values Variant array. Thus all TG
+     * files produced by the graph compiler use a shared value context which means
+     * this class must be used with {@link #sharedValueContext(boolean)} set to
+     * true. As an example, <code>true</code> must be used if the corresponding TG
+     * file is written e.g. like this:
+     * <pre>
+     * DataContainers.writeFile(location 
+     *     new DataContainer(format,
+     *                       version,
+     *                       metadata,
+     *                       new Variant(TransferableGraph1.BINDING, tg)));
+     * </pre>
+     * 
+     * <p>
+     * On the other hand, any TG files serialized using more optimized methods like
+     * <code>ModelTransferableGraphSource</code> do not use a shared value context
+     * when writing the file. This means those files cannot be read safely using
+     * standard {@link Binding} at all, and when using this class,
+     * {@link #sharedValueContext(boolean)} must be set to false to prevent the
+     * import from corrupting datatype values because the referable parts of
+     * datatypes may get bound to the wrong existing types if the data is read using
+     * shared context.
+     * 
+     * <p>
+     * <code>true</code> is the default setting.
+     */
+    public static final boolean DEFAULT_SHARED_VALUE_CONTEXT = true;
+
     private static final Logger LOGGER = LoggerFactory.getLogger(TransferableGraphFileReader.class);
 
        InputStream in = new InputStream() {
@@ -48,42 +89,70 @@ final public class TransferableGraphFileReader extends ByteFileReader {
         }
            
        };
-       
-       
-       final static class InputChannel implements ReadableByteChannel {
 
-               final private InputStream stream;
-               
-               public InputChannel(InputStream stream) {
-                       this.stream = stream;
-               }
-               
-               @Override
-               public boolean isOpen() {
-                       return true;
-               }
+       private static boolean init = true;
+
+       final private static int SIZE = 1<<18;
+       final private static int HEADER = headerSize();
+       final private int header;
+       private boolean shareValueContext = true;
+
+       /**
+        * Reads a {@link DataContainer} containing a {@link TransferableGraph1}
+        * structure from the specified {@link File}.
+        * 
+        * @param file the file to read from
+        * @return the TG contained by the file
+        * @throws IOException
+        */
+       public static TransferableGraph1 read(File file) throws IOException {
+               return read(file, DEFAULT_SHARED_VALUE_CONTEXT);
+       }
+
+       /**
+        * Reads a {@link DataContainer} containing a {@link TransferableGraph1}
+        * structure from the specified InputStream. Note that this implementation does
+        * not close the specified <code>input</code> stream, it is expected to be
+        * closed by the caller.
+        * 
+        * @param input the input stream to read from
+        * @return the TG contained by the stream
+        * @throws IOException
+        */
+       public static TransferableGraph1 read(InputStream input) throws IOException {
+               return read(input, DEFAULT_SHARED_VALUE_CONTEXT);
+       }
 
-               @Override
-               public void close() throws IOException {
+       /**
+        * Reads a {@link DataContainer} containing a {@link TransferableGraph1}
+        * structure from the specified {@link File}.
+        * 
+        * @param file the file to read from
+        * @return the TG contained by the file
+        * @throws IOException
+        */
+       public static TransferableGraph1 read(File file, boolean sharedValueContext) throws IOException {
+               try (TransferableGraphFileReader reader = new TransferableGraphFileReader(file)) {
+                       return reader.sharedValueContext(sharedValueContext).readTG();
                }
+       }
 
-               @Override
-               public int read(ByteBuffer dst) throws IOException {
-                       int pos = dst.position();
-                       int limit = dst.limit();
-                       int i=stream.read(dst.array(), pos, limit-pos);
-                       //LOGGER.warn("Read " + i + " (expected " + dst.array().length + ")");
-                       return i;
+       /**
+        * Reads a {@link DataContainer} containing a {@link TransferableGraph1}
+        * structure from the specified InputStream. Note that this implementation does
+        * not close the specified <code>input</code> stream, it is expected to be
+        * closed by the caller.
+        * 
+        * @param input the input stream to read from
+        * @return the TG contained by the stream
+        * @throws IOException
+        */
+       public static TransferableGraph1 read(InputStream input, boolean sharedValueContext) throws IOException {
+               try (TransferableGraphFileReader reader = new TransferableGraphFileReader(input)) {
+                       return reader.sharedValueContext(sharedValueContext).readTG();
                }
-               
        }
-       
-       private static boolean init = true;
-       
-       final private static int SIZE = 1<<18;
-       final private static int HEADER = headerSize();
-       final private int header;
-       
+
        public TransferableGraphFileReader(File file) throws IOException {
                super(file, SIZE);
                if(init) {
@@ -129,6 +198,11 @@ final public class TransferableGraphFileReader extends ByteFileReader {
                this.header = HEADER;
        }
 
+       public TransferableGraphFileReader sharedValueContext(boolean share) {
+               this.shareValueContext = share;
+               return this;
+       }
+
        private static int headerSize() {
                try {
                        return Bindings.getSerializerUnchecked(Datatype.class).serialize(Datatypes.getDatatypeUnchecked(TransferableGraph1.class)).length;
@@ -140,7 +214,7 @@ final public class TransferableGraphFileReader extends ByteFileReader {
                        throw new Error("Failed to determine TransferableGraph1 header size. ", e);
                }               
        }
-       
+
        public TransferableGraph1 readTG() throws IOException {
 
                if(getSize() == 0) return null;
@@ -161,7 +235,7 @@ final public class TransferableGraphFileReader extends ByteFileReader {
 
                int resourceCount = safeInt();
                
-               List<Object> idcontext = new ArrayList<Object>(); 
+               List<Object> idcontext = new ArrayList<>(); 
                dis = new DataInputStream(in);
                Extensions extensions = (Extensions)Bindings.getSerializerUnchecked(Extensions.class).deserialize((DataInput)dis, idcontext);
                
@@ -174,27 +248,27 @@ final public class TransferableGraphFileReader extends ByteFileReader {
 //             long duration = System.nanoTime() - start;
 //             LOGGER.warn("start in " + 1e-9*duration + "s.");
 //             start = System.nanoTime();
-               
+
                for(int i=0;i<identities;i++) {
                        int rid = safeInt();
                        byte type = bytes[byteIndex++];
                        // External
                        if(type == 1) {
-                               
+
                                int parent = safeInt();
                                int nameLen = getDynamicUInt32();
-                               
+
                                if(byteIndex+nameLen < SIZE) {
                                        ids[i] = new Identity(rid, new External(parent, utf(bytes, byteIndex, byteIndex + nameLen)));
                                        byteIndex += nameLen;
                                } else {
                                        ids[i] = new Identity(rid, new External(parent, utf(safeBytes(nameLen), 0, nameLen)));
                                }
-                               
+
                        } 
                        // Internal
                        else if(type == 3) {
-                               
+
                                int parent = safeInt();
                                int nameLen = getDynamicUInt32();
                                if(byteIndex+nameLen < SIZE) {
@@ -215,6 +289,8 @@ final public class TransferableGraphFileReader extends ByteFileReader {
 
                        } else if(type == 2) {
                                throw new UnsupportedOperationException();
+                       } else {
+                               throw new IllegalStateException();
                        }
 
                }
@@ -257,11 +333,12 @@ final public class TransferableGraphFileReader extends ByteFileReader {
 
                Serializer variantSerializer = Bindings.getSerializerUnchecked(Bindings.VARIANT);
                
-               idcontext = new ArrayList<Object>(); 
                dis = new DataInputStream(in);
                
                for(int i=0;i<valueLength;i++) {
                        int resource = safeInt();
+                       if (!shareValueContext)
+                               idcontext.clear();
                        Variant value = (Variant)variantSerializer
                                .deserialize((DataInput)dis, idcontext);
                        values[i] = new Value(resource, value);