Revision e140d8f6

View differences:

bundles/org.simantics.document.ui/src/org/simantics/document/ui/Activator.java
11 11
 *******************************************************************************/
12 12
package org.simantics.document.ui;
13 13

  
14
import java.nio.file.Path;
15
import java.nio.file.Paths;
16

  
17
import org.eclipse.core.runtime.IPath;
18
import org.eclipse.core.runtime.Platform;
14 19
import org.eclipse.jface.resource.ImageDescriptor;
15 20
import org.eclipse.ui.plugin.AbstractUIPlugin;
16 21
import org.osgi.framework.Bundle;
17 22
import org.osgi.framework.BundleContext;
23
import org.simantics.document.ui.graphfile.ExternalEditorAdapter;
18 24

  
19 25
/**
20 26
 * The activator class controls the plug-in life cycle
......
31 37
    
32 38
	public ImageDescriptor cross;
33 39
	public ImageDescriptor clock_red;
40

  
41
    private static Path location;
34 42
	
35 43
	/**
36 44
	 * The constructor
......
54 62
		cross = ImageDescriptor.createFromURL(bundle.getResource("icons/silk_small/cross.png"));
55 63
		clock_red = ImageDescriptor.createFromURL(bundle.getResource("icons/silk_small/clock_red.png"));
56 64
		
65
		IPath ipath = Platform.getStateLocation(bundle);
66
		location = Paths.get(ipath.toOSString());
57 67
	}
58 68

  
59 69
	/*
......
61 71
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
62 72
	 */
63 73
	public void stop(BundleContext context) throws Exception {
74
	    ExternalEditorAdapter.stopFileWatcher();
64 75
		plugin = null;
65 76
		super.stop(context);
66 77
	}
......
74 85
		return plugin;
75 86
	}
76 87

  
88
	public static Path getInstanceLocation() {
89
	    return location;
90
	}
77 91
}
bundles/org.simantics.document.ui/src/org/simantics/document/ui/graphfile/ExternalEditorAdapter.java
1 1
package org.simantics.document.ui.graphfile;
2 2

  
3
import java.io.File;
3
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
4
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
5
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
6

  
7
import java.io.IOException;
8
import java.io.UnsupportedEncodingException;
9
import java.nio.file.FileSystems;
10
import java.nio.file.Files;
11
import java.nio.file.Path;
12
import java.nio.file.WatchEvent;
13
import java.nio.file.WatchEvent.Kind;
14
import java.nio.file.WatchKey;
15
import java.nio.file.WatchService;
16
import java.security.MessageDigest;
17
import java.security.NoSuchAlgorithmException;
18
import java.util.Map;
19
import java.util.Map.Entry;
20
import java.util.concurrent.ConcurrentHashMap;
21
import java.util.concurrent.ExecutorService;
22
import java.util.concurrent.Executors;
23
import java.util.concurrent.atomic.AtomicBoolean;
4 24

  
5
import org.eclipse.swt.widgets.Display;
6 25
import org.eclipse.ui.PartInitException;
26
import org.simantics.Simantics;
7 27
import org.simantics.db.ReadGraph;
8 28
import org.simantics.db.Resource;
9
import org.simantics.db.common.request.ReadRequest;
29
import org.simantics.db.common.request.UniqueRead;
10 30
import org.simantics.db.exception.DatabaseException;
11 31
import org.simantics.document.DocumentResource;
12 32
import org.simantics.document.ui.Activator;
13 33
import org.simantics.editors.Editors;
14
import org.simantics.graphfile.ontology.GraphFileResource;
15 34
import org.simantics.graphfile.util.GraphFileUtil;
16
import org.simantics.ui.SimanticsUI;
35
import org.simantics.layer0.Layer0;
17 36
import org.simantics.ui.workbench.editor.AbstractResourceEditorAdapter;
18 37
import org.simantics.ui.workbench.editor.EditorAdapter;
19 38
import org.simantics.utils.ui.ExceptionUtils;
39
import org.slf4j.Logger;
40
import org.slf4j.LoggerFactory;
20 41

  
21 42
public class ExternalEditorAdapter extends AbstractResourceEditorAdapter implements EditorAdapter {
22
	
43

  
44
    private static final Logger LOGGER = LoggerFactory.getLogger(ExternalEditorAdapter.class);
45
    
46
    private static final Map<Path, Resource> tempFiles = new ConcurrentHashMap<>();
47
    private static ExternalFileWatcher fileWatcher;
48

  
23 49
	public ExternalEditorAdapter() {
24
		super("External Editor",Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/page.png"));
50
		super("External Editor", Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/page.png"));
25 51
	}
26 52

  
27
	
28 53
	@Override
29 54
	public boolean canHandle(ReadGraph g, Resource r) throws DatabaseException {
30
		GraphFileResource gf = GraphFileResource.getInstance(g);
31 55
		DocumentResource doc = DocumentResource.getInstance(g);
32 56
		if (!g.isInstanceOf(r, doc.FileDocument))
33 57
			return false;
34
		String filename = g.getPossibleRelatedValue(r, gf.HasResourceName);
35
		return filename != null;
58
		return true;
36 59
	}
37 60
	
38
	@Override
39
	protected void openEditor(final Resource input) throws Exception {
40
		SimanticsUI.getSession().asyncRequest(new ReadRequest() {
41
			
42
			@Override
43
			public void run(ReadGraph graph) throws DatabaseException {
44
				final File file = GraphFileUtil.toTempFile(graph, input);
45
				file.deleteOnExit();
46
				Display.getDefault().asyncExec(new Runnable() {
47
					
48
					@Override
49
					public void run() {
50
						
51
						if (file.exists() && file.canRead()) {
52
							try {
53
								Editors.openExternalEditor(file);
54
							} catch (PartInitException e) {
55
								ExceptionUtils.logAndShowError(e);
56
							}
57
						}
58
					}
59
				});
60
			}
61
		});
62
		
61
    @Override
62
    protected void openEditor(final Resource input) throws Exception {
63
        // ensure watching
64
        watch();
65
        
66
        Path path = null;
67
        for (Entry<Path, Resource> entry : tempFiles.entrySet()) {
68
            Resource value = entry.getValue();
69
            if (input.equals(value)) {
70
                path = entry.getKey();
71
            }
72
        }
73
        if (path == null) {
74
            path = Simantics.getSession().syncRequest(new UniqueRead<Path>() {
63 75

  
64
	}
76
                @Override
77
                public Path perform(ReadGraph graph) throws DatabaseException {
78
                    StringBuilder sb = new StringBuilder();
79
                    String uri = graph.getPossibleURI(input);
80
                    if (uri != null) {
81
                        sb.append(generateSHA1(uri)).append("_");
82
                    }
83
                    sb.append(graph.getRelatedValue(input, Layer0.getInstance(graph).HasName).toString());
84
                    Path filePath = Activator.getInstanceLocation().resolve(sb.toString());
85
                    tempFiles.computeIfAbsent(filePath, t -> {
86
                        try {
87
                            GraphFileUtil.writeDataToFile(graph, input, filePath.toFile());
88
                        } catch (IOException | DatabaseException e) {
89
                            LOGGER.error("Could not write data to file ", e);
90
                        }
91
                        return input;
92
                    });
93
                    LOGGER.info("Adding tempFile {} with resource {}", filePath, input);
94
                    return filePath;
95
                }
96
            });
97
        }
98
        try {
99
            Editors.openExternalEditor(path.toFile());
100
        } catch (PartInitException e) {
101
            ExceptionUtils.logAndShowError(e);
102
        }
103
    }
104

  
105
    public static String generateSHA1(String message) {
106
        return hashString(message, "SHA-1");
107
    }
108

  
109
    private static String hashString(String message, String algorithm) {
110
        try {
111
            MessageDigest digest = MessageDigest.getInstance(algorithm);
112
            byte[] hashedBytes = digest.digest(message.getBytes("UTF-8"));
113
     
114
            return convertByteArrayToHexString(hashedBytes);
115
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
116
            // Should not happen
117
            LOGGER.error("Could not generate hash", ex);
118
        }
119
        return "";
120
    }
121

  
122
    private static String convertByteArrayToHexString(byte[] arrayBytes) {
123
        StringBuffer stringBuffer = new StringBuffer();
124
        for (int i = 0; i < arrayBytes.length; i++) {
125
            stringBuffer.append(Integer.toString((arrayBytes[i] & 0xff) + 0x100, 16)
126
                    .substring(1));
127
        }
128
        return stringBuffer.toString();
129
    }
130

  
131
    private static void watch() throws IOException {
132
        if (fileWatcher == null) {
133
            synchronized (ExternalEditorAdapter.class) {
134
                if (fileWatcher == null) {
135
                    fileWatcher = new ExternalFileWatcher();
136
                }
137
            }
138
        }
139
    }
140

  
141
    static class ExternalFileWatcher{
142

  
143
        private ExecutorService service = Executors.newFixedThreadPool(1, r -> {
144
            Thread t = new Thread(r);
145
            t.setDaemon(true);
146
            return t;
147
        });
148
        
149
        private final WatchService ws;
150
        private final AtomicBoolean stopped = new AtomicBoolean(true);
151
        private ConcurrentHashMap<WatchKey, Path> keys = new ConcurrentHashMap<>();
152
        
153
        public ExternalFileWatcher() throws IOException {
154
            ws = FileSystems.getDefault().newWatchService();
155
            
156
            register(Activator.getInstanceLocation());
157
            
158
            service.submit(() -> {
159
                stopped.set(false);
160

  
161
                while (!stopped.get()) {
162
                    try {
163
                        WatchKey key = ws.take();
164
                        for (WatchEvent<?> watchEvent : key.pollEvents()) {
165
                            if (OVERFLOW == watchEvent.kind())
166
                                continue; // loop
167
                            
168
                            @SuppressWarnings("unchecked")
169
                            WatchEvent<Path> pathEvent = (WatchEvent<Path>) watchEvent;
170
                            Kind<Path> kind = pathEvent.kind();
171
                            Path parent = keys.get(key);
172
                            Path newPath = parent.resolve(pathEvent.context());
173
                            if (ENTRY_MODIFY == kind) {
174
                                LOGGER.info("New path modified: " + newPath);
175
                                Resource resource = tempFiles.get(newPath);
176
                                if (resource != null) {
177
                                    GraphFileUtil.writeDataToGraph(newPath.toFile(), resource);
178
                                } else {
179
                                    LOGGER.warn("No resource found for {}", newPath.toAbsolutePath());
180
                                }
181
                            } else if (ENTRY_DELETE == kind) {
182
                                System.out.println("New path deleted: " + newPath);
183
                            }
184
                        }
185
                        if (!key.reset()) {
186
                            keys.remove(key);
187
//                            break; // loop
188
                        }
189
                    } catch (InterruptedException e) {
190
                        if (!stopped.get())
191
                            LOGGER.error("Could not stop", e);
192
                    } catch (Throwable t) {
193
                        LOGGER.error("An error occured", t);
194
                    }
195
                }
196
            });
197
        }
198
        
199
        public void stop() {
200
            stopped.set(true);
201
        }
202
        
203
        private void register(Path path) throws IOException {
204
            if (Files.isDirectory(path)) {
205
                LOGGER.info("Registering path {}", path);
206
                WatchKey key = path.toAbsolutePath().register(ws, ENTRY_DELETE, ENTRY_MODIFY);
207
                keys.put(key, path);
208
            }
209
        }
210
    }
65 211

  
212
    public static void stopFileWatcher() {
213
        tempFiles.clear();
214
        if (fileWatcher != null) {
215
            fileWatcher.stop();
216
            fileWatcher = null;
217
        }
218
    }
66 219
}
bundles/org.simantics.graphfile/src/org/simantics/graphfile/util/GraphFileUtil.java
76 76
	public static File toTempFile(ReadGraph graph, Resource res)throws DatabaseException {
77 77

  
78 78
		GraphFileResource gf = GraphFileResource.getInstance(graph);
79
		String filename = graph.getRelatedValue(res, gf.HasResourceName);
79
		String filename = graph.getPossibleRelatedValue(res, gf.HasResourceName);
80
		if (filename == null)
81
		    filename = graph.getRelatedValue(res, Layer0.getInstance(graph).HasName);
80 82
		
81 83
		int index = filename.lastIndexOf(".");
82 84
		String name = "";

Also available in: Unified diff