1 /*******************************************************************************
2 * Copyright (c) 2007, 2019 Association for Decentralized Information Management
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
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;
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;
24 import java.util.TreeSet;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27 import java.util.stream.Collectors;
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;
81 * TODO Add Debugger Composite as preview!
83 public class SearchResourceDialog extends FilteredItemsSelectionDialog {
85 private static final Logger LOGGER = LoggerFactory.getLogger(SearchResourceDialog.class);
88 * The default maximum amount of Dependencies index hits to produce as results.
90 private static final int DEFAULT_MAX_INDEX_HITS = 1000;
92 private static final Pattern ID_PATTERN = Pattern.compile("\\$([0-9]+)");
94 private static final String SEARCH_RESOURCE_DIALOG = "SearchResourceDialog"; //$NON-NLS-1$
96 private static final int SHOW_IN_BROWSER_ID = IDialogConstants.CLIENT_ID + 1;
98 private static final String SHOW_IN_BROWSER_LABEL = "Show In Browser";
100 private Session session;
101 @SuppressWarnings("unused")
102 private IStructuredSelection selection;
103 private ResourceManager resourceManager;
104 private IResourceFilter resourceFilter = ResourceSearch.FILTER_ALL;
106 LabelProvider detailsLabelProvider = new LabelProvider() {
108 public String getText(Object element) {
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();
118 return session.syncRequest(new Read<String>() {
120 public String perform(ReadGraph g) throws DatabaseException {
121 String name = NameUtils.getSafeName(g, r);
122 String uri = DebugUtils.getPossibleRootRelativePath(g, r);
124 "[" + r.getResourceId() + "] - "
126 + (uri != null ? " - " : "")
127 + (uri != null ? uri : "");
130 } catch (DatabaseException e) {
131 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Resource label provider failed unexpectedly.", e));
137 static class ElementLabelProvider extends ResourceLabelProvider {
138 public ElementLabelProvider(Display display) {
142 public String getText(Object element) {
145 return element.toString();
148 public Image getImage(Object element) {
151 @SuppressWarnings("unchecked")
152 Container<Resource> rc = (Container<Resource>) element;
153 final Resource r = rc.get();
154 return super.getImage(r);
158 ElementLabelProvider labelProvider;
160 public SearchResourceDialog(Session s, boolean multi, Shell shell, String title) {
161 this(s, multi, shell, title, null);
164 public SearchResourceDialog(Session s, boolean multi, Shell shell, String title, IStructuredSelection selection) {
167 this.selection = selection;
168 this.labelProvider = new ElementLabelProvider(shell.getDisplay());
169 setMessage("Enter name, resource URI or ID");
170 setListLabelProvider(labelProvider);
171 setDetailsLabelProvider(detailsLabelProvider);
173 //setInitialPattern("*", FilteredItemsSelectionDialog.FULL_SELECTION);
174 setSelectionHistory(new ResourceSelectionHistory());
175 setSeparatorLabel("Previously selected above, others below");
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")));
182 super.configureShell(shell);
186 protected void createButtonsForButtonBar(Composite parent) {
187 createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
189 createButton(parent, SHOW_IN_BROWSER_ID, SHOW_IN_BROWSER_LABEL,
191 createButton(parent, IDialogConstants.CANCEL_ID,
192 IDialogConstants.CANCEL_LABEL, false);
196 protected void buttonPressed(int buttonId) {
197 if (buttonId == SHOW_IN_BROWSER_ID) {
199 LabeledResource lr = (LabeledResource) getFirstResult();
201 ShowInBrowser.defaultExecute(new StructuredSelection(new ResourceWorkbenchSelectionElement(lr.resource)));
205 super.buttonPressed(buttonId);
208 class ResourceSelectionHistory extends FilteredItemsSelectionDialog.SelectionHistory {
211 protected Object restoreItemFromMemento(IMemento memento) {
212 String data = memento.getTextData();
214 SerialisationSupport support = Simantics.getSession().getService(SerialisationSupport.class);
215 Resource r = support.getResource(Long.parseLong(data));
219 String name = session.syncRequest(new UniqueRead<String>() {
221 public String perform(ReadGraph g) throws DatabaseException {
222 if (!resourceFilter.acceptResource(g, r))
226 return DebugUtils.getSafeLabel(g, r);
227 } catch (Exception ex) {
228 System.out.println("Exception thrown from restoreItemFromMemento");
230 } catch (Throwable t) {}
231 return "" + r.getResourceId();
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);
242 @SuppressWarnings("unchecked")
244 protected void storeItemToMemento(Object item, IMemento memento) {
245 if(item instanceof Container) {
247 SerialisationSupport support = Simantics.getSession().getService(SerialisationSupport.class);
248 memento.putTextData(String.valueOf(support.getRandomAccessId(((Container<Resource>)item).get())));
249 } catch (DatabaseException e) {
257 protected Control createExtendedContentArea(Composite parent) {
262 protected ItemsFilter createFilter() {
263 // NOTE: filter must be created here.
264 return new ItemsFilterWithSearchResults();
267 private class ItemsFilterWithSearchResults extends ItemsFilter {
268 private Set<Object> searchResults = new HashSet<Object>();
270 public ItemsFilterWithSearchResults() {
272 final String pattern = getPattern();
273 final boolean findUris = pattern.trim().startsWith("http:/");
274 final long referencedResourceId = referencedResourceId(pattern);
275 final boolean findIds = referencedResourceId != 0;
277 searchResults.clear();
278 if (pattern.isEmpty()) return;
279 //progressMonitor.beginTask("Searching", IProgressMonitor.UNKNOWN);
282 session.syncRequest(new ReadRequest() {
284 public void run(ReadGraph graph) throws DatabaseException {
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.
295 String uri = pattern;
296 if (uri.endsWith(Role.CHILD.getIdentifier())) {
297 uri = uri.substring(0, uri.length() - 1);
299 Resource r = graph.getPossibleResource(uri);
301 searchResults.add(new LabeledResource(DebugUtils.getSafeURI(graph, r), r));
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));
309 String[] terms = pattern.trim().split("\\s+");
310 if (terms.length == 0) return;
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())
320 filter += Dependencies.FIELD_NAME_SEARCH + ":(";
321 filter += Arrays.stream(terms).map(term -> "+" + IndexQueries.escape(term.toLowerCase(), false) + "*").collect(Collectors.joining(" "));
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))
332 searchResults.add(new LabeledResource(DebugUtils.getSafeLabel(graph, r), r));
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());
347 } catch (DatabaseException ex) {
348 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex));
354 public boolean matchItem(Object item) {
355 return searchResults.contains(item);
359 public boolean isSubFilter(ItemsFilter filter) {
364 public boolean isConsistentItem(Object item) {
369 public boolean equalsFilter(ItemsFilter filter) {
373 public void fillContentProvider(final AbstractContentProvider contentProvider) {
374 for (Object item : searchResults) {
375 contentProvider.add(item, this);
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();
389 * A (cacheable) query to optimize single index queries for immutable
390 * indexes such as ontologies.
392 static class QueryIndex extends BinaryRead<Resource, String, Collection<Resource>> {
394 public QueryIndex(Resource index, String filter) {
395 super(index, filter);
399 public Collection<Resource> perform(ReadGraph graph)
400 throws DatabaseException {
401 Layer0X L0X = Layer0X.getInstance(graph);
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();
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();
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))
419 return new ArrayList<Resource>(resultSet);
424 private long referencedResourceId(String pattern) {
425 Matcher m = ID_PATTERN.matcher(pattern);
427 String id = m.group(1);
429 return Long.parseLong(id);
430 } catch (NumberFormatException nfe) {
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);
447 @SuppressWarnings("unchecked")
449 public String getElementName(Object item) {
450 return ((Container<Resource>)item).get().getResourceId()+"";
451 //return item.toString();
455 protected Comparator<?> getItemsComparator() {
456 return (arg0, arg1) -> {
457 return arg0.toString().compareTo(arg1.toString());
462 protected IStatus validateItem(Object item) {
463 return Status.OK_STATUS;
466 public IResourceFilter getResourceFilter() {
467 return resourceFilter;
470 public void setResourceFilter(IResourceFilter resourceFilter) {
471 this.resourceFilter = resourceFilter;
474 private String getFilterForResourceFilter(Layer0 l0, IResourceFilter filter) {
475 if (filter == null || filter == ResourceSearch.FILTER_ALL)
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);