]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.debug.ui/src/org/simantics/debug/ui/SearchResourceDialog.java
Re-enabled CTRL+SHIFT+R resource search dialog in org.simantics.debug.ui
[simantics/platform.git] / bundles / org.simantics.debug.ui / src / org / simantics / debug / ui / SearchResourceDialog.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2016 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *     Semantum Oy - index based searching and graph manipulation (#4255)\r
12  *******************************************************************************/\r
13 package org.simantics.debug.ui;\r
14 \r
15 import java.util.ArrayList;\r
16 import java.util.Collection;\r
17 import java.util.Collections;\r
18 import java.util.Comparator;\r
19 import java.util.HashSet;\r
20 import java.util.List;\r
21 import java.util.Map;\r
22 import java.util.Set;\r
23 import java.util.TreeSet;\r
24 import java.util.regex.Matcher;\r
25 import java.util.regex.Pattern;\r
26 \r
27 import org.eclipse.core.runtime.CoreException;\r
28 import org.eclipse.core.runtime.IProgressMonitor;\r
29 import org.eclipse.core.runtime.IStatus;\r
30 import org.eclipse.core.runtime.Status;\r
31 import org.eclipse.jface.dialogs.IDialogConstants;\r
32 import org.eclipse.jface.dialogs.IDialogSettings;\r
33 import org.eclipse.jface.resource.JFaceResources;\r
34 import org.eclipse.jface.resource.LocalResourceManager;\r
35 import org.eclipse.jface.resource.ResourceManager;\r
36 import org.eclipse.jface.viewers.IStructuredSelection;\r
37 import org.eclipse.jface.viewers.LabelProvider;\r
38 import org.eclipse.jface.viewers.StructuredSelection;\r
39 import org.eclipse.swt.graphics.Image;\r
40 import org.eclipse.swt.widgets.Composite;\r
41 import org.eclipse.swt.widgets.Control;\r
42 import org.eclipse.swt.widgets.Display;\r
43 import org.eclipse.swt.widgets.Shell;\r
44 import org.eclipse.ui.IMemento;\r
45 import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;\r
46 import org.simantics.Simantics;\r
47 import org.simantics.db.ReadGraph;\r
48 import org.simantics.db.Resource;\r
49 import org.simantics.db.Session;\r
50 import org.simantics.db.common.primitiverequest.PossibleAdapter;\r
51 import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
52 import org.simantics.db.common.request.BinaryRead;\r
53 import org.simantics.db.common.request.ObjectsWithType;\r
54 import org.simantics.db.common.request.ReadRequest;\r
55 import org.simantics.db.common.request.UniqueRead;\r
56 import org.simantics.db.common.uri.UnescapedChildMapOfResource;\r
57 import org.simantics.db.common.utils.NameUtils;\r
58 import org.simantics.db.exception.DatabaseException;\r
59 import org.simantics.db.layer0.migration.OntologiesFromLibrary;\r
60 import org.simantics.db.layer0.variable.Variables.Role;\r
61 import org.simantics.db.request.Read;\r
62 import org.simantics.db.service.SerialisationSupport;\r
63 import org.simantics.debug.ui.ResourceSearch.IResourceFilter;\r
64 import org.simantics.debug.ui.internal.Activator;\r
65 import org.simantics.debug.ui.internal.DebugUtils;\r
66 import org.simantics.layer0.Layer0;\r
67 import org.simantics.operation.Layer0X;\r
68 import org.simantics.scl.runtime.function.Function;\r
69 import org.simantics.ui.selection.ResourceWorkbenchSelectionElement;\r
70 import org.simantics.ui.workbench.dialogs.ResourceLabelProvider;\r
71 import org.simantics.utils.Container;\r
72 import org.simantics.utils.ui.BundleUtils;\r
73 import org.slf4j.Logger;\r
74 import org.slf4j.LoggerFactory;\r
75 \r
76 /**\r
77  * TODO Add Debugger Composite as preview!\r
78  */\r
79 public class SearchResourceDialog extends FilteredItemsSelectionDialog {\r
80 \r
81     private static final Logger LOGGER = LoggerFactory.getLogger(SearchResourceDialog.class);\r
82 \r
83     /**\r
84      * The default maximum amount of Dependencies index hits to produce as results.\r
85      */\r
86     private static final int DEFAULT_MAX_INDEX_HITS = 1000;\r
87 \r
88     private static final Pattern ID_PATTERN = Pattern.compile("\\$([0-9]+)");\r
89 \r
90     private static final String SEARCH_RESOURCE_DIALOG = "SearchResourceDialog"; //$NON-NLS-1$\r
91 \r
92     private static final int SHOW_IN_BROWSER_ID = IDialogConstants.CLIENT_ID + 1;\r
93 \r
94     private static final String SHOW_IN_BROWSER_LABEL = "Show In Browser";\r
95 \r
96     private Session session;\r
97     @SuppressWarnings("unused")\r
98     private IStructuredSelection selection;\r
99     private ResourceManager resourceManager;\r
100     private IResourceFilter resourceFilter = ResourceSearch.FILTER_ALL;\r
101 \r
102     LabelProvider detailsLabelProvider = new LabelProvider() {\r
103         @Override\r
104         public String getText(Object element) {\r
105             if (element == null)\r
106                 return "null";\r
107             // This may happen if multiple choice is enabled\r
108             if (element instanceof String)\r
109                 return (String) element;\r
110             @SuppressWarnings("unchecked")\r
111             Container<Resource> rc = (Container<Resource>) element;\r
112             final Resource r = rc.get();\r
113             try {\r
114                 return session.syncRequest(new Read<String>() {\r
115                     @Override\r
116                     public String perform(ReadGraph g) throws DatabaseException {\r
117                         String name = NameUtils.getSafeName(g, r);\r
118                         String uri = g.getPossibleURI(r);\r
119                         String label = "[" + r.getResourceId() + "] - " + name;\r
120                         if (uri != null)\r
121                             label = label + " - " + uri;\r
122                         return label;\r
123                     }\r
124                 });\r
125             } catch (DatabaseException e) {\r
126                 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Resource label provider failed unexpectedly.", e));\r
127                 return "";\r
128             }\r
129         }\r
130     };\r
131 \r
132     static class ElementLabelProvider extends ResourceLabelProvider {\r
133         public ElementLabelProvider(Display display) {\r
134             super(display);\r
135         }\r
136         @Override\r
137         public String getText(Object element) {\r
138             if (element==null)\r
139                 return "null";\r
140             return element.toString();\r
141         }\r
142         @Override\r
143         public Image getImage(Object element) {\r
144             if (element == null)\r
145                 return null;\r
146             @SuppressWarnings("unchecked")\r
147             Container<Resource> rc = (Container<Resource>) element;\r
148             final Resource r = rc.get();\r
149             return super.getImage(r);\r
150         }\r
151     };\r
152 \r
153     ElementLabelProvider labelProvider;\r
154 \r
155     public SearchResourceDialog(Session s, boolean multi, Shell shell, String title) {\r
156         this(s, multi, shell, title, null);\r
157     }\r
158 \r
159     public SearchResourceDialog(Session s, boolean multi, Shell shell, String title, IStructuredSelection selection) {\r
160         super(shell, multi);\r
161         this.session = s;\r
162         this.selection = selection;\r
163         this.labelProvider = new ElementLabelProvider(shell.getDisplay());\r
164         setMessage("Enter name, resource URI or ID");\r
165         setListLabelProvider(labelProvider);\r
166         setDetailsLabelProvider(detailsLabelProvider);\r
167         setTitle(title);\r
168         //setInitialPattern("*", FilteredItemsSelectionDialog.FULL_SELECTION);\r
169         setSelectionHistory(new ResourceSelectionHistory());\r
170         setSeparatorLabel("Previously selected above, others below");\r
171     }\r
172 \r
173     @Override\r
174     protected void configureShell(Shell shell) {\r
175         this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), shell);\r
176         setImage((Image) resourceManager.get(BundleUtils.getImageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/cog_blue.png")));\r
177         super.configureShell(shell);\r
178     }\r
179 \r
180     @Override\r
181     protected void createButtonsForButtonBar(Composite parent) {\r
182         createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,\r
183                 true);\r
184         createButton(parent, SHOW_IN_BROWSER_ID, SHOW_IN_BROWSER_LABEL,\r
185                 true);\r
186         createButton(parent, IDialogConstants.CANCEL_ID,\r
187                 IDialogConstants.CANCEL_LABEL, false);\r
188     }\r
189 \r
190     @Override\r
191     protected void buttonPressed(int buttonId) {\r
192         if (buttonId == SHOW_IN_BROWSER_ID) {\r
193             okPressed();\r
194             LabeledResource lr = (LabeledResource) getFirstResult();\r
195             ShowInBrowser.defaultExecute(new StructuredSelection(new ResourceWorkbenchSelectionElement(lr.resource)));\r
196             return;\r
197         }\r
198         super.buttonPressed(buttonId);\r
199     }\r
200 \r
201     class ResourceSelectionHistory extends FilteredItemsSelectionDialog.SelectionHistory {\r
202 \r
203         @Override\r
204         protected Object restoreItemFromMemento(IMemento memento) {\r
205             String data = memento.getTextData();\r
206             try {\r
207                 SerialisationSupport support = Simantics.getSession().getService(SerialisationSupport.class);\r
208                 Resource r = support.getResource(Long.parseLong(data));\r
209                 if (r == null)\r
210                     return null;\r
211 \r
212                 String name = session.syncRequest(new UniqueRead<String>() {\r
213                     @Override\r
214                     public String perform(ReadGraph g) throws DatabaseException {\r
215                         if (!resourceFilter.acceptResource(g, r))\r
216                             return null;\r
217                         try {\r
218                             try {\r
219                                 return g.adapt(r, String.class);\r
220                             } catch (Exception ex) {\r
221                                 System.out.println("Exception thrown from restoreItemFromMemento");\r
222                             }\r
223                         } catch (Throwable t) {}\r
224                         return "" + r.getResourceId();\r
225                     }\r
226                 });\r
227                 if (name==null) return null;\r
228                 return new LabeledResource(name, r);\r
229             } catch (NumberFormatException | DatabaseException e) {\r
230                 LOGGER.info("Search memento restoration failed.", e);\r
231                 return null;\r
232             }\r
233         }\r
234 \r
235         @SuppressWarnings("unchecked")\r
236         @Override\r
237         protected void storeItemToMemento(Object item, IMemento memento) {\r
238             if(item instanceof Container) {\r
239                 try {\r
240                     SerialisationSupport support = Simantics.getSession().getService(SerialisationSupport.class);\r
241                     memento.putTextData(String.valueOf(support.getRandomAccessId(((Container<Resource>)item).get())));\r
242                 } catch (DatabaseException e) {\r
243                     e.printStackTrace();\r
244                 }\r
245             }\r
246         }\r
247     };\r
248 \r
249     @Override\r
250     protected Control createExtendedContentArea(Composite parent) {\r
251         return null;\r
252     }\r
253 \r
254     @Override\r
255     protected ItemsFilter createFilter() {\r
256         // NOTE: filter must be created here.\r
257         return new ItemsFilter() {\r
258             @Override\r
259             public boolean matchItem(Object item) {\r
260                 //return matches(item.toString());\r
261                 return true;\r
262             }\r
263 \r
264             // If this method returns true, it means fillContentProvider will\r
265             // not be called again, but the existing results are just re-filtered.\r
266             @Override\r
267             public boolean isSubFilter(ItemsFilter filter) {\r
268                 //System.out.println(getPattern() + " vs. " + filter.getPattern());\r
269                 return false;\r
270             }\r
271 \r
272             @Override\r
273             public boolean isConsistentItem(Object item) {\r
274                 return true;\r
275             }\r
276 \r
277             @Override\r
278             public boolean equalsFilter(ItemsFilter filter) {\r
279                 return false;\r
280             }\r
281         };\r
282     }\r
283 \r
284     @Override\r
285     protected void fillContentProvider(final AbstractContentProvider contentProvider,\r
286             final ItemsFilter itemsFilter, final IProgressMonitor progressMonitor)\r
287                     throws CoreException {\r
288         final String pattern = itemsFilter.getPattern();\r
289         final boolean findUris = pattern.trim().startsWith("http:/");\r
290         final long referencedResourceId = referencedResourceId(pattern);\r
291         final boolean findIds = referencedResourceId != 0;\r
292 \r
293         //progressMonitor.beginTask("Searching", IProgressMonitor.UNKNOWN);\r
294 \r
295         try {\r
296             session.syncRequest(new ReadRequest() {\r
297                 @Override\r
298                 public void run(ReadGraph graph) throws DatabaseException {\r
299                     // Find by ID first.\r
300                     if (findIds) {\r
301                         try {\r
302                             Resource r = graph.getService(SerialisationSupport.class).getResource(referencedResourceId);\r
303                             contentProvider.add(new LabeledResource(DebugUtils.getSafeLabel(graph, r), r), itemsFilter);\r
304                             //contentProvider.add(new LabeledResource(pattern, r), itemsFilter);\r
305                         } catch (DatabaseException e) {\r
306                             // No resource for specified id.\r
307                         }\r
308                     }\r
309                     if (findUris) {\r
310                         String uri = pattern;\r
311                         if (uri.endsWith(Role.CHILD.getIdentifier())) {\r
312                             uri = uri.substring(0, uri.length() - 1);\r
313                         }\r
314                         Resource r = graph.getPossibleResource(uri);\r
315                         if (r != null) {\r
316                             contentProvider.add(new LabeledResource(DebugUtils.getSafeURI(graph, r), r), itemsFilter );\r
317 \r
318                             Map<String, Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(r));\r
319                             for (Resource child : children.values()) {\r
320                                 contentProvider.add(new LabeledResource(DebugUtils.getSafeURI(graph, child), child), itemsFilter );\r
321                             }\r
322                         }\r
323                     } else {\r
324                         Resource project = Simantics.peekProjectResource();\r
325                         if (project != null) {\r
326                             IResourceFilter rf = resourceFilter;\r
327                             String filter = getFilterForResourceFilter(rf);\r
328                             if (!filter.isEmpty())\r
329                                 filter += " AND ";\r
330                             filter += "Name:" + pattern + "*";\r
331 \r
332                             Layer0 L0 = Layer0.getInstance(graph);\r
333 \r
334                             Set<Resource> indexRoots = new HashSet<>();\r
335                             indexRoots.addAll(graph.syncRequest(new ObjectsWithType(project, L0.ConsistsOf, L0.IndexRoot)));\r
336                             indexRoots.addAll(graph.syncRequest(new OntologiesFromLibrary(graph.getRootLibrary())));\r
337                             for (Resource indexRoot : indexRoots) {\r
338                                 Collection<Resource> hits = find(graph, indexRoot, filter);\r
339                                 for (Resource r : hits) {\r
340                                     if (rf != null && !rf.acceptResource(graph, r))\r
341                                         continue;\r
342                                     contentProvider.add(new LabeledResource(DebugUtils.getSafeLabel(graph, r), r), itemsFilter);\r
343                                 }\r
344                             }\r
345                         }\r
346                     }\r
347                 }\r
348 \r
349                 public Collection<Resource> find(ReadGraph graph, Resource index, String filter) throws DatabaseException {\r
350                     //TimeLogger.resetTimeAndLog("find(" + graph.getURI(index) + ", " + filter + ")");\r
351                     Collection<Resource> indexResult = graph.syncRequest(new QueryIndex(index, filter), TransientCacheListener.<Collection<Resource>>instance());\r
352                     //TimeLogger.log("found " + indexResult.size());\r
353                     return indexResult;\r
354                 }\r
355 \r
356             });\r
357         } catch (DatabaseException ex) {\r
358             Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex));\r
359         }\r
360 \r
361         progressMonitor.done();\r
362     }\r
363 \r
364     /**\r
365      * A (cacheable) query to optimize single index queries for immutable\r
366      * indexes such as ontologies.\r
367      */\r
368     static class QueryIndex extends BinaryRead<Resource, String, Collection<Resource>> {\r
369 \r
370         public QueryIndex(Resource index, String filter) {\r
371             super(index, filter);\r
372         }\r
373 \r
374         @Override\r
375         public Collection<Resource> perform(ReadGraph graph)\r
376                 throws DatabaseException {\r
377             Layer0X L0X = Layer0X.getInstance(graph);\r
378 \r
379             @SuppressWarnings({ "unchecked", "rawtypes" })\r
380             Function dependencies = graph.syncRequest(new PossibleAdapter(L0X.DependencyResources, Function.class), TransientCacheListener.<Function>instance());\r
381             if (dependencies == null)\r
382                 return Collections.emptyList();\r
383 \r
384             @SuppressWarnings("unchecked")\r
385             List<Resource> results = (List<Resource>) dependencies.apply(graph, parameter, parameter2, DEFAULT_MAX_INDEX_HITS);\r
386             if (results == null || results.isEmpty())\r
387                 return Collections.emptyList();\r
388 \r
389             // TreeSet to keep the results in deterministic order and to prevent duplicates.\r
390             Set<Resource> resultSet = new TreeSet<>();\r
391             for (Resource res : results) {\r
392                 if (res != null && !resultSet.contains(res))\r
393                     resultSet.add(res);\r
394             }\r
395             return new ArrayList<Resource>(resultSet);\r
396         }\r
397 \r
398     }\r
399 \r
400     private long referencedResourceId(String pattern) {\r
401         Matcher m = ID_PATTERN.matcher(pattern);\r
402         if (m.matches()) {\r
403            String id = m.group(1);\r
404            try {\r
405                return Long.parseLong(id);\r
406            } catch (NumberFormatException nfe) {\r
407            }\r
408         }\r
409         return 0;\r
410     }\r
411 \r
412     @Override\r
413     protected IDialogSettings getDialogSettings() {\r
414         IDialogSettings settings = Activator.getDefault().getDialogSettings()\r
415         .getSection(SEARCH_RESOURCE_DIALOG);\r
416         if (settings == null) {\r
417             settings = Activator.getDefault().getDialogSettings()\r
418             .addNewSection(SEARCH_RESOURCE_DIALOG);\r
419         }\r
420         return settings;\r
421     }\r
422 \r
423     @SuppressWarnings("unchecked")\r
424     @Override\r
425     public String getElementName(Object item) {\r
426         return ((Container<Resource>)item).get().getResourceId()+"";\r
427         //return item.toString();\r
428     }\r
429 \r
430     @Override\r
431     protected Comparator<?> getItemsComparator() {\r
432         return (arg0, arg1) -> {\r
433             return arg0.toString().compareTo(arg1.toString());\r
434         };\r
435     }\r
436 \r
437     @Override\r
438     protected IStatus validateItem(Object item) {\r
439         return Status.OK_STATUS;\r
440     }\r
441 \r
442     public IResourceFilter getResourceFilter() {\r
443         return resourceFilter;\r
444     }\r
445 \r
446     public void setResourceFilter(IResourceFilter resourceFilter) {\r
447         this.resourceFilter = resourceFilter;\r
448     }\r
449 \r
450     private String getFilterForResourceFilter(IResourceFilter filter) {\r
451         if (filter == null || filter == ResourceSearch.FILTER_ALL)\r
452             return "";\r
453         if (filter == ResourceSearch.FILTER_RELATIONS)\r
454             return "Types:Relation"; \r
455         if (filter == ResourceSearch.FILTER_TYPES)\r
456             return "Types:Type"; \r
457         return "";\r
458     }\r
459 \r
460 }\r