/*******************************************************************************
* Copyright (c) 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http\://www.eclipse.org/legal/epl-v10.html
*
* Contributors\:
* IBM Corporation - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.geomap.internal;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Display;
/**
* An async image that loads itself in the background on an image-fetcher thread.
* Once its loaded it will trigger a redraw. Sometimes redraws that
* are not really necessary can be triggered, but that is not relevant in terms of
* performance for this swt-component.
*
*/
@SuppressWarnings("serial")
class AsyncImage extends AtomicReference<ImageData> implements Runnable {
private final GeoMapHelper geoMapHelper;
private final TileRef tile;
private final String tileUrl;
private volatile long stamp;
// private final AtomicReference<ImageData> imageData = new AtomicReference<ImageData>();
private Image image = null; // might as well be thread-local
AsyncImage(GeoMapHelper geoMapHelper, TileRef tile, String tileUrl) {
this.geoMapHelper = geoMapHelper;
this.stamp = this.geoMapHelper.zoomStamp.longValue();
this.tile = tile;
this.tileUrl = tileUrl;
this.geoMapHelper.executor.execute(new FutureTask<Boolean>(this, Boolean.TRUE));
}
private Runnable removeTileFromCacheRunnable = new Runnable() {
public void run() {
AsyncImage.this.geoMapHelper.cache.remove(tile);
}
};
private Runnable tileUpdatedRunnable = new Runnable() {
public void run() {
AsyncImage.this.geoMapHelper.tileUpdated(tile);
}
};
public void run() {
if (stamp != this.geoMapHelper.zoomStamp.longValue()) {
try {
// here is a race, we just live with.
if (! this.geoMapHelper.getDisplay().isDisposed()) {
this.geoMapHelper.getDisplay().asyncExec(removeTileFromCacheRunnable);
}
} catch (SWTException e) {
// ignore
}
return;
}
try {
URLConnection con = new URL(tileUrl).openConnection();
con.setRequestProperty("User-Agent", "org.eclipse.nebula.widgets.geomap.GeoMap"); //$NON-NLS-1$ //$NON-NLS-2$
set(new ImageData(con.getInputStream()));
try {
// here is a race, we just live with.
if (! this.geoMapHelper.getDisplay().isDisposed()) {
this.geoMapHelper.getDisplay().asyncExec(tileUpdatedRunnable);
}
} catch (SWTException e) {
// ignore
}
} catch (Exception e) {
// log.log(Level.SEVERE, "failed to load imagedata from url: " + tileUrl, e);
}
}
public Image getImage(Display display) {
checkThread(display);
if (image == null && get() != null) {
image = new Image(display, get());
}
return image;
}
public void dispose() {
checkThread(geoMapHelper.getDisplay());
if (image != null) {
image.dispose();
image = null;
}
}
private void checkThread(Display display) {
// jdk 1.6 bug from checkWidget still fails here
if (display.getThread() != Thread.currentThread()) {
throw new IllegalStateException("Wrong thread to pick up the image"); //$NON-NLS-1$
}
}
}