]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.ui/src/org/simantics/ui/workbench/dialogs/ResourceLabelProvider.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.ui / src / org / simantics / ui / workbench / dialogs / ResourceLabelProvider.java
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
8  *\r
9  * Contributors:\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
14 \r
15 import gnu.trove.map.hash.THashMap;\r
16 \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
22 \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
52 \r
53 /**\r
54  * ResourceLabelProvider is a label provider for Simantics database\r
55  * {@link Resource}s.\r
56  * \r
57  * <p>\r
58  * This label provider utilizes {@link ImageDescriptorProvider} adapters for\r
59  * determining the icon to use.\r
60  * \r
61  * @author Toni Kalajainen\r
62  * @author Tuukka Lehtonen\r
63  * \r
64  * @see Resource\r
65  * @see ILabelProvider\r
66  * @see ILabelProviderListener\r
67  */\r
68 public class ResourceLabelProvider implements ILabelProvider {\r
69 \r
70     protected List<ILabelProviderListener> listeners = null;\r
71     protected IThreadWorkQueue thread;\r
72     protected TaskRepository tasks;\r
73     protected LoadJob loadJob;\r
74 \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
79     }\r
80 \r
81     public void fireChangedEvent(LabelProviderChangedEvent e) {\r
82         ILabelProviderListener[] listeners;\r
83         synchronized(this) {\r
84             if (this.listeners == null)\r
85                 return;\r
86             listeners = this.listeners.toArray(new ILabelProviderListener[0]);\r
87         }\r
88         for (ILabelProviderListener l : listeners)\r
89             l.labelProviderChanged(e);\r
90     }\r
91 \r
92     public void asyncFireChangedEvent(final LabelProviderChangedEvent e) {\r
93         thread.asyncExec(new Runnable() {\r
94             @Override\r
95             public void run() {\r
96                 fireChangedEvent(e);\r
97             }\r
98         });\r
99     }\r
100 \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
105     }\r
106 \r
107     public void dispose() {\r
108         if (listeners != null)\r
109             listeners.clear();\r
110         loadJob.dispose();\r
111         tasks.dispose();\r
112     }\r
113 \r
114     public void clear() {\r
115         tasks.clear();\r
116     }\r
117 \r
118     public boolean isLabelProperty(Object element, String property) {\r
119         System.out.println("isLabelProperty(" + element + ", " + property + ")");\r
120         return false;\r
121     }\r
122 \r
123     public synchronized void removeListener(ILabelProviderListener listener) {\r
124         if (listeners != null)\r
125             listeners.remove(listener);\r
126     }\r
127 \r
128     @Override\r
129     public Image getImage(Object element) {\r
130         if (element == null)\r
131             return null;\r
132         Resource r = (Resource) element;\r
133         Task task = tasks.getCompleted(r);\r
134         if (task != null) {\r
135             return task.image;\r
136         }\r
137         task = tasks.queue(r);\r
138         loadJob.scheduleIfNecessary(100);\r
139         return task.image;\r
140     }\r
141 \r
142     @Override\r
143     public String getText(Object element) {\r
144         if (element == null)\r
145             return null;\r
146         Resource r = (Resource) element;\r
147         Task task = tasks.getCompleted(r);\r
148         if (task != null) {\r
149             return task.label;\r
150         }\r
151         task = tasks.queue(r);\r
152         loadJob.scheduleIfNecessary(100);\r
153         return task.label;\r
154     }\r
155 \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
160 \r
161         public LoadJob(String name, TaskRepository tasks, ResourceLabelProvider labelProvider) {\r
162             super(name);\r
163             setUser(false);\r
164             setSystem(true);\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
169         }\r
170 \r
171         /**\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
175          * \r
176          * @return <code>true</code> if scheduled, <code>false</code> otherwise\r
177          */\r
178         public boolean scheduleIfNecessary(long delay) {\r
179             if (isScheduled.compareAndSet(false, true)) {\r
180                 schedule(delay);\r
181                 return true;\r
182             }\r
183             return false;\r
184         }\r
185 \r
186         void dispose() {\r
187             tasks = null;\r
188             labelProvider = null;\r
189         }\r
190 \r
191         @Override\r
192         public boolean shouldSchedule() {\r
193             return tasks != null;\r
194         }\r
195 \r
196         @Override\r
197         public boolean shouldRun() {\r
198             return tasks != null;\r
199         }\r
200 \r
201         @Override\r
202         protected IStatus run(IProgressMonitor monitor) {\r
203             try {\r
204                 isScheduled.set(false);\r
205 \r
206                 TaskRepository tr = tasks;\r
207                 if (tr == null)\r
208                     return Status.CANCEL_STATUS;\r
209 \r
210                 Task[] taskList = tr.pruneTasks();\r
211                 return runTasks(tr, taskList);\r
212             } finally {\r
213                 monitor.done();\r
214             }\r
215         }\r
216 \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
222             if (rm == null)\r
223                 return Status.CANCEL_STATUS;\r
224 \r
225             final MultiStatus result = new MultiStatus(Activator.PLUGIN_ID, 0, "Problems encountered while invoking resource label provider:", null);\r
226             try {\r
227                 Simantics.getSession().syncRequest(new ReadRequest() {\r
228                     @Override\r
229                     public void run(ReadGraph graph) throws DatabaseException {\r
230                         for (Task task : taskList) {\r
231                             try {\r
232                                 if (tr.isDisposed())\r
233                                     return;\r
234 \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
239                             }\r
240                         }\r
241                     }\r
242                 });\r
243                 return result;\r
244             } catch (DatabaseException e) {\r
245                 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Resource label provider failed unexpectedly.", e);\r
246             } finally {\r
247                 labelProvider.asyncFireChangedEvent(new LabelProviderChangedEvent(labelProvider));\r
248             }\r
249         }\r
250 \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
254         }\r
255 \r
256         protected String getText(ReadGraph graph, Resource resource) {\r
257             try {\r
258                 return graph.adapt(resource, String.class);\r
259             } catch (DatabaseException e) {\r
260                 try {\r
261                     return NameUtils.getSafeName(graph, resource);\r
262                 } catch (DatabaseException e2) {\r
263                     return "";\r
264                 }\r
265             }\r
266         }\r
267 \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
271         }\r
272 \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
277                 return null;\r
278             if (decorations == null || decorations.length == 0)\r
279                 return baseImage;\r
280             List<ImageDescriptor> images = new ArrayList<ImageDescriptor>(1+decorations.length);\r
281             images.add(baseImage);\r
282             for (ImageDescriptor image : decorations)\r
283                 images.add(image);\r
284             return CompositionImageDescriptor.compose(images);\r
285         }\r
286 \r
287     }\r
288 \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
292 \r
293         ResourceManager resourceManager;\r
294 \r
295         public TaskRepository(Device device) {\r
296             this.resourceManager = new DeviceResourceManager(device);\r
297         }\r
298 \r
299         public boolean isDisposed() {\r
300             return resourceManager == null;\r
301         }\r
302 \r
303         public void dispose() {\r
304             if (resourceManager != null) {\r
305                 resourceManager.dispose();\r
306                 resourceManager = null;\r
307             }\r
308         }\r
309 \r
310         public Task queue(Task task) {\r
311             synchronized (tasks) {\r
312                 tasks.put(task.resource, task);\r
313             }\r
314             return task;\r
315         }\r
316 \r
317         public Task queue(Resource resource) {\r
318             return queue(new Task(resource));\r
319         }\r
320 \r
321         public void clear() {\r
322             pruneTasks();\r
323             pruneCompleted();\r
324         }\r
325 \r
326         public Task[] pruneTasks() {\r
327             synchronized (tasks) {\r
328                 Task[] result = tasks.values().toArray(Task.NONE);\r
329                 tasks.clear();\r
330                 return result;\r
331             }\r
332         }\r
333 \r
334         public void pruneCompleted() {\r
335             synchronized (completed) {\r
336                 completed.clear();\r
337             }\r
338         }\r
339 \r
340         public void complete(Task task) {\r
341             synchronized (completed) {\r
342                  completed.put(task.resource, task);\r
343             }\r
344         }\r
345 \r
346         public Task getCompleted(Resource r) {\r
347             synchronized (completed) {\r
348                 return completed.get(r);\r
349             }\r
350         }\r
351 \r
352     }\r
353 \r
354     private static class Task {\r
355         public static final Task[] NONE = {};\r
356 \r
357         public final Resource resource;\r
358         public String label = "";\r
359         public Image image;\r
360 \r
361         public Task(Resource resource) {\r
362             this.resource = resource;\r
363         }\r
364     }\r
365 \r
366 }\r