X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.ui%2Fsrc%2Forg%2Fsimantics%2Fui%2Fworkbench%2Fdialogs%2FResourceLabelProvider.java;fp=bundles%2Forg.simantics.ui%2Fsrc%2Forg%2Fsimantics%2Fui%2Fworkbench%2Fdialogs%2FResourceLabelProvider.java;h=704138d2af6e7627eae9a0ba6d8f7c878c17be1f;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/dialogs/ResourceLabelProvider.java b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/dialogs/ResourceLabelProvider.java new file mode 100644 index 000000000..704138d2a --- /dev/null +++ b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/dialogs/ResourceLabelProvider.java @@ -0,0 +1,366 @@ +/******************************************************************************* + * 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 (#4255) + *******************************************************************************/ +package org.simantics.ui.workbench.dialogs; + +import gnu.trove.map.hash.THashMap; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.resource.DeviceResourceManager; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ResourceManager; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.LabelProviderChangedEvent; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.progress.IProgressConstants; +import org.simantics.DatabaseJob; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.ui.icons.ImageDescriptorProvider; +import org.simantics.ui.icons.ImageUtil; +import org.simantics.ui.internal.Activator; +import org.simantics.utils.threads.IThreadWorkQueue; +import org.simantics.utils.threads.SWTThread; +import org.simantics.utils.ui.gfx.CompositionImageDescriptor; + +/** + * ResourceLabelProvider is a label provider for Simantics database + * {@link Resource}s. + * + *

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