--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2012 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * 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:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.maps.tile;
+
+import java.awt.Image;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class TileCache implements ITileListener {
+
+ public static class LevelCache {
+ /**
+ * Allow collection of tile images when under memory pressure.
+ */
+ private Map<TileKey, SoftReference<Image>> cache = new HashMap<TileKey, SoftReference<Image>>();
+
+ public Image get(TileKey key) {
+ SoftReference<Image> ref = cache.get(key);
+ if (ref == null)
+ return null;
+ return ref.get();
+ }
+
+ public void put(TileKey key, Image value) {
+ cache.put(key, new SoftReference<Image>(value));
+ }
+
+ public void remove(TileKey key) {
+ cache.remove(key);
+ }
+ }
+
+ private final ITileJobQueue job;
+ private Map<Integer, LevelCache> levels = new HashMap<Integer, LevelCache>();
+ private List<ITileListener> tileListeners = new ArrayList<ITileListener>();
+
+ public TileCache(ITileJobQueue job) {
+ this.job = job;
+ }
+
+ public void addTileListener(ITileListener l) {
+ tileListeners.add(l);
+ }
+
+ public void removeTileListener(ITileListener l) {
+ tileListeners.remove(l);
+ }
+
+ private LevelCache getLevel(int l, boolean create) {
+ synchronized (levels) {
+ LevelCache level = levels.get(l);
+ if (level == null && create) {
+ level = new LevelCache();
+ levels.put(l, level);
+ }
+ return level;
+ }
+ }
+
+ /**
+ * @param key
+ * @return <code>null</code> to signify that the requested tile was not
+ * cached and a background request has been scheduled for the tile.
+ */
+ public Image get(final TileKey key) {
+ LevelCache level = getLevel(key.getLevel(), true);
+ Image image = level.get(key);
+ if (image == null) {
+ job.addJob(key, this);
+ }
+ return image;
+ }
+
+ /**
+ * Take a peek into the cache to see if there is an image readily available
+ * for the requested tile.
+ *
+ * @param key the key of the requested tile
+ * @return <code>null</code> if there was no image in the cache
+ */
+ public Image peek(final TileKey key) {
+ LevelCache level = getLevel(key.getLevel(), false);
+ if (level == null)
+ return null;
+ synchronized (level) {
+ return level.get(key);
+ }
+ }
+
+ /**
+ * Flush the specified tile from the cache.
+ *
+ * @param key
+ */
+ public synchronized void flush(final TileKey key) {
+ LevelCache level = getLevel(key.getLevel(), false);
+ if (level != null) {
+ synchronized (level) {
+ level.remove(key);
+ }
+ }
+ }
+
+ /**
+ * Removes queries from the job queue that do not pass the specified filter.
+ */
+ public void filterJobQueue(IFilter<TileKey> filter) {
+ job.filterQueries(filter);
+ }
+
+ @Override
+ public void tileCanceled(TileKey key) {
+ for (ITileListener l : tileListeners) {
+ l.tileCanceled(key);
+ }
+ }
+
+ @Override
+ public void tileFailed(TileKey key, Throwable e) {
+ for (ITileListener l : tileListeners) {
+ l.tileFailed(key, e);
+ }
+ }
+
+ @Override
+ public void tileUpdated(TileKey key, Image image) {
+ LevelCache level = getLevel(key.getLevel(), true);
+ synchronized (level) {
+ level.put(key, image);
+ }
+
+ for (ITileListener l : tileListeners) {
+ l.tileUpdated(key, image);
+ }
+ }
+
+}