/******************************************************************************* * 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> cache = new HashMap>(); public Image get(TileKey key) { SoftReference ref = cache.get(key); if (ref == null) return null; return ref.get(); } public void put(TileKey key, Image value) { cache.put(key, new SoftReference(value)); } public void remove(TileKey key) { cache.remove(key); } } private final ITileJobQueue job; private Map levels = new HashMap(); private List tileListeners = new ArrayList(); 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 null 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 null 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 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); } } public void clear() { synchronized (this) { for (LevelCache level : levels.values()) { level.cache.clear(); } } } }