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
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
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
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
77 * TODO Add Debugger Composite as preview!
\r
79 public class SearchResourceDialog extends FilteredItemsSelectionDialog {
\r
81 private static final Logger LOGGER = LoggerFactory.getLogger(SearchResourceDialog.class);
\r
84 * The default maximum amount of Dependencies index hits to produce as results.
\r
86 private static final int DEFAULT_MAX_INDEX_HITS = 1000;
\r
88 private static final Pattern ID_PATTERN = Pattern.compile("\\$([0-9]+)");
\r
90 private static final String SEARCH_RESOURCE_DIALOG = "SearchResourceDialog"; //$NON-NLS-1$
\r
92 private static final int SHOW_IN_BROWSER_ID = IDialogConstants.CLIENT_ID + 1;
\r
94 private static final String SHOW_IN_BROWSER_LABEL = "Show In Browser";
\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
102 LabelProvider detailsLabelProvider = new LabelProvider() {
\r
104 public String getText(Object element) {
\r
105 if (element == 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
114 return session.syncRequest(new Read<String>() {
\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
121 label = label + " - " + uri;
\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
132 static class ElementLabelProvider extends ResourceLabelProvider {
\r
133 public ElementLabelProvider(Display display) {
\r
137 public String getText(Object element) {
\r
140 return element.toString();
\r
143 public Image getImage(Object element) {
\r
144 if (element == 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
153 ElementLabelProvider labelProvider;
\r
155 public SearchResourceDialog(Session s, boolean multi, Shell shell, String title) {
\r
156 this(s, multi, shell, title, null);
\r
159 public SearchResourceDialog(Session s, boolean multi, Shell shell, String title, IStructuredSelection selection) {
\r
160 super(shell, multi);
\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
168 //setInitialPattern("*", FilteredItemsSelectionDialog.FULL_SELECTION);
\r
169 setSelectionHistory(new ResourceSelectionHistory());
\r
170 setSeparatorLabel("Previously selected above, others below");
\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
181 protected void createButtonsForButtonBar(Composite parent) {
\r
182 createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
\r
184 createButton(parent, SHOW_IN_BROWSER_ID, SHOW_IN_BROWSER_LABEL,
\r
186 createButton(parent, IDialogConstants.CANCEL_ID,
\r
187 IDialogConstants.CANCEL_LABEL, false);
\r
191 protected void buttonPressed(int buttonId) {
\r
192 if (buttonId == SHOW_IN_BROWSER_ID) {
\r
194 LabeledResource lr = (LabeledResource) getFirstResult();
\r
195 ShowInBrowser.defaultExecute(new StructuredSelection(new ResourceWorkbenchSelectionElement(lr.resource)));
\r
198 super.buttonPressed(buttonId);
\r
201 class ResourceSelectionHistory extends FilteredItemsSelectionDialog.SelectionHistory {
\r
204 protected Object restoreItemFromMemento(IMemento memento) {
\r
205 String data = memento.getTextData();
\r
207 SerialisationSupport support = Simantics.getSession().getService(SerialisationSupport.class);
\r
208 Resource r = support.getResource(Long.parseLong(data));
\r
212 String name = session.syncRequest(new UniqueRead<String>() {
\r
214 public String perform(ReadGraph g) throws DatabaseException {
\r
215 if (!resourceFilter.acceptResource(g, r))
\r
219 return g.adapt(r, String.class);
\r
220 } catch (Exception ex) {
\r
221 System.out.println("Exception thrown from restoreItemFromMemento");
\r
223 } catch (Throwable t) {}
\r
224 return "" + r.getResourceId();
\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
235 @SuppressWarnings("unchecked")
\r
237 protected void storeItemToMemento(Object item, IMemento memento) {
\r
238 if(item instanceof Container) {
\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
250 protected Control createExtendedContentArea(Composite parent) {
\r
255 protected ItemsFilter createFilter() {
\r
256 // NOTE: filter must be created here.
\r
257 return new ItemsFilter() {
\r
259 public boolean matchItem(Object item) {
\r
260 //return matches(item.toString());
\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
267 public boolean isSubFilter(ItemsFilter filter) {
\r
268 //System.out.println(getPattern() + " vs. " + filter.getPattern());
\r
273 public boolean isConsistentItem(Object item) {
\r
278 public boolean equalsFilter(ItemsFilter filter) {
\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
293 //progressMonitor.beginTask("Searching", IProgressMonitor.UNKNOWN);
\r
296 session.syncRequest(new ReadRequest() {
\r
298 public void run(ReadGraph graph) throws DatabaseException {
\r
299 // Find by ID first.
\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
310 String uri = pattern;
\r
311 if (uri.endsWith(Role.CHILD.getIdentifier())) {
\r
312 uri = uri.substring(0, uri.length() - 1);
\r
314 Resource r = graph.getPossibleResource(uri);
\r
316 contentProvider.add(new LabeledResource(DebugUtils.getSafeURI(graph, r), r), itemsFilter );
\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
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
330 filter += "Name:" + pattern + "*";
\r
332 Layer0 L0 = Layer0.getInstance(graph);
\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
342 contentProvider.add(new LabeledResource(DebugUtils.getSafeLabel(graph, r), r), itemsFilter);
\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
357 } catch (DatabaseException ex) {
\r
358 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex));
\r
361 progressMonitor.done();
\r
365 * A (cacheable) query to optimize single index queries for immutable
\r
366 * indexes such as ontologies.
\r
368 static class QueryIndex extends BinaryRead<Resource, String, Collection<Resource>> {
\r
370 public QueryIndex(Resource index, String filter) {
\r
371 super(index, filter);
\r
375 public Collection<Resource> perform(ReadGraph graph)
\r
376 throws DatabaseException {
\r
377 Layer0X L0X = Layer0X.getInstance(graph);
\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
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
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
395 return new ArrayList<Resource>(resultSet);
\r
400 private long referencedResourceId(String pattern) {
\r
401 Matcher m = ID_PATTERN.matcher(pattern);
\r
403 String id = m.group(1);
\r
405 return Long.parseLong(id);
\r
406 } catch (NumberFormatException nfe) {
\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
423 @SuppressWarnings("unchecked")
\r
425 public String getElementName(Object item) {
\r
426 return ((Container<Resource>)item).get().getResourceId()+"";
\r
427 //return item.toString();
\r
431 protected Comparator<?> getItemsComparator() {
\r
432 return (arg0, arg1) -> {
\r
433 return arg0.toString().compareTo(arg1.toString());
\r
438 protected IStatus validateItem(Object item) {
\r
439 return Status.OK_STATUS;
\r
442 public IResourceFilter getResourceFilter() {
\r
443 return resourceFilter;
\r
446 public void setResourceFilter(IResourceFilter resourceFilter) {
\r
447 this.resourceFilter = resourceFilter;
\r
450 private String getFilterForResourceFilter(IResourceFilter filter) {
\r
451 if (filter == null || filter == ResourceSearch.FILTER_ALL)
\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