--- /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 (#4255)\r
+ *******************************************************************************/\r
+package org.simantics.ui.workbench.dialogs;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.concurrent.CopyOnWriteArrayList;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.MultiStatus;\r
+import org.eclipse.core.runtime.Status;\r
+import org.eclipse.core.runtime.jobs.Job;\r
+import org.eclipse.jface.resource.DeviceResourceManager;\r
+import org.eclipse.jface.resource.ImageDescriptor;\r
+import org.eclipse.jface.resource.ResourceManager;\r
+import org.eclipse.jface.viewers.ILabelProvider;\r
+import org.eclipse.jface.viewers.ILabelProviderListener;\r
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;\r
+import org.eclipse.swt.graphics.Device;\r
+import org.eclipse.swt.graphics.Image;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.ui.progress.IProgressConstants;\r
+import org.simantics.DatabaseJob;\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.request.ReadRequest;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.ui.icons.ImageDescriptorProvider;\r
+import org.simantics.ui.icons.ImageUtil;\r
+import org.simantics.ui.internal.Activator;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+import org.simantics.utils.threads.SWTThread;\r
+import org.simantics.utils.ui.gfx.CompositionImageDescriptor;\r
+\r
+/**\r
+ * ResourceLabelProvider is a label provider for Simantics database\r
+ * {@link Resource}s.\r
+ * \r
+ * <p>\r
+ * This label provider utilizes {@link ImageDescriptorProvider} adapters for\r
+ * determining the icon to use.\r
+ * \r
+ * @author Toni Kalajainen\r
+ * @author Tuukka Lehtonen\r
+ * \r
+ * @see Resource\r
+ * @see ILabelProvider\r
+ * @see ILabelProviderListener\r
+ */\r
+public class ResourceLabelProvider implements ILabelProvider {\r
+\r
+ protected List<ILabelProviderListener> listeners = null;\r
+ protected IThreadWorkQueue thread;\r
+ protected TaskRepository tasks;\r
+ protected LoadJob loadJob;\r
+\r
+ public ResourceLabelProvider(Display display) {\r
+ this.thread = SWTThread.getThreadAccess(display);\r
+ this.tasks = new TaskRepository(display);\r
+ this.loadJob = new LoadJob("Resource Labeler", tasks, this);\r
+ }\r
+\r
+ public void fireChangedEvent(LabelProviderChangedEvent e) {\r
+ ILabelProviderListener[] listeners;\r
+ synchronized(this) {\r
+ if (this.listeners == null)\r
+ return;\r
+ listeners = this.listeners.toArray(new ILabelProviderListener[0]);\r
+ }\r
+ for (ILabelProviderListener l : listeners)\r
+ l.labelProviderChanged(e);\r
+ }\r
+\r
+ public void asyncFireChangedEvent(final LabelProviderChangedEvent e) {\r
+ thread.asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ fireChangedEvent(e);\r
+ }\r
+ });\r
+ }\r
+\r
+ public synchronized void addListener(ILabelProviderListener listener) {\r
+ if (listeners == null)\r
+ listeners = new CopyOnWriteArrayList<ILabelProviderListener>();\r
+ listeners.add(listener);\r
+ }\r
+\r
+ public void dispose() {\r
+ if (listeners != null)\r
+ listeners.clear();\r
+ loadJob.dispose();\r
+ tasks.dispose();\r
+ }\r
+\r
+ public void clear() {\r
+ tasks.clear();\r
+ }\r
+\r
+ public boolean isLabelProperty(Object element, String property) {\r
+ System.out.println("isLabelProperty(" + element + ", " + property + ")");\r
+ return false;\r
+ }\r
+\r
+ public synchronized void removeListener(ILabelProviderListener listener) {\r
+ if (listeners != null)\r
+ listeners.remove(listener);\r
+ }\r
+\r
+ @Override\r
+ public Image getImage(Object element) {\r
+ if (element == null)\r
+ return null;\r
+ Resource r = (Resource) element;\r
+ Task task = tasks.getCompleted(r);\r
+ if (task != null) {\r
+ return task.image;\r
+ }\r
+ task = tasks.queue(r);\r
+ loadJob.scheduleIfNecessary(100);\r
+ return task.image;\r
+ }\r
+\r
+ @Override\r
+ public String getText(Object element) {\r
+ if (element == null)\r
+ return null;\r
+ Resource r = (Resource) element;\r
+ Task task = tasks.getCompleted(r);\r
+ if (task != null) {\r
+ return task.label;\r
+ }\r
+ task = tasks.queue(r);\r
+ loadJob.scheduleIfNecessary(100);\r
+ return task.label;\r
+ }\r
+\r
+ private static class LoadJob extends DatabaseJob {\r
+ private TaskRepository tasks;\r
+ private ResourceLabelProvider labelProvider;\r
+ private AtomicBoolean isScheduled = new AtomicBoolean();\r
+\r
+ public LoadJob(String name, TaskRepository tasks, ResourceLabelProvider labelProvider) {\r
+ super(name);\r
+ setUser(false);\r
+ setSystem(true);\r
+ setPriority(SHORT);\r
+ setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE);\r
+ this.tasks = tasks;\r
+ this.labelProvider = labelProvider;\r
+ }\r
+\r
+ /**\r
+ * Schedules the job with {@link Job#schedule()} if necessary, i.e. if\r
+ * not already scheduled. This method avoids some unnecessary overhead\r
+ * caused by repeatedly invoking {@link Job#schedule()}.\r
+ * \r
+ * @return <code>true</code> if scheduled, <code>false</code> otherwise\r
+ */\r
+ public boolean scheduleIfNecessary(long delay) {\r
+ if (isScheduled.compareAndSet(false, true)) {\r
+ schedule(delay);\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ void dispose() {\r
+ tasks = null;\r
+ labelProvider = null;\r
+ }\r
+\r
+ @Override\r
+ public boolean shouldSchedule() {\r
+ return tasks != null;\r
+ }\r
+\r
+ @Override\r
+ public boolean shouldRun() {\r
+ return tasks != null;\r
+ }\r
+\r
+ @Override\r
+ protected IStatus run(IProgressMonitor monitor) {\r
+ try {\r
+ isScheduled.set(false);\r
+\r
+ TaskRepository tr = tasks;\r
+ if (tr == null)\r
+ return Status.CANCEL_STATUS;\r
+\r
+ Task[] taskList = tr.pruneTasks();\r
+ return runTasks(tr, taskList);\r
+ } finally {\r
+ monitor.done();\r
+ }\r
+ }\r
+\r
+ private IStatus runTasks(final TaskRepository tr, final Task[] taskList) {\r
+ Session session = Simantics.peekSession();\r
+ if (session == null)\r
+ return Status.CANCEL_STATUS;\r
+ final ResourceManager rm = tr.resourceManager;\r
+ if (rm == null)\r
+ return Status.CANCEL_STATUS;\r
+\r
+ final MultiStatus result = new MultiStatus(Activator.PLUGIN_ID, 0, "Problems encountered while invoking resource label provider:", null);\r
+ try {\r
+ Simantics.getSession().syncRequest(new ReadRequest() {\r
+ @Override\r
+ public void run(ReadGraph graph) throws DatabaseException {\r
+ for (Task task : taskList) {\r
+ try {\r
+ if (tr.isDisposed())\r
+ return;\r
+\r
+ runTask(rm, graph, task);\r
+ tasks.complete(task);\r
+ } catch (DatabaseException e) {\r
+ result.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
+ }\r
+ }\r
+ }\r
+ });\r
+ return result;\r
+ } catch (DatabaseException e) {\r
+ return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Resource label provider failed unexpectedly.", e);\r
+ } finally {\r
+ labelProvider.asyncFireChangedEvent(new LabelProviderChangedEvent(labelProvider));\r
+ }\r
+ }\r
+\r
+ protected void runTask(ResourceManager rm, ReadGraph graph, Task task) throws DatabaseException {\r
+ task.label = getText(graph, task.resource);\r
+ task.image = getImage(rm, graph, task.resource);\r
+ }\r
+\r
+ protected String getText(ReadGraph graph, Resource resource) {\r
+ try {\r
+ return graph.adapt(resource, String.class);\r
+ } catch (DatabaseException e) {\r
+ try {\r
+ return NameUtils.getSafeName(graph, resource);\r
+ } catch (DatabaseException e2) {\r
+ return "";\r
+ }\r
+ }\r
+ }\r
+\r
+ public Image getImage(ResourceManager rm, ReadGraph graph, Resource resource) throws DatabaseException {\r
+ ImageDescriptor i = getImageDescriptor(graph, resource);\r
+ return i == null ? null : (Image) rm.get(i);\r
+ }\r
+\r
+ public ImageDescriptor getImageDescriptor(ReadGraph graph, Resource resource, ImageDescriptor... decorations)\r
+ throws DatabaseException {\r
+ ImageDescriptor baseImage = ImageUtil.adaptImageDescriptor(graph, resource);\r
+ if (baseImage == null)\r
+ return null;\r
+ if (decorations == null || decorations.length == 0)\r
+ return baseImage;\r
+ List<ImageDescriptor> images = new ArrayList<ImageDescriptor>(1+decorations.length);\r
+ images.add(baseImage);\r
+ for (ImageDescriptor image : decorations)\r
+ images.add(image);\r
+ return CompositionImageDescriptor.compose(images);\r
+ }\r
+\r
+ }\r
+\r
+ private static class TaskRepository {\r
+ Map<Resource, Task> tasks = new THashMap<Resource, Task>();\r
+ Map<Resource, Task> completed = new THashMap<Resource, Task>();\r
+\r
+ ResourceManager resourceManager;\r
+\r
+ public TaskRepository(Device device) {\r
+ this.resourceManager = new DeviceResourceManager(device);\r
+ }\r
+\r
+ public boolean isDisposed() {\r
+ return resourceManager == null;\r
+ }\r
+\r
+ public void dispose() {\r
+ if (resourceManager != null) {\r
+ resourceManager.dispose();\r
+ resourceManager = null;\r
+ }\r
+ }\r
+\r
+ public Task queue(Task task) {\r
+ synchronized (tasks) {\r
+ tasks.put(task.resource, task);\r
+ }\r
+ return task;\r
+ }\r
+\r
+ public Task queue(Resource resource) {\r
+ return queue(new Task(resource));\r
+ }\r
+\r
+ public void clear() {\r
+ pruneTasks();\r
+ pruneCompleted();\r
+ }\r
+\r
+ public Task[] pruneTasks() {\r
+ synchronized (tasks) {\r
+ Task[] result = tasks.values().toArray(Task.NONE);\r
+ tasks.clear();\r
+ return result;\r
+ }\r
+ }\r
+\r
+ public void pruneCompleted() {\r
+ synchronized (completed) {\r
+ completed.clear();\r
+ }\r
+ }\r
+\r
+ public void complete(Task task) {\r
+ synchronized (completed) {\r
+ completed.put(task.resource, task);\r
+ }\r
+ }\r
+\r
+ public Task getCompleted(Resource r) {\r
+ synchronized (completed) {\r
+ return completed.get(r);\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ private static class Task {\r
+ public static final Task[] NONE = {};\r
+\r
+ public final Resource resource;\r
+ public String label = "";\r
+ public Image image;\r
+\r
+ public Task(Resource resource) {\r
+ this.resource = resource;\r
+ }\r
+ }\r
+\r
+}\r