--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2013 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ * Semantum Oy - index based searching and graph manipulation (#4255)\r
+ *******************************************************************************/\r
+package org.simantics.debug.ui;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.HashSet;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.TreeSet;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.eclipse.core.runtime.CoreException;\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.Status;\r
+import org.eclipse.jface.dialogs.IDialogSettings;\r
+import org.eclipse.jface.resource.JFaceResources;\r
+import org.eclipse.jface.resource.LocalResourceManager;\r
+import org.eclipse.jface.resource.ResourceManager;\r
+import org.eclipse.jface.viewers.IStructuredSelection;\r
+import org.eclipse.jface.viewers.LabelProvider;\r
+import org.eclipse.swt.events.DisposeEvent;\r
+import org.eclipse.swt.events.DisposeListener;\r
+import org.eclipse.swt.graphics.Image;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Control;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.swt.widgets.Shell;\r
+import org.eclipse.ui.IMemento;\r
+import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;\r
+import org.simantics.Simantics;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.common.primitiverequest.Adapter;\r
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
+import org.simantics.db.common.request.BinaryRead;\r
+import org.simantics.db.common.request.ObjectsWithType;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.common.uri.UnescapedChildMapOfResource;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.migration.OntologiesFromLibrary;\r
+import org.simantics.db.layer0.variable.Variables.Role;\r
+import org.simantics.db.request.Read;\r
+import org.simantics.db.service.SerialisationSupport;\r
+import org.simantics.debug.ui.ResourceSearch.IResourceFilter;\r
+import org.simantics.debug.ui.internal.Activator;\r
+import org.simantics.debug.ui.internal.DebugUtils;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.operation.Layer0X;\r
+import org.simantics.scl.runtime.function.Function;\r
+import org.simantics.ui.SimanticsUI;\r
+import org.simantics.ui.workbench.dialogs.ResourceLabelProvider;\r
+import org.simantics.utils.Container;\r
+import org.simantics.utils.ui.BundleUtils;\r
+\r
+/**\r
+ * TODO Add Debugger Composite as preview!\r
+ */\r
+public class SearchResourceDialog extends FilteredItemsSelectionDialog {\r
+\r
+ private static final String SEARCH_RESOURCE_DIALOG = "SearchResourceDialog"; //$NON-NLS-1$\r
+\r
+ Session session;\r
+ IStructuredSelection selection;\r
+ ResourceManager resourceManager;\r
+\r
+ // Needed in a subclass\r
+ protected IResourceFilter resourceFilter = ResourceSearch.FILTER_ALL;\r
+\r
+ LabelProvider detailsLabelProvider = new LabelProvider() {\r
+ @Override\r
+ public String getText(Object element) {\r
+ if (element == null)\r
+ return "null";\r
+ // This may happen if multiple choice is enabled\r
+ if (element instanceof String)\r
+ return (String) element;\r
+ @SuppressWarnings("unchecked")\r
+ Container<Resource> rc = (Container<Resource>) element;\r
+ final Resource r = rc.get();\r
+ try {\r
+ return session.syncRequest(new Read<String>() {\r
+ @Override\r
+ public String perform(ReadGraph g) throws DatabaseException {\r
+ String name = NameUtils.getSafeName(g, r);\r
+ String uri = g.getPossibleURI(r);\r
+ String label = "[" + r.getResourceId() + "] - " + name;\r
+ if (uri != null)\r
+ label = label + " - " + uri;\r
+ return label;\r
+ }\r
+ });\r
+ } catch (DatabaseException e) {\r
+ Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Resource label provider failed unexpectedly.", e));\r
+ return "";\r
+ }\r
+ }\r
+ };\r
+\r
+ static class ElementLabelProvider extends ResourceLabelProvider {\r
+ public ElementLabelProvider(Display display) {\r
+ super(display);\r
+ }\r
+ @Override\r
+ public String getText(Object element) {\r
+ if (element==null)\r
+ return "null";\r
+ return element.toString();\r
+ }\r
+ @Override\r
+ public Image getImage(Object element) {\r
+ if (element == null)\r
+ return null;\r
+ @SuppressWarnings("unchecked")\r
+ Container<Resource> rc = (Container<Resource>) element;\r
+ final Resource r = rc.get();\r
+ return super.getImage(r);\r
+ }\r
+ };\r
+\r
+ ElementLabelProvider labelProvider;\r
+\r
+ public SearchResourceDialog(Session s, boolean multi, Shell shell, String title) {\r
+ this(s, multi, shell, title, null);\r
+ }\r
+\r
+ public SearchResourceDialog(Session s, boolean multi, Shell shell, String title, IStructuredSelection selection) {\r
+ super(shell, multi);\r
+ this.session = s;\r
+ resourceManager = new LocalResourceManager(JFaceResources.getResources());\r
+ this.labelProvider = new ElementLabelProvider(shell.getDisplay());\r
+ setMessage("Enter ID, URI or name");\r
+ setListLabelProvider(labelProvider);\r
+ setDetailsLabelProvider(detailsLabelProvider);\r
+ setImage((Image) resourceManager.get(BundleUtils.getImageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/cog_blue.png")));\r
+ setTitle(title);\r
+ //setInitialPattern("*", FilteredItemsSelectionDialog.FULL_SELECTION);\r
+ setSelectionHistory(new ResourceSelectionHistory());\r
+ this.selection = selection;\r
+ setSeparatorLabel("Previously selected above, others below");\r
+ }\r
+\r
+ @Override\r
+ protected void configureShell(Shell shell) {\r
+ super.configureShell(shell);\r
+ shell.addDisposeListener(new DisposeListener() {\r
+ @Override\r
+ public void widgetDisposed(DisposeEvent e) {\r
+ resourceManager.dispose();\r
+ }\r
+ });\r
+ }\r
+\r
+ class ResourceSelectionHistory extends FilteredItemsSelectionDialog.SelectionHistory {\r
+\r
+ @Override\r
+ protected Object restoreItemFromMemento(IMemento memento) {\r
+ String dada = memento.getTextData();\r
+ try {\r
+ SerialisationSupport support = SimanticsUI.getSession().getService(SerialisationSupport.class);\r
+ final Resource r = support.getResource(Long.parseLong(dada));\r
+ if (r == null) {\r
+ return null;\r
+ }\r
+\r
+ String name = session.syncRequest(new Read<String>() {\r
+ @Override\r
+ public String perform(ReadGraph g) throws DatabaseException {\r
+ if (!resourceFilter.acceptResource(g, r)) {\r
+ return null;\r
+ }\r
+ String name = null;\r
+ try {\r
+ try {\r
+ name = g.adapt(r, String.class);\r
+ } catch (Exception ex) {\r
+ System.out.println("Exception thrown from restoreItemFromMemento");\r
+ }\r
+ if (name != null) {\r
+ return name;\r
+ }\r
+ } catch (Throwable t) {}\r
+ return "" + r.getResourceId();\r
+ }\r
+ });\r
+ if (name==null) return null;\r
+ return new LabeledResource(name, r);\r
+ } catch (NumberFormatException e) {\r
+ e.printStackTrace();\r
+ return null;\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ return null;\r
+ }\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ protected void storeItemToMemento(Object item, IMemento memento) {\r
+ if(item instanceof Container) {\r
+ try {\r
+ SerialisationSupport support = SimanticsUI.getSession().getService(SerialisationSupport.class);\r
+ memento.putTextData(String.valueOf(support.getRandomAccessId(((Container<Resource>)item).get())));\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+ };\r
+\r
+ @Override\r
+ protected Control createExtendedContentArea(Composite parent) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ protected ItemsFilter createFilter() {\r
+ // NOTE: filter must be created here.\r
+ return new ItemsFilter() {\r
+ @Override\r
+ public boolean matchItem(Object item) {\r
+ //return matches(item.toString());\r
+ return true;\r
+ }\r
+\r
+ // If this method returns true, it means fillContentProvider will\r
+ // not be called again, but the existing results are just re-filtered.\r
+ @Override\r
+ public boolean isSubFilter(ItemsFilter filter) {\r
+ //System.out.println(getPattern() + " vs. " + filter.getPattern());\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public boolean isConsistentItem(Object item) {\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public boolean equalsFilter(ItemsFilter filter) {\r
+ return false;\r
+ }\r
+ };\r
+ }\r
+\r
+ @Override\r
+ protected void fillContentProvider(final AbstractContentProvider contentProvider,\r
+ final ItemsFilter itemsFilter, final IProgressMonitor progressMonitor)\r
+ throws CoreException {\r
+ final String pattern = itemsFilter.getPattern();\r
+ final boolean findUris = pattern.trim().startsWith("http:/");\r
+ final long referencedResourceId = referencedResourceId(pattern);\r
+ final boolean findIds = referencedResourceId != 0;\r
+\r
+ progressMonitor.beginTask("Searching", IProgressMonitor.UNKNOWN);\r
+\r
+ try {\r
+ session.syncRequest(new ReadRequest() {\r
+ @Override\r
+ public void run(ReadGraph graph) throws DatabaseException {\r
+ // Find by ID first.\r
+ if (findIds) {\r
+ try {\r
+ Resource r = graph.getService(SerialisationSupport.class).getResource(referencedResourceId);\r
+ contentProvider.add(new LabeledResource(DebugUtils.getSafeLabel(graph, r), r), itemsFilter);\r
+ //contentProvider.add(new LabeledResource(pattern, r), itemsFilter);\r
+ } catch (DatabaseException e) {\r
+ // No resource for specified id.\r
+ }\r
+ }\r
+ if (findUris) {\r
+ String uri = pattern;\r
+ if (uri.endsWith(Role.CHILD.getIdentifier())) {\r
+ uri = uri.substring(0, uri.length() - 1);\r
+ }\r
+ Resource r = graph.getPossibleResource(uri);\r
+ if (r != null) {\r
+ contentProvider.add(new LabeledResource(DebugUtils.getSafeURI(graph, r), r), itemsFilter );\r
+\r
+ Map<String, Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(r));\r
+ for (Resource child : children.values()) {\r
+ contentProvider.add(new LabeledResource(DebugUtils.getSafeURI(graph, child), child), itemsFilter );\r
+ }\r
+ }\r
+ } else {\r
+ Resource project = Simantics.peekProjectResource();\r
+ if (project != null) {\r
+ IResourceFilter rf = resourceFilter;\r
+ String filter = getFilterForResourceFilter(rf);\r
+ if (!filter.isEmpty())\r
+ filter += " AND ";\r
+ filter += "Name:" + pattern + "*";\r
+\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ \r
+ HashSet<Resource> indexRoots = new HashSet<Resource>();\r
+ indexRoots.addAll(graph.syncRequest(new ObjectsWithType(project, L0.ConsistsOf, L0.IndexRoot)));\r
+ indexRoots.addAll(graph.syncRequest(new OntologiesFromLibrary(graph.getRootLibrary())));\r
+ for (Resource indexRoot : indexRoots) {\r
+ Collection<Resource> hits = new ArrayList<Resource>(find(graph, indexRoot, filter));\r
+ hits.add(indexRoot);\r
+ for (Resource r : hits) {\r
+ if (rf != null && !rf.acceptResource(graph, r))\r
+ continue;\r
+ contentProvider.add(new LabeledResource(DebugUtils.getSafeLabel(graph, r), r), itemsFilter);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ public Collection<Resource> find(ReadGraph graph, Resource index, String filter) throws DatabaseException {\r
+ Collection<Resource> indexResult = graph.syncRequest(new QueryIndex(index, filter), TransientCacheListener.<Collection<Resource>>instance());\r
+\r
+// Layer0 L0 = Layer0.getInstance(graph);\r
+// Collection<Resource> linkedRoots = graph.syncRequest(new ObjectsWithType(index, L0.IsLinkedTo, L0.IndexRoot));\r
+// if (linkedRoots.isEmpty())\r
+ return indexResult;\r
+\r
+// Collection<Resource> result = indexResult;\r
+// for (Resource dep : linkedRoots) {\r
+// Collection<Resource> linkedIndexResults = find(graph, dep, filter);\r
+// if (linkedIndexResults.isEmpty())\r
+// continue;\r
+// if (result == indexResult) {\r
+// result = new ArrayList<Resource>(indexResult.size() + linkedIndexResults.size());\r
+// result.addAll(indexResult);\r
+// } else {\r
+// }\r
+// result.addAll(linkedIndexResults);\r
+// }\r
+// return result;\r
+ }\r
+\r
+ });\r
+ } catch (DatabaseException ex) {\r
+ Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex));\r
+ }\r
+\r
+ progressMonitor.done();\r
+ }\r
+\r
+ /**\r
+ * A (cacheable) query to optimize single index queries for immutable\r
+ * indexes such as ontologies.\r
+ */\r
+ static class QueryIndex extends BinaryRead<Resource, String, Collection<Resource>> {\r
+\r
+ public QueryIndex(Resource index, String filter) {\r
+ super(index, filter);\r
+ }\r
+\r
+ @Override\r
+ public Collection<Resource> perform(ReadGraph graph)\r
+ throws DatabaseException {\r
+ Layer0X L0X = Layer0X.getInstance(graph);\r
+\r
+ @SuppressWarnings({ "unchecked", "rawtypes" })\r
+ Function dependencies = graph.syncRequest(new Adapter(L0X.Dependencies, Function.class), TransientCacheListener.<Function>instance());\r
+\r
+ @SuppressWarnings("unchecked")\r
+ Collection<Map<String, Object>> results = (Collection<Map<String, Object>>)dependencies.apply(graph, parameter, parameter2);\r
+ if (results == null || results.isEmpty())\r
+ return Collections.emptyList();\r
+\r
+ // TreeSet to keep the results in deterministic order.\r
+ Set<Resource> resultSet = new TreeSet<Resource>();\r
+ for (Map<String, Object> entry : results) {\r
+ Resource res = (Resource)entry.get("Resource");\r
+ if (res != null && !resultSet.contains(res))\r
+ resultSet.add(res);\r
+ }\r
+\r
+ return new ArrayList<Resource>(resultSet);\r
+ }\r
+\r
+ }\r
+\r
+ Pattern ID_PATTERN = Pattern.compile("\\$([0-9]+)");\r
+\r
+ private long referencedResourceId(String pattern) {\r
+ Matcher m = ID_PATTERN.matcher(pattern);\r
+ if (m.matches()) {\r
+ String id = m.group(1);\r
+ try {\r
+ return Long.parseLong(id);\r
+ } catch (NumberFormatException nfe) {\r
+ }\r
+ }\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ protected IDialogSettings getDialogSettings() {\r
+ IDialogSettings settings = Activator.getDefault().getDialogSettings()\r
+ .getSection(SEARCH_RESOURCE_DIALOG);\r
+ if (settings == null) {\r
+ settings = Activator.getDefault().getDialogSettings()\r
+ .addNewSection(SEARCH_RESOURCE_DIALOG);\r
+ }\r
+ return settings;\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public String getElementName(Object item) {\r
+ return ((Container<Resource>)item).get().getResourceId()+"";\r
+ //return item.toString();\r
+ }\r
+\r
+ @SuppressWarnings("rawtypes")\r
+ @Override\r
+ protected Comparator getItemsComparator() {\r
+ return new Comparator() {\r
+ @Override\r
+ public int compare(Object arg0, Object arg1) {\r
+ return arg0.toString().compareTo(arg1.toString());\r
+ }\r
+ };\r
+ }\r
+\r
+ @Override\r
+ protected IStatus validateItem(Object item) {\r
+ return Status.OK_STATUS;\r
+ }\r
+\r
+ public IResourceFilter getResourceFilter() {\r
+ return resourceFilter;\r
+ }\r
+\r
+ public void setResourceFilter(IResourceFilter resourceFilter) {\r
+ this.resourceFilter = resourceFilter;\r
+ }\r
+\r
+ private String getFilterForResourceFilter(IResourceFilter filter) {\r
+ if (filter == null || filter == ResourceSearch.FILTER_ALL)\r
+ return "";\r
+ if (filter == ResourceSearch.FILTER_RELATIONS)\r
+ return "Types:Relation"; \r
+ if (filter == ResourceSearch.FILTER_TYPES)\r
+ return "Types:Type"; \r
+ return "";\r
+ }\r
+\r
+}\r