]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.debug.ui/src/org/simantics/debug/ui/SearchResourceDialog.java
Fixed labels for search results restored from previous search memento
[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 = DebugUtils.getPossibleRootRelativePath(g, r);\r
119                         return\r
120                                 "[" + r.getResourceId() + "] - "\r
121                                 + name\r
122                                 + (uri != null ? " - " : "")\r
123                                 + (uri != null ? uri : "");\r
124                     }\r
125                 });\r
126             } catch (DatabaseException e) {\r
127                 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Resource label provider failed unexpectedly.", e));\r
128                 return "";\r
129             }\r
130         }\r
131     };\r
132 \r
133     static class ElementLabelProvider extends ResourceLabelProvider {\r
134         public ElementLabelProvider(Display display) {\r
135             super(display);\r
136         }\r
137         @Override\r
138         public String getText(Object element) {\r
139             if (element==null)\r
140                 return "null";\r
141             return element.toString();\r
142         }\r
143         @Override\r
144         public Image getImage(Object element) {\r
145             if (element == null)\r
146                 return null;\r
147             @SuppressWarnings("unchecked")\r
148             Container<Resource> rc = (Container<Resource>) element;\r
149             final Resource r = rc.get();\r
150             return super.getImage(r);\r
151         }\r
152     };\r
153 \r
154     ElementLabelProvider labelProvider;\r
155 \r
156     public SearchResourceDialog(Session s, boolean multi, Shell shell, String title) {\r
157         this(s, multi, shell, title, null);\r
158     }\r
159 \r
160     public SearchResourceDialog(Session s, boolean multi, Shell shell, String title, IStructuredSelection selection) {\r
161         super(shell, multi);\r
162         this.session = s;\r
163         this.selection = selection;\r
164         this.labelProvider = new ElementLabelProvider(shell.getDisplay());\r
165         setMessage("Enter name, resource URI or ID");\r
166         setListLabelProvider(labelProvider);\r
167         setDetailsLabelProvider(detailsLabelProvider);\r
168         setTitle(title);\r
169         //setInitialPattern("*", FilteredItemsSelectionDialog.FULL_SELECTION);\r
170         setSelectionHistory(new ResourceSelectionHistory());\r
171         setSeparatorLabel("Previously selected above, others below");\r
172     }\r
173 \r
174     @Override\r
175     protected void configureShell(Shell shell) {\r
176         this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), shell);\r
177         setImage((Image) resourceManager.get(BundleUtils.getImageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/cog_blue.png")));\r
178         super.configureShell(shell);\r
179     }\r
180 \r
181     @Override\r
182     protected void createButtonsForButtonBar(Composite parent) {\r
183         createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,\r
184                 true);\r
185         createButton(parent, SHOW_IN_BROWSER_ID, SHOW_IN_BROWSER_LABEL,\r
186                 true);\r
187         createButton(parent, IDialogConstants.CANCEL_ID,\r
188                 IDialogConstants.CANCEL_LABEL, false);\r
189     }\r
190 \r
191     @Override\r
192     protected void buttonPressed(int buttonId) {\r
193         if (buttonId == SHOW_IN_BROWSER_ID) {\r
194             okPressed();\r
195             LabeledResource lr = (LabeledResource) getFirstResult();\r
196             ShowInBrowser.defaultExecute(new StructuredSelection(new ResourceWorkbenchSelectionElement(lr.resource)));\r
197             return;\r
198         }\r
199         super.buttonPressed(buttonId);\r
200     }\r
201 \r
202     class ResourceSelectionHistory extends FilteredItemsSelectionDialog.SelectionHistory {\r
203 \r
204         @Override\r
205         protected Object restoreItemFromMemento(IMemento memento) {\r
206             String data = memento.getTextData();\r
207             try {\r
208                 SerialisationSupport support = Simantics.getSession().getService(SerialisationSupport.class);\r
209                 Resource r = support.getResource(Long.parseLong(data));\r
210                 if (r == null)\r
211                     return null;\r
212 \r
213                 String name = session.syncRequest(new UniqueRead<String>() {\r
214                     @Override\r
215                     public String perform(ReadGraph g) throws DatabaseException {\r
216                         if (!resourceFilter.acceptResource(g, r))\r
217                             return null;\r
218                         try {\r
219                             try {\r
220                                 return DebugUtils.getSafeLabel(g, r);\r
221                             } catch (Exception ex) {\r
222                                 System.out.println("Exception thrown from restoreItemFromMemento");\r
223                             }\r
224                         } catch (Throwable t) {}\r
225                         return "" + r.getResourceId();\r
226                     }\r
227                 });\r
228                 if (name==null) return null;\r
229                 return new LabeledResource(name, r);\r
230             } catch (NumberFormatException | DatabaseException e) {\r
231                 LOGGER.info("Search memento restoration failed.", e);\r
232                 return null;\r
233             }\r
234         }\r
235 \r
236         @SuppressWarnings("unchecked")\r
237         @Override\r
238         protected void storeItemToMemento(Object item, IMemento memento) {\r
239             if(item instanceof Container) {\r
240                 try {\r
241                     SerialisationSupport support = Simantics.getSession().getService(SerialisationSupport.class);\r
242                     memento.putTextData(String.valueOf(support.getRandomAccessId(((Container<Resource>)item).get())));\r
243                 } catch (DatabaseException e) {\r
244                     e.printStackTrace();\r
245                 }\r
246             }\r
247         }\r
248     };\r
249 \r
250     @Override\r
251     protected Control createExtendedContentArea(Composite parent) {\r
252         return null;\r
253     }\r
254 \r
255     @Override\r
256     protected ItemsFilter createFilter() {\r
257         // NOTE: filter must be created here.\r
258         return new ItemsFilter() {\r
259             @Override\r
260             public boolean matchItem(Object item) {\r
261                 //return matches(item.toString());\r
262                 return true;\r
263             }\r
264 \r
265             // If this method returns true, it means fillContentProvider will\r
266             // not be called again, but the existing results are just re-filtered.\r
267             @Override\r
268             public boolean isSubFilter(ItemsFilter filter) {\r
269                 //System.out.println(getPattern() + " vs. " + filter.getPattern());\r
270                 return false;\r
271             }\r
272 \r
273             @Override\r
274             public boolean isConsistentItem(Object item) {\r
275                 return true;\r
276             }\r
277 \r
278             @Override\r
279             public boolean equalsFilter(ItemsFilter filter) {\r
280                 return false;\r
281             }\r
282         };\r
283     }\r
284 \r
285     @Override\r
286     protected void fillContentProvider(final AbstractContentProvider contentProvider,\r
287             final ItemsFilter itemsFilter, final IProgressMonitor progressMonitor)\r
288                     throws CoreException {\r
289         final String pattern = itemsFilter.getPattern();\r
290         final boolean findUris = pattern.trim().startsWith("http:/");\r
291         final long referencedResourceId = referencedResourceId(pattern);\r
292         final boolean findIds = referencedResourceId != 0;\r
293 \r
294         //progressMonitor.beginTask("Searching", IProgressMonitor.UNKNOWN);\r
295 \r
296         try {\r
297             session.syncRequest(new ReadRequest() {\r
298                 @Override\r
299                 public void run(ReadGraph graph) throws DatabaseException {\r
300                     // Find by ID first.\r
301                     if (findIds) {\r
302                         try {\r
303                             Resource r = graph.getService(SerialisationSupport.class).getResource(referencedResourceId);\r
304                             contentProvider.add(new LabeledResource(DebugUtils.getSafeLabel(graph, r), r), itemsFilter);\r
305                             //contentProvider.add(new LabeledResource(pattern, r), itemsFilter);\r
306                         } catch (DatabaseException e) {\r
307                             // No resource for specified id.\r
308                         }\r
309                     }\r
310                     if (findUris) {\r
311                         String uri = pattern;\r
312                         if (uri.endsWith(Role.CHILD.getIdentifier())) {\r
313                             uri = uri.substring(0, uri.length() - 1);\r
314                         }\r
315                         Resource r = graph.getPossibleResource(uri);\r
316                         if (r != null) {\r
317                             contentProvider.add(new LabeledResource(DebugUtils.getSafeURI(graph, r), r), itemsFilter );\r
318 \r
319                             Map<String, Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(r));\r
320                             for (Resource child : children.values()) {\r
321                                 contentProvider.add(new LabeledResource(DebugUtils.getSafeURI(graph, child), child), itemsFilter );\r
322                             }\r
323                         }\r
324                     } else {\r
325                         Resource project = Simantics.peekProjectResource();\r
326                         if (project != null) {\r
327                             IResourceFilter rf = resourceFilter;\r
328                             String filter = getFilterForResourceFilter(rf);\r
329                             if (!filter.isEmpty())\r
330                                 filter += " AND ";\r
331                             filter += "Name:" + pattern + "*";\r
332 \r
333                             Layer0 L0 = Layer0.getInstance(graph);\r
334 \r
335                             Set<Resource> indexRoots = new HashSet<>();\r
336                             indexRoots.addAll(graph.syncRequest(new ObjectsWithType(project, L0.ConsistsOf, L0.IndexRoot)));\r
337                             indexRoots.addAll(graph.syncRequest(new OntologiesFromLibrary(graph.getRootLibrary())));\r
338                             for (Resource indexRoot : indexRoots) {\r
339                                 Collection<Resource> hits = find(graph, indexRoot, filter);\r
340                                 for (Resource r : hits) {\r
341                                     if (rf != null && !rf.acceptResource(graph, r))\r
342                                         continue;\r
343                                     contentProvider.add(new LabeledResource(DebugUtils.getSafeLabel(graph, r), r), itemsFilter);\r
344                                 }\r
345                             }\r
346                         }\r
347                     }\r
348                 }\r
349 \r
350                 public Collection<Resource> find(ReadGraph graph, Resource index, String filter) throws DatabaseException {\r
351                     //TimeLogger.resetTimeAndLog("find(" + graph.getURI(index) + ", " + filter + ")");\r
352                     Collection<Resource> indexResult = graph.syncRequest(new QueryIndex(index, filter), TransientCacheListener.<Collection<Resource>>instance());\r
353                     //TimeLogger.log("found " + indexResult.size());\r
354                     return indexResult;\r
355                 }\r
356 \r
357             });\r
358         } catch (DatabaseException ex) {\r
359             Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex));\r
360         }\r
361 \r
362         progressMonitor.done();\r
363     }\r
364 \r
365     /**\r
366      * A (cacheable) query to optimize single index queries for immutable\r
367      * indexes such as ontologies.\r
368      */\r
369     static class QueryIndex extends BinaryRead<Resource, String, Collection<Resource>> {\r
370 \r
371         public QueryIndex(Resource index, String filter) {\r
372             super(index, filter);\r
373         }\r
374 \r
375         @Override\r
376         public Collection<Resource> perform(ReadGraph graph)\r
377                 throws DatabaseException {\r
378             Layer0X L0X = Layer0X.getInstance(graph);\r
379 \r
380             @SuppressWarnings({ "unchecked", "rawtypes" })\r
381             Function dependencies = graph.syncRequest(new PossibleAdapter(L0X.DependencyResources, Function.class), TransientCacheListener.<Function>instance());\r
382             if (dependencies == null)\r
383                 return Collections.emptyList();\r
384 \r
385             @SuppressWarnings("unchecked")\r
386             List<Resource> results = (List<Resource>) dependencies.apply(graph, parameter, parameter2, DEFAULT_MAX_INDEX_HITS);\r
387             if (results == null || results.isEmpty())\r
388                 return Collections.emptyList();\r
389 \r
390             // TreeSet to keep the results in deterministic order and to prevent duplicates.\r
391             Set<Resource> resultSet = new TreeSet<>();\r
392             for (Resource res : results) {\r
393                 if (res != null && !resultSet.contains(res))\r
394                     resultSet.add(res);\r
395             }\r
396             return new ArrayList<Resource>(resultSet);\r
397         }\r
398 \r
399     }\r
400 \r
401     private long referencedResourceId(String pattern) {\r
402         Matcher m = ID_PATTERN.matcher(pattern);\r
403         if (m.matches()) {\r
404            String id = m.group(1);\r
405            try {\r
406                return Long.parseLong(id);\r
407            } catch (NumberFormatException nfe) {\r
408            }\r
409         }\r
410         return 0;\r
411     }\r
412 \r
413     @Override\r
414     protected IDialogSettings getDialogSettings() {\r
415         IDialogSettings settings = Activator.getDefault().getDialogSettings()\r
416         .getSection(SEARCH_RESOURCE_DIALOG);\r
417         if (settings == null) {\r
418             settings = Activator.getDefault().getDialogSettings()\r
419             .addNewSection(SEARCH_RESOURCE_DIALOG);\r
420         }\r
421         return settings;\r
422     }\r
423 \r
424     @SuppressWarnings("unchecked")\r
425     @Override\r
426     public String getElementName(Object item) {\r
427         return ((Container<Resource>)item).get().getResourceId()+"";\r
428         //return item.toString();\r
429     }\r
430 \r
431     @Override\r
432     protected Comparator<?> getItemsComparator() {\r
433         return (arg0, arg1) -> {\r
434             return arg0.toString().compareTo(arg1.toString());\r
435         };\r
436     }\r
437 \r
438     @Override\r
439     protected IStatus validateItem(Object item) {\r
440         return Status.OK_STATUS;\r
441     }\r
442 \r
443     public IResourceFilter getResourceFilter() {\r
444         return resourceFilter;\r
445     }\r
446 \r
447     public void setResourceFilter(IResourceFilter resourceFilter) {\r
448         this.resourceFilter = resourceFilter;\r
449     }\r
450 \r
451     private String getFilterForResourceFilter(IResourceFilter filter) {\r
452         if (filter == null || filter == ResourceSearch.FILTER_ALL)\r
453             return "";\r
454         if (filter == ResourceSearch.FILTER_RELATIONS)\r
455             return "Types:Relation"; \r
456         if (filter == ResourceSearch.FILTER_TYPES)\r
457             return "Types:Type"; \r
458         return "";\r
459     }\r
460 \r
461 }\r