Fall back to HasName if HasResourceName not available for GraphFile 40/1340/5
authorjsimomaa <jani.simomaa@gmail.com>
Fri, 29 Dec 2017 11:21:36 +0000 (13:21 +0200)
committerjsimomaa <jani.simomaa@gmail.com>
Tue, 2 Jan 2018 07:23:44 +0000 (09:23 +0200)
Also adding watching of externally opened files and writeback for data
changed by the external editor

Now with hashed URI based filename

refs #7699

Change-Id: I2eadb539d599b0e55840e0fd7f141e44004833fc

bundles/org.simantics.document.ui/src/org/simantics/document/ui/Activator.java
bundles/org.simantics.document.ui/src/org/simantics/document/ui/graphfile/ExternalEditorAdapter.java
bundles/org.simantics.graphfile/src/org/simantics/graphfile/util/GraphFileUtil.java

index a4db0a578a5f4eb72ecbc5dd30c6530e06fb975c..5a25c5b6b7f8708d0c7865ff20ad0ee8150ca68a 100644 (file)
  *******************************************************************************/
 package org.simantics.document.ui;
 
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.simantics.document.ui.graphfile.ExternalEditorAdapter;
 
 /**
  * The activator class controls the plug-in life cycle
@@ -31,6 +37,8 @@ public class Activator extends AbstractUIPlugin {
     
        public ImageDescriptor cross;
        public ImageDescriptor clock_red;
+
+    private static Path location;
        
        /**
         * The constructor
@@ -54,6 +62,8 @@ public class Activator extends AbstractUIPlugin {
                cross = ImageDescriptor.createFromURL(bundle.getResource("icons/silk_small/cross.png"));
                clock_red = ImageDescriptor.createFromURL(bundle.getResource("icons/silk_small/clock_red.png"));
                
+               IPath ipath = Platform.getStateLocation(bundle);
+               location = Paths.get(ipath.toOSString());
        }
 
        /*
@@ -61,6 +71,7 @@ public class Activator extends AbstractUIPlugin {
         * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
         */
        public void stop(BundleContext context) throws Exception {
+           ExternalEditorAdapter.stopFileWatcher();
                plugin = null;
                super.stop(context);
        }
@@ -74,4 +85,7 @@ public class Activator extends AbstractUIPlugin {
                return plugin;
        }
 
+       public static Path getInstanceLocation() {
+           return location;
+       }
 }
index 03b256b28094f23098bb1b2f9f069f3787630ed9..42190d87829283cf165d4ede9a49422abf645fae 100644 (file)
 package org.simantics.document.ui.graphfile;
 
-import java.io.File;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchEvent.Kind;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.PartInitException;
+import org.simantics.Simantics;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
-import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.common.request.UniqueRead;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.document.DocumentResource;
 import org.simantics.document.ui.Activator;
 import org.simantics.editors.Editors;
-import org.simantics.graphfile.ontology.GraphFileResource;
 import org.simantics.graphfile.util.GraphFileUtil;
-import org.simantics.ui.SimanticsUI;
+import org.simantics.layer0.Layer0;
 import org.simantics.ui.workbench.editor.AbstractResourceEditorAdapter;
 import org.simantics.ui.workbench.editor.EditorAdapter;
 import org.simantics.utils.ui.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ExternalEditorAdapter extends AbstractResourceEditorAdapter implements EditorAdapter {
-       
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ExternalEditorAdapter.class);
+    
+    private static final Map<Path, Resource> tempFiles = new ConcurrentHashMap<>();
+    private static ExternalFileWatcher fileWatcher;
+
        public ExternalEditorAdapter() {
-               super("External Editor",Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/page.png"));
+               super("External Editor", Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/page.png"));
        }
 
-       
        @Override
        public boolean canHandle(ReadGraph g, Resource r) throws DatabaseException {
-               GraphFileResource gf = GraphFileResource.getInstance(g);
                DocumentResource doc = DocumentResource.getInstance(g);
                if (!g.isInstanceOf(r, doc.FileDocument))
                        return false;
-               String filename = g.getPossibleRelatedValue(r, gf.HasResourceName);
-               return filename != null;
+               return true;
        }
        
-       @Override
-       protected void openEditor(final Resource input) throws Exception {
-               SimanticsUI.getSession().asyncRequest(new ReadRequest() {
-                       
-                       @Override
-                       public void run(ReadGraph graph) throws DatabaseException {
-                               final File file = GraphFileUtil.toTempFile(graph, input);
-                               file.deleteOnExit();
-                               Display.getDefault().asyncExec(new Runnable() {
-                                       
-                                       @Override
-                                       public void run() {
-                                               
-                                               if (file.exists() && file.canRead()) {
-                                                       try {
-                                                               Editors.openExternalEditor(file);
-                                                       } catch (PartInitException e) {
-                                                               ExceptionUtils.logAndShowError(e);
-                                                       }
-                                               }
-                                       }
-                               });
-                       }
-               });
-               
+    @Override
+    protected void openEditor(final Resource input) throws Exception {
+        // ensure watching
+        watch();
+        
+        Path path = null;
+        for (Entry<Path, Resource> entry : tempFiles.entrySet()) {
+            Resource value = entry.getValue();
+            if (input.equals(value)) {
+                path = entry.getKey();
+            }
+        }
+        if (path == null) {
+            path = Simantics.getSession().syncRequest(new UniqueRead<Path>() {
 
-       }
+                @Override
+                public Path perform(ReadGraph graph) throws DatabaseException {
+                    StringBuilder sb = new StringBuilder();
+                    String uri = graph.getPossibleURI(input);
+                    if (uri != null) {
+                        sb.append(generateSHA1(uri)).append("_");
+                    }
+                    sb.append(graph.getRelatedValue(input, Layer0.getInstance(graph).HasName).toString());
+                    Path filePath = Activator.getInstanceLocation().resolve(sb.toString());
+                    tempFiles.computeIfAbsent(filePath, t -> {
+                        try {
+                            GraphFileUtil.writeDataToFile(graph, input, filePath.toFile());
+                        } catch (IOException | DatabaseException e) {
+                            LOGGER.error("Could not write data to file ", e);
+                        }
+                        return input;
+                    });
+                    LOGGER.info("Adding tempFile {} with resource {}", filePath, input);
+                    return filePath;
+                }
+            });
+        }
+        try {
+            Editors.openExternalEditor(path.toFile());
+        } catch (PartInitException e) {
+            ExceptionUtils.logAndShowError(e);
+        }
+    }
+
+    public static String generateSHA1(String message) {
+        return hashString(message, "SHA-1");
+    }
+
+    private static String hashString(String message, String algorithm) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance(algorithm);
+            byte[] hashedBytes = digest.digest(message.getBytes("UTF-8"));
+     
+            return convertByteArrayToHexString(hashedBytes);
+        } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
+            // Should not happen
+            LOGGER.error("Could not generate hash", ex);
+        }
+        return "";
+    }
+
+    private static String convertByteArrayToHexString(byte[] arrayBytes) {
+        StringBuffer stringBuffer = new StringBuffer();
+        for (int i = 0; i < arrayBytes.length; i++) {
+            stringBuffer.append(Integer.toString((arrayBytes[i] & 0xff) + 0x100, 16)
+                    .substring(1));
+        }
+        return stringBuffer.toString();
+    }
+
+    private static void watch() throws IOException {
+        if (fileWatcher == null) {
+            synchronized (ExternalEditorAdapter.class) {
+                if (fileWatcher == null) {
+                    fileWatcher = new ExternalFileWatcher();
+                }
+            }
+        }
+    }
+
+    static class ExternalFileWatcher{
+
+        private ExecutorService service = Executors.newFixedThreadPool(1, r -> {
+            Thread t = new Thread(r);
+            t.setDaemon(true);
+            return t;
+        });
+        
+        private final WatchService ws;
+        private final AtomicBoolean stopped = new AtomicBoolean(true);
+        private ConcurrentHashMap<WatchKey, Path> keys = new ConcurrentHashMap<>();
+        
+        public ExternalFileWatcher() throws IOException {
+            ws = FileSystems.getDefault().newWatchService();
+            
+            register(Activator.getInstanceLocation());
+            
+            service.submit(() -> {
+                stopped.set(false);
+
+                while (!stopped.get()) {
+                    try {
+                        WatchKey key = ws.take();
+                        for (WatchEvent<?> watchEvent : key.pollEvents()) {
+                            if (OVERFLOW == watchEvent.kind())
+                                continue; // loop
+                            
+                            @SuppressWarnings("unchecked")
+                            WatchEvent<Path> pathEvent = (WatchEvent<Path>) watchEvent;
+                            Kind<Path> kind = pathEvent.kind();
+                            Path parent = keys.get(key);
+                            Path newPath = parent.resolve(pathEvent.context());
+                            if (ENTRY_MODIFY == kind) {
+                                LOGGER.info("New path modified: " + newPath);
+                                Resource resource = tempFiles.get(newPath);
+                                if (resource != null) {
+                                    GraphFileUtil.writeDataToGraph(newPath.toFile(), resource);
+                                } else {
+                                    LOGGER.warn("No resource found for {}", newPath.toAbsolutePath());
+                                }
+                            } else if (ENTRY_DELETE == kind) {
+                                System.out.println("New path deleted: " + newPath);
+                            }
+                        }
+                        if (!key.reset()) {
+                            keys.remove(key);
+//                            break; // loop
+                        }
+                    } catch (InterruptedException e) {
+                        if (!stopped.get())
+                            LOGGER.error("Could not stop", e);
+                    } catch (Throwable t) {
+                        LOGGER.error("An error occured", t);
+                    }
+                }
+            });
+        }
+        
+        public void stop() {
+            stopped.set(true);
+        }
+        
+        private void register(Path path) throws IOException {
+            if (Files.isDirectory(path)) {
+                LOGGER.info("Registering path {}", path);
+                WatchKey key = path.toAbsolutePath().register(ws, ENTRY_DELETE, ENTRY_MODIFY);
+                keys.put(key, path);
+            }
+        }
+    }
 
+    public static void stopFileWatcher() {
+        tempFiles.clear();
+        if (fileWatcher != null) {
+            fileWatcher.stop();
+            fileWatcher = null;
+        }
+    }
 }
index 76003e023d778a1a73826152e715190400ac31ea..fbfcc3a146e9c39a63853cec2faf3c6d52eaa29b 100644 (file)
@@ -76,7 +76,9 @@ public class GraphFileUtil {
        public static File toTempFile(ReadGraph graph, Resource res)throws DatabaseException {
 
                GraphFileResource gf = GraphFileResource.getInstance(graph);
-               String filename = graph.getRelatedValue(res, gf.HasResourceName);
+               String filename = graph.getPossibleRelatedValue(res, gf.HasResourceName);
+               if (filename == null)
+                   filename = graph.getRelatedValue(res, Layer0.getInstance(graph).HasName);
                
                int index = filename.lastIndexOf(".");
                String name = "";