www.pudn.com > GMapViewer-src.zip > MapTileManager.java



package org.sreid.j2me.gmapviewer;

import java.util.*;
import java.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import org.sreid.j2me.util.*;


class MapTileManager {

	private final GMapViewer app;

	private final MapTileCache cache;

	MapTileManager(GMapViewer app) {
		this.app = app;
		cache = app.cache;
	}


	/**
	 * Requests a map tile. Returns from cache if possible. Begins loading if it's not already loaded.
	 */
	MapTile getMapTile(final XYZ xyz) {
		MapTile mapTile1;
		synchronized (cache) {
			// Load cached, if possible.
			mapTile1 = (MapTile)cache.get(xyz);
			// If we found no tile, construct a new one.
			if (mapTile1 == null) {
				mapTile1 = new MapTile(xyz);
				cache.put(mapTile1);
			}
		}
		final MapTile mapTile = mapTile1; // final, so runnable below can use it

		// Background download/decode.
		final int requestID = app.canvas.requestID; // allows us to discard old requests
		if (shouldDownloadTile(mapTile)) {
			app.async1.enqueueJob(new Runnable() { public void run() {
				if (app.canvas.requestID != requestID) return;
				if (shouldDownloadTile(mapTile)) {
					download(mapTile);
				}
			}});
		}
		else if (mapTile.image == null) {
			app.async2.enqueueJob(new Runnable() { public void run() {
				if (app.canvas.requestID != requestID) return;
				if (mapTile.bytes == null) cache.loadFromRMS(mapTile);
				decode(mapTile);
			}});
		}
		return mapTile;
	}

	private boolean shouldDownloadTile(MapTile mt) {
		return mt.bytes == null && mt.rmsID == -1 && !mt.downloading && app.online;
	}

	private void download(final MapTile mapTile) {
		synchronized (mapTile) {
			if (!shouldDownloadTile(mapTile)) {
				return;
			}
			// Claim the task 
			mapTile.downloading = true;
		}
		app.canvas.repaint();
		try {
			byte[] buffer = (byte[])app.sharedBuffers.claimResourceIgnoreInterrupt();
			try {
				String params =
				 "&tx=" + mapTile.xyz.x +
				 "&ty=" + mapTile.xyz.y +
				 "&tz=" + mapTile.xyz.z ;
				int count = app.getDataFromServer("maptile", params, buffer, "MAPTILE");
				byte[] response = new byte[count];
				System.arraycopy(buffer, 0, response, 0, count);
				mapTile.bytes = response;
			}
			finally {
				app.sharedBuffers.releaseResource(buffer);
			}
			cache.put(mapTile); // this is done after buffer released, as cache.put may need it
		}
		catch (Exception e) {
			app.online = false;
			app.exception("An error occured while downloading a map tile. Switching to offline mode.", e);
		}
		finally {
			synchronized (mapTile) {
				mapTile.downloading = false;
			}
			app.canvas.repaint();
		}
	}

	private void decode(final MapTile mapTile) {
		synchronized (mapTile) {
			if (mapTile.image != null || mapTile.decoding || mapTile.bytes == null) {
				// We don't need to / can't do anything.
				return;
			}
			// Claim the task for ourselves.
			mapTile.decoding = true;
		}
		try {
			app.canvas.repaint(); // inform that decoding begins
			mapTile.image = Image.createImage(mapTile.bytes, 0, mapTile.bytes.length);
			cache.put(mapTile);
		}
		catch (Exception e) {
			app.exception("An error occured while decoding a map tile image. This may be caused by memory fragmentation, which can only be fixed by restarting the program.", e);
			cache.invalidate(mapTile); // force re-download of the tile (in case it really is a corrupt tile)
		}
		finally {
			synchronized (mapTile) {
				mapTile.decoding = false;
			}
			app.canvas.repaint(); // inform that decoding complete
		}
	}

}