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