]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.maps/src/org/simantics/maps/tile/TileCache.java
Ensure ITileProviders return BufferedImages with compatible ColorModel
[simantics/district.git] / org.simantics.district.maps / src / org / simantics / maps / tile / TileCache.java
1 /*******************************************************************************
2  * Copyright (c) 2012 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.maps.tile;
13
14 import java.awt.Image;
15 import java.lang.ref.SoftReference;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20
21 /**
22  * @author Tuukka Lehtonen
23  */
24 public class TileCache implements ITileListener {
25
26     public static class LevelCache {
27         /**
28          * Allow collection of tile images when under memory pressure.
29          */
30         private Map<TileKey, SoftReference<Image>> cache = new HashMap<TileKey, SoftReference<Image>>();
31
32         public Image get(TileKey key) {
33             SoftReference<Image> ref = cache.get(key);
34             if (ref == null)
35                 return null;
36             return ref.get();
37         }
38
39         public void put(TileKey key, Image value) {
40             cache.put(key, new SoftReference<Image>(value));
41         }
42
43         public void remove(TileKey key) {
44             cache.remove(key);
45         }
46     }
47
48     private final ITileJobQueue      job;
49     private Map<Integer, LevelCache> levels = new HashMap<Integer, LevelCache>();
50     private List<ITileListener>      tileListeners = new ArrayList<ITileListener>();
51
52     public TileCache(ITileJobQueue job) {
53         this.job = job;
54     }
55
56     public void addTileListener(ITileListener l) {
57         tileListeners.add(l);
58     }
59
60     public void removeTileListener(ITileListener l) {
61         tileListeners.remove(l);
62     }
63
64     private LevelCache getLevel(int l, boolean create) {
65         synchronized (levels) {
66             LevelCache level = levels.get(l);
67             if (level == null && create) {
68                 level = new LevelCache();
69                 levels.put(l, level);
70             }
71             return level;
72         }
73     }
74
75     /**
76      * @param key
77      * @return <code>null</code> to signify that the requested tile was not
78      *         cached and a background request has been scheduled for the tile.
79      */
80     public Image get(final TileKey key) {
81         LevelCache level = getLevel(key.getLevel(), true);
82         Image image = level.get(key);
83         if (image == null) {
84                 job.addJob(key, this);
85         }
86         return image;
87     }
88
89     /**
90      * Take a peek into the cache to see if there is an image readily available
91      * for the requested tile.
92      * 
93      * @param key the key of the requested tile
94      * @return <code>null</code> if there was no image in the cache
95      */
96     public Image peek(final TileKey key) {
97         LevelCache level = getLevel(key.getLevel(), false);
98         if (level == null)
99             return null;
100         synchronized (level) {
101             return level.get(key);
102         }
103     }
104
105     /**
106      * Flush the specified tile from the cache.
107      * 
108      * @param key
109      */
110     public synchronized void flush(final TileKey key) {
111         LevelCache level = getLevel(key.getLevel(), false);
112         if (level != null) {
113             synchronized (level) {
114                 level.remove(key);
115             }
116         }
117     }
118
119     /**
120      * Removes queries from the job queue that do not pass the specified filter.
121      */
122     public void filterJobQueue(IFilter<TileKey> filter) {
123         job.filterQueries(filter);
124     }
125
126         @Override
127         public void tileCanceled(TileKey key) {
128         for (ITileListener l : tileListeners) {
129             l.tileCanceled(key);
130         }
131         }
132
133         @Override
134         public void tileFailed(TileKey key, Throwable e) {
135         for (ITileListener l : tileListeners) {
136             l.tileFailed(key, e);
137         }
138         }
139
140         @Override
141         public void tileUpdated(TileKey key, Image image) {
142         LevelCache level = getLevel(key.getLevel(), true);
143         synchronized (level) {
144             level.put(key, image);
145         }
146         
147         for (ITileListener l : tileListeners) {
148             l.tileUpdated(key, image);
149         }
150         }
151
152 }