1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2013 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 (#4255)
\r
12 *******************************************************************************/
\r
13 package org.simantics.ui.workbench.dialogs;
\r
15 import gnu.trove.map.hash.THashMap;
\r
17 import java.util.ArrayList;
\r
18 import java.util.List;
\r
19 import java.util.Map;
\r
20 import java.util.concurrent.CopyOnWriteArrayList;
\r
21 import java.util.concurrent.atomic.AtomicBoolean;
\r
23 import org.eclipse.core.runtime.IProgressMonitor;
\r
24 import org.eclipse.core.runtime.IStatus;
\r
25 import org.eclipse.core.runtime.MultiStatus;
\r
26 import org.eclipse.core.runtime.Status;
\r
27 import org.eclipse.core.runtime.jobs.Job;
\r
28 import org.eclipse.jface.resource.DeviceResourceManager;
\r
29 import org.eclipse.jface.resource.ImageDescriptor;
\r
30 import org.eclipse.jface.resource.ResourceManager;
\r
31 import org.eclipse.jface.viewers.ILabelProvider;
\r
32 import org.eclipse.jface.viewers.ILabelProviderListener;
\r
33 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
\r
34 import org.eclipse.swt.graphics.Device;
\r
35 import org.eclipse.swt.graphics.Image;
\r
36 import org.eclipse.swt.widgets.Display;
\r
37 import org.eclipse.ui.progress.IProgressConstants;
\r
38 import org.simantics.DatabaseJob;
\r
39 import org.simantics.Simantics;
\r
40 import org.simantics.db.ReadGraph;
\r
41 import org.simantics.db.Resource;
\r
42 import org.simantics.db.Session;
\r
43 import org.simantics.db.common.request.ReadRequest;
\r
44 import org.simantics.db.common.utils.NameUtils;
\r
45 import org.simantics.db.exception.DatabaseException;
\r
46 import org.simantics.ui.icons.ImageDescriptorProvider;
\r
47 import org.simantics.ui.icons.ImageUtil;
\r
48 import org.simantics.ui.internal.Activator;
\r
49 import org.simantics.utils.threads.IThreadWorkQueue;
\r
50 import org.simantics.utils.threads.SWTThread;
\r
51 import org.simantics.utils.ui.gfx.CompositionImageDescriptor;
\r
54 * ResourceLabelProvider is a label provider for Simantics database
\r
55 * {@link Resource}s.
\r
58 * This label provider utilizes {@link ImageDescriptorProvider} adapters for
\r
59 * determining the icon to use.
\r
61 * @author Toni Kalajainen
\r
62 * @author Tuukka Lehtonen
\r
65 * @see ILabelProvider
\r
66 * @see ILabelProviderListener
\r
68 public class ResourceLabelProvider implements ILabelProvider {
\r
70 protected List<ILabelProviderListener> listeners = null;
\r
71 protected IThreadWorkQueue thread;
\r
72 protected TaskRepository tasks;
\r
73 protected LoadJob loadJob;
\r
75 public ResourceLabelProvider(Display display) {
\r
76 this.thread = SWTThread.getThreadAccess(display);
\r
77 this.tasks = new TaskRepository(display);
\r
78 this.loadJob = new LoadJob("Resource Labeler", tasks, this);
\r
81 public void fireChangedEvent(LabelProviderChangedEvent e) {
\r
82 ILabelProviderListener[] listeners;
\r
83 synchronized(this) {
\r
84 if (this.listeners == null)
\r
86 listeners = this.listeners.toArray(new ILabelProviderListener[0]);
\r
88 for (ILabelProviderListener l : listeners)
\r
89 l.labelProviderChanged(e);
\r
92 public void asyncFireChangedEvent(final LabelProviderChangedEvent e) {
\r
93 thread.asyncExec(new Runnable() {
\r
96 fireChangedEvent(e);
\r
101 public synchronized void addListener(ILabelProviderListener listener) {
\r
102 if (listeners == null)
\r
103 listeners = new CopyOnWriteArrayList<ILabelProviderListener>();
\r
104 listeners.add(listener);
\r
107 public void dispose() {
\r
108 if (listeners != null)
\r
114 public void clear() {
\r
118 public boolean isLabelProperty(Object element, String property) {
\r
119 System.out.println("isLabelProperty(" + element + ", " + property + ")");
\r
123 public synchronized void removeListener(ILabelProviderListener listener) {
\r
124 if (listeners != null)
\r
125 listeners.remove(listener);
\r
129 public Image getImage(Object element) {
\r
130 if (element == null)
\r
132 Resource r = (Resource) element;
\r
133 Task task = tasks.getCompleted(r);
\r
134 if (task != null) {
\r
137 task = tasks.queue(r);
\r
138 loadJob.scheduleIfNecessary(100);
\r
143 public String getText(Object element) {
\r
144 if (element == null)
\r
146 Resource r = (Resource) element;
\r
147 Task task = tasks.getCompleted(r);
\r
148 if (task != null) {
\r
151 task = tasks.queue(r);
\r
152 loadJob.scheduleIfNecessary(100);
\r
156 private static class LoadJob extends DatabaseJob {
\r
157 private TaskRepository tasks;
\r
158 private ResourceLabelProvider labelProvider;
\r
159 private AtomicBoolean isScheduled = new AtomicBoolean();
\r
161 public LoadJob(String name, TaskRepository tasks, ResourceLabelProvider labelProvider) {
\r
165 setPriority(SHORT);
\r
166 setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE);
\r
167 this.tasks = tasks;
\r
168 this.labelProvider = labelProvider;
\r
172 * Schedules the job with {@link Job#schedule()} if necessary, i.e. if
\r
173 * not already scheduled. This method avoids some unnecessary overhead
\r
174 * caused by repeatedly invoking {@link Job#schedule()}.
\r
176 * @return <code>true</code> if scheduled, <code>false</code> otherwise
\r
178 public boolean scheduleIfNecessary(long delay) {
\r
179 if (isScheduled.compareAndSet(false, true)) {
\r
188 labelProvider = null;
\r
192 public boolean shouldSchedule() {
\r
193 return tasks != null;
\r
197 public boolean shouldRun() {
\r
198 return tasks != null;
\r
202 protected IStatus run(IProgressMonitor monitor) {
\r
204 isScheduled.set(false);
\r
206 TaskRepository tr = tasks;
\r
208 return Status.CANCEL_STATUS;
\r
210 Task[] taskList = tr.pruneTasks();
\r
211 return runTasks(tr, taskList);
\r
217 private IStatus runTasks(final TaskRepository tr, final Task[] taskList) {
\r
218 Session session = Simantics.peekSession();
\r
219 if (session == null)
\r
220 return Status.CANCEL_STATUS;
\r
221 final ResourceManager rm = tr.resourceManager;
\r
223 return Status.CANCEL_STATUS;
\r
225 final MultiStatus result = new MultiStatus(Activator.PLUGIN_ID, 0, "Problems encountered while invoking resource label provider:", null);
\r
227 Simantics.getSession().syncRequest(new ReadRequest() {
\r
229 public void run(ReadGraph graph) throws DatabaseException {
\r
230 for (Task task : taskList) {
\r
232 if (tr.isDisposed())
\r
235 runTask(rm, graph, task);
\r
236 tasks.complete(task);
\r
237 } catch (DatabaseException e) {
\r
238 result.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
\r
244 } catch (DatabaseException e) {
\r
245 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Resource label provider failed unexpectedly.", e);
\r
247 labelProvider.asyncFireChangedEvent(new LabelProviderChangedEvent(labelProvider));
\r
251 protected void runTask(ResourceManager rm, ReadGraph graph, Task task) throws DatabaseException {
\r
252 task.label = getText(graph, task.resource);
\r
253 task.image = getImage(rm, graph, task.resource);
\r
256 protected String getText(ReadGraph graph, Resource resource) {
\r
258 return graph.adapt(resource, String.class);
\r
259 } catch (DatabaseException e) {
\r
261 return NameUtils.getSafeName(graph, resource);
\r
262 } catch (DatabaseException e2) {
\r
268 public Image getImage(ResourceManager rm, ReadGraph graph, Resource resource) throws DatabaseException {
\r
269 ImageDescriptor i = getImageDescriptor(graph, resource);
\r
270 return i == null ? null : (Image) rm.get(i);
\r
273 public ImageDescriptor getImageDescriptor(ReadGraph graph, Resource resource, ImageDescriptor... decorations)
\r
274 throws DatabaseException {
\r
275 ImageDescriptor baseImage = ImageUtil.adaptImageDescriptor(graph, resource);
\r
276 if (baseImage == null)
\r
278 if (decorations == null || decorations.length == 0)
\r
280 List<ImageDescriptor> images = new ArrayList<ImageDescriptor>(1+decorations.length);
\r
281 images.add(baseImage);
\r
282 for (ImageDescriptor image : decorations)
\r
284 return CompositionImageDescriptor.compose(images);
\r
289 private static class TaskRepository {
\r
290 Map<Resource, Task> tasks = new THashMap<Resource, Task>();
\r
291 Map<Resource, Task> completed = new THashMap<Resource, Task>();
\r
293 ResourceManager resourceManager;
\r
295 public TaskRepository(Device device) {
\r
296 this.resourceManager = new DeviceResourceManager(device);
\r
299 public boolean isDisposed() {
\r
300 return resourceManager == null;
\r
303 public void dispose() {
\r
304 if (resourceManager != null) {
\r
305 resourceManager.dispose();
\r
306 resourceManager = null;
\r
310 public Task queue(Task task) {
\r
311 synchronized (tasks) {
\r
312 tasks.put(task.resource, task);
\r
317 public Task queue(Resource resource) {
\r
318 return queue(new Task(resource));
\r
321 public void clear() {
\r
326 public Task[] pruneTasks() {
\r
327 synchronized (tasks) {
\r
328 Task[] result = tasks.values().toArray(Task.NONE);
\r
334 public void pruneCompleted() {
\r
335 synchronized (completed) {
\r
340 public void complete(Task task) {
\r
341 synchronized (completed) {
\r
342 completed.put(task.resource, task);
\r
346 public Task getCompleted(Resource r) {
\r
347 synchronized (completed) {
\r
348 return completed.get(r);
\r
354 private static class Task {
\r
355 public static final Task[] NONE = {};
\r
357 public final Resource resource;
\r
358 public String label = "";
\r
359 public Image image;
\r
361 public Task(Resource resource) {
\r
362 this.resource = resource;
\r