source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java@ 4636

Last change on this file since 4636 was 4636, checked in by Don-vip, 12 years ago

see #7113 and #6987 - NPE after Split Way - Undo

File size: 10.9 KB
Line 
1package org.openstreetmap.josm.data.osm.visitor.paint.relations;
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.HashMap;
6import java.util.Iterator;
7import java.util.List;
8import java.util.Map;
9
10import org.openstreetmap.josm.Main;
11import org.openstreetmap.josm.data.SelectionChangedListener;
12import org.openstreetmap.josm.data.osm.DataSet;
13import org.openstreetmap.josm.data.osm.Node;
14import org.openstreetmap.josm.data.osm.OsmPrimitive;
15import org.openstreetmap.josm.data.osm.Relation;
16import org.openstreetmap.josm.data.osm.Way;
17import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
18import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
19import org.openstreetmap.josm.data.osm.event.DataSetListener;
20import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
21import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
22import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
23import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
24import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
25import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
26import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
27import org.openstreetmap.josm.data.projection.Projection;
28import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
29import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
30import org.openstreetmap.josm.gui.NavigatableComponent;
31import org.openstreetmap.josm.gui.layer.Layer;
32import org.openstreetmap.josm.gui.layer.OsmDataLayer;
33
34/*
35 * A memory cache for Multipolygon objects.
36 *
37 */
38public class MultipolygonCache implements DataSetListener, LayerChangeListener, ProjectionChangeListener, SelectionChangedListener {
39
40 private static final MultipolygonCache instance = new MultipolygonCache();
41
42 private final Map<NavigatableComponent, Map<DataSet, Map<Relation, Multipolygon>>> cache;
43
44 private final Collection<PolyData> selectedPolyData;
45
46 private MultipolygonCache() {
47 this.cache = new HashMap<NavigatableComponent, Map<DataSet, Map<Relation, Multipolygon>>>();
48 this.selectedPolyData = new ArrayList<Multipolygon.PolyData>();
49 Main.addProjectionChangeListener(this);
50 DataSet.addSelectionListener(this);
51 }
52
53 public static final MultipolygonCache getInstance() {
54 return instance;
55 }
56
57 public final Multipolygon get(NavigatableComponent nc, Relation r) {
58 return get(nc, r, false);
59 }
60
61 public final Multipolygon get(NavigatableComponent nc, Relation r, boolean forceRefresh) {
62 Multipolygon multipolygon = null;
63 if (nc != null && r != null) {
64 Map<DataSet, Map<Relation, Multipolygon>> map1 = cache.get(nc);
65 if (map1 == null) {
66 cache.put(nc, map1 = new HashMap<DataSet, Map<Relation, Multipolygon>>());
67 }
68 Map<Relation, Multipolygon> map2 = map1.get(r.getDataSet());
69 if (map2 == null) {
70 map1.put(r.getDataSet(), map2 = new HashMap<Relation, Multipolygon>());
71 }
72 multipolygon = map2.get(r);
73 if (multipolygon == null || forceRefresh) {
74 map2.put(r, multipolygon = new Multipolygon(r));
75 for (PolyData pd : multipolygon.getCombinedPolygons()) {
76 if (pd.selected) {
77 selectedPolyData.add(pd);
78 }
79 }
80 }
81 }
82 return multipolygon;
83 }
84
85 public final void clear(NavigatableComponent nc) {
86 Map<DataSet, Map<Relation, Multipolygon>> map = cache.remove(nc);
87 if (map != null) {
88 map.clear();
89 map = null;
90 }
91 }
92
93 public final void clear(DataSet ds) {
94 for (Map<DataSet, Map<Relation, Multipolygon>> map1 : cache.values()) {
95 Map<Relation, Multipolygon> map2 = map1.remove(ds);
96 if (map2 != null) {
97 map2.clear();
98 map2 = null;
99 }
100 }
101 }
102
103 public final void clear() {
104 cache.clear();
105 }
106
107 private final Collection<Map<Relation, Multipolygon>> getMapsFor(DataSet ds) {
108 List<Map<Relation, Multipolygon>> result = new ArrayList<Map<Relation, Multipolygon>>();
109 for (Map<DataSet, Map<Relation, Multipolygon>> map : cache.values()) {
110 Map<Relation, Multipolygon> map2 = map.get(ds);
111 if (map2 != null) {
112 result.add(map2);
113 }
114 }
115 return result;
116 }
117
118 private static final boolean isMultipolygon(OsmPrimitive p) {
119 return p instanceof Relation && ((Relation) p).isMultipolygon();
120 }
121
122 private final void updateMultipolygonsReferringTo(AbstractDatasetChangedEvent event) {
123 updateMultipolygonsReferringTo(event, event.getPrimitives(), event.getDataset());
124 }
125
126 private final void updateMultipolygonsReferringTo(
127 final AbstractDatasetChangedEvent event, Collection<? extends OsmPrimitive> primitives, DataSet ds) {
128 updateMultipolygonsReferringTo(event, primitives, ds, null);
129 }
130
131 private final Collection<Map<Relation, Multipolygon>> updateMultipolygonsReferringTo(
132 AbstractDatasetChangedEvent event, Collection<? extends OsmPrimitive> primitives,
133 DataSet ds, Collection<Map<Relation, Multipolygon>> initialMaps) {
134 Collection<Map<Relation, Multipolygon>> maps = initialMaps;
135 if (primitives != null) {
136 for (OsmPrimitive p : primitives) {
137 if (isMultipolygon(p)) {
138 if (maps == null) {
139 maps = getMapsFor(ds);
140 }
141 processEvent(event, (Relation) p, maps);
142
143 } else if (p instanceof Way && p.getDataSet() != null) {
144 for (OsmPrimitive ref : p.getReferrers()) {
145 if (isMultipolygon(ref)) {
146 if (maps == null) {
147 maps = getMapsFor(ds);
148 }
149 processEvent(event, (Relation) ref, maps);
150 }
151 }
152 } else if (p instanceof Node && p.getDataSet() != null) {
153 maps = updateMultipolygonsReferringTo(event, p.getReferrers(), ds, maps);
154 }
155 }
156 }
157 return maps;
158 }
159
160 private final void processEvent(AbstractDatasetChangedEvent event, Relation r, Collection<Map<Relation, Multipolygon>> maps) {
161 if (event instanceof NodeMovedEvent || event instanceof WayNodesChangedEvent) {
162 dispatchEvent(event, r, maps);
163 } else if (event instanceof PrimitivesRemovedEvent) {
164 if (event.getPrimitives().contains(r)) {
165 removeMultipolygonFrom(r, maps);
166 }
167 } else {
168 // Default (non-optimal) action: remove multipolygon from cache
169 removeMultipolygonFrom(r, maps);
170 }
171 }
172
173 private final void dispatchEvent(AbstractDatasetChangedEvent event, Relation r, Collection<Map<Relation, Multipolygon>> maps) {
174 for (Map<Relation, Multipolygon> map : maps) {
175 Multipolygon m = map.get(r);
176 if (m != null) {
177 for (PolyData pd : m.getCombinedPolygons()) {
178 if (event instanceof NodeMovedEvent) {
179 pd.nodeMoved((NodeMovedEvent) event);
180 } else if (event instanceof WayNodesChangedEvent) {
181 pd.wayNodesChanged((WayNodesChangedEvent)event);
182 }
183 }
184 }
185 }
186 }
187
188 private final void removeMultipolygonFrom(Relation r, Collection<Map<Relation, Multipolygon>> maps) {
189 for (Map<Relation, Multipolygon> map : maps) {
190 map.remove(r);
191 }
192 }
193
194 @Override
195 public void primitivesAdded(PrimitivesAddedEvent event) {
196 // Do nothing
197 }
198
199 @Override
200 public void primitivesRemoved(PrimitivesRemovedEvent event) {
201 updateMultipolygonsReferringTo(event);
202 }
203
204 @Override
205 public void tagsChanged(TagsChangedEvent event) {
206 // Do nothing
207 }
208
209 @Override
210 public void nodeMoved(NodeMovedEvent event) {
211 updateMultipolygonsReferringTo(event);
212 }
213
214 @Override
215 public void wayNodesChanged(WayNodesChangedEvent event) {
216 updateMultipolygonsReferringTo(event);
217 }
218
219 @Override
220 public void relationMembersChanged(RelationMembersChangedEvent event) {
221 updateMultipolygonsReferringTo(event);
222 }
223
224 @Override
225 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
226 // Do nothing
227 }
228
229 @Override
230 public void dataChanged(DataChangedEvent event) {
231 // Do nothing
232 }
233
234 @Override
235 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
236 // Do nothing
237 }
238
239 @Override
240 public void layerAdded(Layer newLayer) {
241 // Do nothing
242 }
243
244 @Override
245 public void layerRemoved(Layer oldLayer) {
246 if (oldLayer instanceof OsmDataLayer) {
247 clear(((OsmDataLayer) oldLayer).data);
248 }
249 }
250
251 @Override
252 public void projectionChanged(Projection oldValue, Projection newValue) {
253 clear();
254 }
255
256 @Override
257 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
258
259 for (Iterator<PolyData> it = selectedPolyData.iterator(); it.hasNext();) {
260 it.next().selected = false;
261 it.remove();
262 }
263
264 DataSet ds = null;
265 Collection<Map<Relation, Multipolygon>> maps = null;
266 for (OsmPrimitive p : newSelection) {
267 if (p instanceof Way && p.getDataSet() != null) {
268 if (ds == null) {
269 ds = p.getDataSet();
270 }
271 for (OsmPrimitive ref : p.getReferrers()) {
272 if (isMultipolygon(ref)) {
273 if (maps == null) {
274 maps = getMapsFor(ds);
275 }
276 for (Map<Relation, Multipolygon> map : maps) {
277 Multipolygon multipolygon = map.get(ref);
278 if (multipolygon != null) {
279 for (PolyData pd : multipolygon.getCombinedPolygons()) {
280 if (pd.getWayIds().contains(p.getUniqueId())) {
281 pd.selected = true;
282 selectedPolyData.add(pd);
283 }
284 }
285 }
286 }
287 }
288 }
289 }
290 }
291 }
292}
Note: See TracBrowser for help on using the repository browser.