]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
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 (file)
index 0000000..704138d
--- /dev/null
@@ -0,0 +1,366 @@
+/*******************************************************************************\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