Java tutorial
/* * 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); } }