com.nokia.carbide.cpp.internal.project.ui.images.providers.DelayedImageLoadingLabelProviderBase.java Source code

Java tutorial

Introduction

Here is the source code for com.nokia.carbide.cpp.internal.project.ui.images.providers.DelayedImageLoadingLabelProviderBase.java

Source

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/
package com.nokia.carbide.cpp.internal.project.ui.images.providers;

import com.nokia.carbide.cpp.internal.project.ui.ProjectUIPlugin;
import com.nokia.carbide.cpp.internal.project.ui.editors.images.Messages;
import com.nokia.carbide.cpp.ui.images.IImageModel;
import com.nokia.cpp.internal.api.utils.core.*;
import com.nokia.cpp.internal.api.utils.ui.IToolTipLabelProvider;

import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;

import java.io.File;
import java.util.*;

/**
 * This class is a label provider that manages a collection of images that it loads one at a time
 * in a background thread or job, sending out an event when images are ready.
 * <p>
 * This provider must be disposed when finished, or else threads remain running.
 *
 */
public abstract class DelayedImageLoadingLabelProviderBase extends LabelProvider implements IToolTipLabelProvider {
    private static final String CURSOR_ICON = "icons/busycursor.png"; //$NON-NLS-1$
    private static final String BROKEN_ICON = "icons/brokenimage.png"; //$NON-NLS-1$
    private List<Pair<IImageModel, Point>> pendingImages;
    private Object pendingImageMutex;

    private Thread loaderThread;
    private Job loaderJob;

    private Image stubImage;

    private Set<IImageModel> deadImages;
    private Map<Pair<IImageModel, Point>, Image> loadedImages;
    private Image brokenImage;
    private Map<IImageModel, String> errorMap;

    public DelayedImageLoadingLabelProviderBase() {
        this.pendingImageMutex = new Object();
        this.loaderThread = null;
        this.loadedImages = new HashMap<Pair<IImageModel, Point>, Image>();
        this.errorMap = new HashMap<IImageModel, String>();

        // get the image used for files while being loaded 
        // and for broken images
        if (ProjectUIPlugin.getDefault() != null) {
            this.stubImage = ProjectUIPlugin.getImageDescriptor(CURSOR_ICON).createImage();
            this.brokenImage = ProjectUIPlugin.getImageDescriptor(BROKEN_ICON).createImage();
        } else {
            File cursorImageFile = new File(CURSOR_ICON);
            File brokenImageFile = new File(BROKEN_ICON);
            if (cursorImageFile.exists())
                this.stubImage = new Image(Display.getDefault(), cursorImageFile.getAbsolutePath());
            if (brokenImageFile.exists())
                this.brokenImage = new Image(Display.getDefault(), brokenImageFile.getAbsolutePath());
        }

        if (this.stubImage == null)
            this.stubImage = new Image(Display.getDefault(), 1, 1);
        if (this.brokenImage == null)
            this.brokenImage = new Image(Display.getDefault(), 1, 1);

        // images known not to exist (so stop trying!!!)
        this.deadImages = new HashSet<IImageModel>();
    }

    /**
     * Override to convert the path from an element (IImageSourceReference) to an absolute path
     * @param elementPath
     * @return
     */
    protected IPath resolvePath(IPath elementPath) {
        return elementPath;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.LabelProvider#dispose()
     */
    @Override
    public void dispose() {
        interrupt();
        flush();
        stubImage.dispose();
        brokenImage.dispose();
        super.dispose();
    }

    /**
     * Flush any cached images
     */
    public void flush() {
        synchronized (pendingImageMutex) {
            for (Image image : loadedImages.values()) {
                image.dispose();
            }
            loadedImages.clear();
        }
    }

    protected synchronized Image findOrLoadImage(IImageModel model, Point size) {
        if (model == null || deadImages.contains(model)) {
            return brokenImage;
        }

        Pair<IImageModel, Point> key = new Pair<IImageModel, Point>(model, size);
        Image image;
        synchronized (pendingImageMutex) {
            image = loadedImages.get(key);
        }

        if (image != null) {
            return image;
        }

        loadImageInBackground(key);
        return stubImage;
    }

    /** Get the hourglass image */
    protected Image getStubImage() {
        return stubImage;
    }

    /**
     * @param path full path to image
     * @param object
     */
    private void loadImageInBackground(final Pair<IImageModel, Point> key) {
        if (Platform.isRunning()) {
            if (loaderJob == null || loaderJob.getResult() != null) {
                loaderJob = new Job(
                        Messages.getString("DelayedImageLoadingLabelProviderBase.ImageLoadingJobLabel")) { //$NON-NLS-1$

                    @Override
                    protected IStatus run(IProgressMonitor monitor) {
                        monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
                        try {
                            loadImagesLoop(monitor);
                        } finally {
                            monitor.done();
                        }
                        return Status.OK_STATUS;
                    }

                };
                loaderJob.setRule(null);
                loaderJob.setSystem(true);
                loaderJob.setUser(false);
                loaderJob.schedule();
            }
        } else {
            if (loaderThread == null) {
                Runnable runnable = new Runnable() {

                    public void run() {
                        try {
                            loadImagesLoop(null);
                        } finally {
                        }
                    }

                };
                loaderThread = new Thread(runnable);
                loaderThread.start();
            }
        }
        synchronized (pendingImageMutex) {
            if (pendingImages == null)
                pendingImages = new LinkedList<Pair<IImageModel, Point>>();

            if (!pendingImages.contains(key)) {
                pendingImages.add(0, key);
                pendingImageMutex.notify();
            }
        }
    }

    /**
     * Load images from the pending images list.
     * @param monitor
     */
    protected void loadImagesLoop(IProgressMonitor monitor) {
        int worked = 0;
        final List<Object> updated = new ArrayList<Object>();
        while (!Thread.interrupted()) {
            Pair<IImageModel, Point> pair = null;
            synchronized (pendingImageMutex) {
                // pendingImages == null is a hint that we should quit
                while (pendingImages != null && pendingImages.size() == 0) {
                    try {
                        pendingImageMutex.wait();
                    } catch (InterruptedException e) {
                        break;
                    }
                }
                if (pendingImages == null || pendingImages.size() == 0)
                    break;

                pair = pendingImages.remove(0);
            }

            if (pair == null)
                continue;

            final IImageModel model = pair.first;

            // now, load the image
            Image image = null;
            try {
                //System.out.println(this+" Loading " + pair.second.first); //$NON-NLS-1$
                image = model.getImageDescriptor(pair.second).createImage();
                synchronized (pendingImageMutex) {
                    loadedImages.put(pair, image);
                }
            } catch (CoreException e) {
                errorMap.put(model, e.getLocalizedMessage());
            } catch (Throwable t) {
                // to avoid crashing this thread
            }

            if (image == null) {
                // don't try to load this again
                deadImages.add(model);
            } else {
                worked++;
            }

            updated.add(model);
            int remaining = 0;
            synchronized (pendingImageMutex) {
                if (pendingImages != null)
                    remaining = pendingImages.size();
            }
            if (!Thread.interrupted() && (worked >= 10 || remaining == 0)) {
                // do this in UI thread
                Display.getDefault().asyncExec(new Runnable() {

                    public void run() {
                        fireLabelProviderChanged(new LabelProviderChangedEvent(
                                DelayedImageLoadingLabelProviderBase.this, updated.toArray()));
                        updated.clear();
                    }
                });
                worked = 0;
            }

            if (monitor != null)
                monitor.worked(1);
        }

        // final notification
        if (!Thread.interrupted() && pendingImages != null) {
            try {
                Display.getDefault().asyncExec(new Runnable() {

                    public void run() {
                        fireLabelProviderChanged(new LabelProviderChangedEvent(
                                DelayedImageLoadingLabelProviderBase.this, updated.toArray()));
                    }
                });
            } catch (SWTError e) {

            }
        }

    }

    /**
     * Interrupt the provider early (before being disposed)
     */
    public void interrupt() {
        synchronized (pendingImageMutex) {
            if (pendingImages != null)
                pendingImages.clear();
            pendingImages = null;
            pendingImageMutex.notify();

        }
        if (loaderThread != null) {
            loaderThread.interrupt();
            try {
                loaderThread.join();
            } catch (InterruptedException e) {
            }
            loaderThread = null;
        }
        if (loaderJob != null) {
            loaderJob.cancel();
            try {
                loaderJob.join();
            } catch (InterruptedException e) {
            }
            loaderJob = null;
        }
    }

    public String getToolTipText(Object element) {
        return errorMap.get(element);
    }
}