]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.document.ui/src/org/simantics/document/ui/graphfile/ExternalEditorAdapter.java
Externalize strings in org.simantics.document.ui
[simantics/platform.git] / bundles / org.simantics.document.ui / src / org / simantics / document / ui / graphfile / ExternalEditorAdapter.java
index 010237fef15695aacd0bcf219a9f95f13aaa0e1a..07ed6432771cced743a3a1ac5e17f3c7478c913c 100644 (file)
-package org.simantics.document.ui.graphfile;\r
-\r
-import java.io.File;\r
-\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.ui.PartInitException;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.common.request.ReadRequest;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.document.DocumentResource;\r
-import org.simantics.document.ui.Activator;\r
-import org.simantics.editors.Editors;\r
-import org.simantics.graphfile.ontology.GraphFileResource;\r
-import org.simantics.graphfile.util.GraphFileUtil;\r
-import org.simantics.ui.SimanticsUI;\r
-import org.simantics.ui.workbench.editor.AbstractResourceEditorAdapter;\r
-import org.simantics.ui.workbench.editor.EditorAdapter;\r
-import org.simantics.utils.ui.ExceptionUtils;\r
-\r
-public class ExternalEditorAdapter extends AbstractResourceEditorAdapter implements EditorAdapter {\r
-       \r
-       public ExternalEditorAdapter() {\r
-               super("External Editor",Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/page.png"));\r
-       }\r
-\r
-       \r
-       @Override\r
-       public boolean canHandle(ReadGraph g, Resource r) throws DatabaseException {\r
-               GraphFileResource gf = GraphFileResource.getInstance(g);\r
-               DocumentResource doc = DocumentResource.getInstance(g);\r
-               if (!g.isInstanceOf(r, doc.FileDocument))\r
-                       return false;\r
-               String filename = g.getPossibleRelatedValue(r, gf.HasResourceName);\r
-               return filename != null;\r
-       }\r
-       \r
-       @Override\r
-       protected void openEditor(final Resource input) throws Exception {\r
-               SimanticsUI.getSession().asyncRequest(new ReadRequest() {\r
-                       \r
-                       @Override\r
-                       public void run(ReadGraph graph) throws DatabaseException {\r
-                               final File file = GraphFileUtil.toTempFile(graph, input);\r
-                               file.deleteOnExit();\r
-                               Display.getDefault().asyncExec(new Runnable() {\r
-                                       \r
-                                       @Override\r
-                                       public void run() {\r
-                                               \r
-                                               if (file.exists() && file.canRead()) {\r
-                                                       try {\r
-                                                               Editors.openExternalEditor(file);\r
-                                                       } catch (PartInitException e) {\r
-                                                               ExceptionUtils.logAndShowError(e);\r
-                                                       }\r
-                                               }\r
-                                       }\r
-                               });\r
-                       }\r
-               });\r
-               \r
-\r
-       }\r
-\r
-}\r
+package org.simantics.document.ui.graphfile;
+
+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.ui.PartInitException;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+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.util.GraphFileUtil;
+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(Messages.ExternalEditorAdapter_ExternalEditor, Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/page.png")); //$NON-NLS-2$ //$NON-NLS-3$
+       }
+
+       @Override
+       public boolean canHandle(ReadGraph g, Resource r) throws DatabaseException {
+               DocumentResource doc = DocumentResource.getInstance(g);
+               if (!g.isInstanceOf(r, doc.FileDocument))
+                       return false;
+               return true;
+       }
+       
+    @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("_"); //$NON-NLS-1$
+                    }
+                    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); //$NON-NLS-1$
+                        }
+                        return input;
+                    });
+                    LOGGER.info("Adding tempFile {} with resource {}", filePath, input); //$NON-NLS-1$
+                    return filePath;
+                }
+            });
+        }
+        try {
+            Editors.openExternalEditor(path.toFile());
+        } catch (PartInitException e) {
+            ExceptionUtils.logAndShowError(e);
+        }
+    }
+
+    public static String generateSHA1(String message) {
+        return hashString(message, "SHA-1"); //$NON-NLS-1$
+    }
+
+    private static String hashString(String message, String algorithm) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance(algorithm);
+            byte[] hashedBytes = digest.digest(message.getBytes("UTF-8")); //$NON-NLS-1$
+     
+            return convertByteArrayToHexString(hashedBytes);
+        } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
+            // Should not happen
+            LOGGER.error("Could not generate hash", ex); //$NON-NLS-1$
+        }
+        return ""; //$NON-NLS-1$
+    }
+
+    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); //$NON-NLS-1$
+                                Resource resource = tempFiles.get(newPath);
+                                if (resource != null) {
+                                    GraphFileUtil.writeDataToGraph(newPath.toFile(), resource);
+                                } else {
+                                    LOGGER.warn("No resource found for {}", newPath.toAbsolutePath()); //$NON-NLS-1$
+                                }
+                            } else if (ENTRY_DELETE == kind) {
+                                System.out.println("New path deleted: " + newPath); //$NON-NLS-1$
+                            }
+                        }
+                        if (!key.reset()) {
+                            keys.remove(key);
+//                            break; // loop
+                        }
+                    } catch (InterruptedException e) {
+                        if (!stopped.get())
+                            LOGGER.error("Could not stop", e); //$NON-NLS-1$
+                    } catch (Throwable t) {
+                        LOGGER.error("An error occured", t); //$NON-NLS-1$
+                    }
+                }
+            });
+        }
+        
+        public void stop() {
+            stopped.set(true);
+        }
+        
+        private void register(Path path) throws IOException {
+            if (Files.isDirectory(path)) {
+                LOGGER.info("Registering path {}", path); //$NON-NLS-1$
+                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;
+        }
+    }
+}