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