]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.document.ui/src/org/simantics/document/ui/graphfile/ExternalEditorAdapter.java
Fall back to HasName if HasResourceName not available for GraphFile
[simantics/platform.git] / bundles / org.simantics.document.ui / src / org / simantics / document / ui / graphfile / ExternalEditorAdapter.java
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;
+        }
+    }
 }