X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.document.ui%2Fsrc%2Forg%2Fsimantics%2Fdocument%2Fui%2Fgraphfile%2FExternalEditorAdapter.java;fp=bundles%2Forg.simantics.document.ui%2Fsrc%2Forg%2Fsimantics%2Fdocument%2Fui%2Fgraphfile%2FExternalEditorAdapter.java;h=42190d87829283cf165d4ede9a49422abf645fae;hb=e140d8f611f3ebcee3f36370aa0b50344d896953;hp=03b256b28094f23098bb1b2f9f069f3787630ed9;hpb=505e21552623e5c9b12fefb2e3793a64d390a8fd;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.document.ui/src/org/simantics/document/ui/graphfile/ExternalEditorAdapter.java b/bundles/org.simantics.document.ui/src/org/simantics/document/ui/graphfile/ExternalEditorAdapter.java index 03b256b28..42190d878 100644 --- a/bundles/org.simantics.document.ui/src/org/simantics/document/ui/graphfile/ExternalEditorAdapter.java +++ b/bundles/org.simantics.document.ui/src/org/simantics/document/ui/graphfile/ExternalEditorAdapter.java @@ -1,66 +1,219 @@ 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 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 entry : tempFiles.entrySet()) { + Resource value = entry.getValue(); + if (input.equals(value)) { + path = entry.getKey(); + } + } + if (path == null) { + path = Simantics.getSession().syncRequest(new UniqueRead() { - } + @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 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 pathEvent = (WatchEvent) watchEvent; + Kind 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; + } + } }