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