source: josm/trunk/src/org/openstreetmap/josm/data/osm/DataSet.java@ 3157

Last change on this file since 3157 was 3147, checked in by jttt, 14 years ago

Use virtual collections for Dataset.all... methods

  • Property svn:eol-style set to native
File size: 31.1 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.geom.Area;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.Iterator;
14import java.util.LinkedHashSet;
15import java.util.LinkedList;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19
20import org.openstreetmap.josm.data.SelectionChangedListener;
21import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
22import org.openstreetmap.josm.data.osm.event.ChangesetIdChangedEvent;
23import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
24import org.openstreetmap.josm.data.osm.event.DataSetListener;
25import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
26import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
27import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
28import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
29import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
30import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
31
32/**
33 * DataSet is the data behind the application. It can consists of only a few points up to the whole
34 * osm database. DataSet's can be merged together, saved, (up/down/disk)loaded etc.
35 *
36 * Note that DataSet is not an osm-primitive and so has no key association but a few members to
37 * store some information.
38 *
39 * @author imi
40 */
41public class DataSet implements Cloneable {
42
43 private static class IdHash implements Hash<PrimitiveId,OsmPrimitive> {
44
45 public int getHashCode(PrimitiveId k) {
46 return (int)k.getUniqueId() ^ k.getType().hashCode();
47 }
48
49 public boolean equals(PrimitiveId key, OsmPrimitive value) {
50 if (key == null || value == null) return false;
51 return key.getUniqueId() == value.getUniqueId() && key.getType() == value.getType();
52 }
53 }
54
55 private Storage<OsmPrimitive> allPrimitives = new Storage<OsmPrimitive>(new IdHash());
56 private Map<PrimitiveId, OsmPrimitive> primitivesMap = allPrimitives.foreignKey(new IdHash());
57 private List<DataSetListener> listeners = new ArrayList<DataSetListener>();
58 // Number of open calls to beginUpdate
59 private int updateCount;
60
61 private int highlightUpdateCount;
62
63 /**
64 * This method can be used to detect changes in highlight state of primitives. If highlighting was changed
65 * then the method will return different number.
66 * @return
67 */
68 public int getHighlightUpdateCount() {
69 return highlightUpdateCount;
70 }
71
72 /**
73 * The API version that created this data set, if any.
74 */
75 private String version;
76
77 /**
78 * Replies the API version this dataset was created from. May be null.
79 *
80 * @return the API version this dataset was created from. May be null.
81 */
82 public String getVersion() {
83 return version;
84 }
85
86 /**
87 * Sets the API version this dataset was created from.
88 *
89 * @param version the API version, i.e. "0.5" or "0.6"
90 */
91 public void setVersion(String version) {
92 this.version = version;
93 }
94
95 /**
96 * All nodes goes here, even when included in other data (ways etc). This enables the instant
97 * conversion of the whole DataSet by iterating over this data structure.
98 */
99 private QuadBuckets<Node> nodes = new QuadBuckets<Node>();
100
101 /**
102 * Replies an unmodifiable collection of nodes in this dataset
103 *
104 * @return an unmodifiable collection of nodes in this dataset
105 */
106 public Collection<Node> getNodes() {
107 return Collections.unmodifiableCollection(nodes);
108 }
109
110 public List<Node> searchNodes(BBox bbox) {
111 return nodes.search(bbox);
112 }
113
114 /**
115 * All ways (Streets etc.) in the DataSet.
116 *
117 * The way nodes are stored only in the way list.
118 */
119 private QuadBuckets<Way> ways = new QuadBuckets<Way>();
120
121 /**
122 * Replies an unmodifiable collection of ways in this dataset
123 *
124 * @return an unmodifiable collection of ways in this dataset
125 */
126 public Collection<Way> getWays() {
127 return Collections.unmodifiableCollection(ways);
128 }
129
130 public List<Way> searchWays(BBox bbox) {
131 return ways.search(bbox);
132 }
133
134 /**
135 * All relations/relationships
136 */
137 private Collection<Relation> relations = new LinkedList<Relation>();
138
139 /**
140 * Replies an unmodifiable collection of relations in this dataset
141 *
142 * @return an unmodifiable collection of relations in this dataset
143 */
144 public Collection<Relation> getRelations() {
145 return Collections.unmodifiableCollection(relations);
146 }
147
148 public List<Relation> searchRelations(BBox bbox) {
149 // QuadBuckets might be useful here (don't forget to do reindexing after some of rm is changed)
150 List<Relation> result = new ArrayList<Relation>();
151 for (Relation r: relations) {
152 if (r.getBBox().intersects(bbox)) {
153 result.add(r);
154 }
155 }
156 return result;
157 }
158
159 /**
160 * All data sources of this DataSet.
161 */
162 public Collection<DataSource> dataSources = new LinkedList<DataSource>();
163
164 /**
165 * @return A collection containing all primitives of the dataset. Data are not ordered
166 */
167 public Collection<OsmPrimitive> allPrimitives() {
168 return Collections.unmodifiableCollection(allPrimitives);
169 }
170
171 /**
172 * @return A collection containing all not-deleted primitives (except keys).
173 */
174 public Collection<OsmPrimitive> allNonDeletedPrimitives() {
175 return new DatasetCollection.AllNonDeleted(allPrimitives);
176 }
177
178 public Collection<OsmPrimitive> allNonDeletedCompletePrimitives() {
179 return new DatasetCollection.AllNonDeletedComplete(allPrimitives);
180 }
181
182 public Collection<OsmPrimitive> allNonDeletedPhysicalPrimitives() {
183 return new DatasetCollection.AllNonDeletedPhysical(allPrimitives);
184 }
185
186 /**
187 * @return A collection containing all modified primitives
188 */
189 public Collection<OsmPrimitive> allModifiedPrimitives() {
190 return new DatasetCollection.AllModified(allPrimitives);
191 }
192
193 /**
194 * Adds a primitive to the dataset
195 *
196 * @param primitive the primitive.
197 */
198 public void addPrimitive(OsmPrimitive primitive) {
199 if (getPrimitiveById(primitive) != null)
200 throw new DataIntegrityProblemException(
201 tr("Unable to add primitive {0} to the dataset because it is already included", primitive.toString()));
202
203 if (primitive instanceof Node) {
204 nodes.add((Node) primitive);
205 } else if (primitive instanceof Way) {
206 ways.add((Way) primitive);
207 } else if (primitive instanceof Relation) {
208 relations.add((Relation) primitive);
209 }
210 allPrimitives.add(primitive);
211 primitive.setDataset(this);
212 firePrimitivesAdded(Collections.singletonList(primitive), false);
213 }
214
215 public OsmPrimitive addPrimitive(PrimitiveData data) {
216 OsmPrimitive result;
217 if (data instanceof NodeData) {
218 result = new Node();
219 } else if (data instanceof WayData) {
220 result = new Way();
221 } else if (data instanceof RelationData) {
222 result = new Relation();
223 } else
224 throw new AssertionError();
225 result.setDataset(this);
226 result.load(data);
227 addPrimitive(result);
228 return result;
229 }
230
231 /**
232 * Removes a primitive from the dataset. This method only removes the
233 * primitive form the respective collection of primitives managed
234 * by this dataset, i.e. from {@see #nodes}, {@see #ways}, or
235 * {@see #relations}. References from other primitives to this
236 * primitive are left unchanged.
237 *
238 * @param primitive the primitive
239 */
240 public void removePrimitive(PrimitiveId primitiveId) {
241 OsmPrimitive primitive = getPrimitiveByIdChecked(primitiveId);
242 if (primitive == null)
243 return;
244 if (primitive instanceof Node) {
245 nodes.remove(primitive);
246 } else if (primitive instanceof Way) {
247 ways.remove(primitive);
248 } else if (primitive instanceof Relation) {
249 relations.remove(primitive);
250 }
251 selectedPrimitives.remove(primitive);
252 allPrimitives.remove(primitive);
253 primitive.setDataset(null);
254 errors.remove(primitive);
255 firePrimitivesRemoved(Collections.singletonList(primitive), false);
256 }
257
258 /*---------------------------------------------------
259 * SELECTION HANDLING
260 *---------------------------------------------------*/
261
262 /**
263 * A list of listeners to selection changed events. The list is static, as listeners register
264 * themselves for any dataset selection changes that occur, regardless of the current active
265 * dataset. (However, the selection does only change in the active layer)
266 */
267 public static final Collection<SelectionChangedListener> selListeners =
268 Collections.synchronizedList(new LinkedList<SelectionChangedListener>());
269
270 /**
271 * Notifies all registered {@see SelectionChangedListener} about the current selection in
272 * this dataset.
273 *
274 */
275 public void fireSelectionChanged(){
276 synchronized (selListeners) {
277 List<? extends OsmPrimitive> currentSelection = Collections.unmodifiableList(new ArrayList<OsmPrimitive>(selectedPrimitives));
278 for (SelectionChangedListener l : selListeners) {
279 l.selectionChanged(currentSelection);
280 }
281 }
282 }
283
284 LinkedHashSet<OsmPrimitive> selectedPrimitives = new LinkedHashSet<OsmPrimitive>();
285
286 public Collection<OsmPrimitive> getSelectedNodesAndWays() {
287 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
288 for (OsmPrimitive osm : selectedPrimitives) {
289 if (osm instanceof Way ||
290 osm instanceof Node) {
291 sel.add(osm);
292 }
293 }
294 return sel;
295 }
296
297 /**
298 * Replies an unmodifiable collection of primitives currently selected
299 * in this dataset
300 *
301 * @return unmodifiable collection of primitives
302 */
303 public Collection<OsmPrimitive> getSelected() {
304 return Collections.unmodifiableSet(selectedPrimitives);
305 }
306
307 /**
308 * Return selected nodes.
309 */
310 public Collection<Node> getSelectedNodes() {
311 List<Node> result = new ArrayList<Node>(selectedPrimitives.size());
312 for (OsmPrimitive primitive:selectedPrimitives) {
313 if (primitive instanceof Node) {
314 result.add((Node)primitive);
315 }
316 }
317 return result;
318 }
319
320 /**
321 * Return selected ways.
322 */
323 public Collection<Way> getSelectedWays() {
324 List<Way> result = new ArrayList<Way>(selectedPrimitives.size());
325 for (OsmPrimitive primitive:selectedPrimitives) {
326 if (primitive instanceof Way) {
327 result.add((Way)primitive);
328 }
329 }
330 return result;
331 }
332
333 /**
334 * Return selected relations.
335 */
336 public Collection<Relation> getSelectedRelations() {
337 List<Relation> result = new ArrayList<Relation>(selectedPrimitives.size() / 10);
338 for (OsmPrimitive primitive:selectedPrimitives) {
339 if (primitive instanceof Relation) {
340 result.add((Relation)primitive);
341 }
342 }
343 return result;
344 }
345
346 public boolean isSelected(OsmPrimitive osm) {
347 return selectedPrimitives.contains(osm);
348 }
349
350 public void toggleSelected(Collection<? extends PrimitiveId> osm) {
351 boolean changed = false;
352 for (PrimitiveId o : osm) {
353 changed = changed | this.__toggleSelected(o);
354 }
355 if (changed) {
356 fireSelectionChanged();
357 }
358 }
359 public void toggleSelected(PrimitiveId... osm) {
360 toggleSelected(Arrays.asList(osm));
361 }
362 private boolean __toggleSelected(PrimitiveId primitiveId) {
363 OsmPrimitive primitive = getPrimitiveByIdChecked(primitiveId);
364 if (primitive == null)
365 return false;
366 if (!selectedPrimitives.remove(primitive)) {
367 selectedPrimitives.add(primitive);
368 }
369 return true;
370 }
371
372 /**
373 * Sets the current selection to the primitives in <code>selection</code>.
374 * Notifies all {@see SelectionChangedListener} if <code>fireSelectionChangeEvent</code> is true.
375 *
376 * @param selection the selection
377 * @param fireSelectionChangeEvent true, if the selection change listeners are to be notified; false, otherwise
378 */
379 public void setSelected(Collection<? extends PrimitiveId> selection, boolean fireSelectionChangeEvent) {
380 boolean wasEmpty = selectedPrimitives.isEmpty();
381 selectedPrimitives = new LinkedHashSet<OsmPrimitive>();
382 addSelected(selection, fireSelectionChangeEvent);
383 if (!wasEmpty && selectedPrimitives.isEmpty() && fireSelectionChangeEvent) {
384 fireSelectionChanged();
385 }
386 }
387
388 /**
389 * Sets the current selection to the primitives in <code>selection</code>
390 * and notifies all {@see SelectionChangedListener}.
391 *
392 * @param selection the selection
393 */
394 public void setSelected(Collection<? extends PrimitiveId> selection) {
395 setSelected(selection, true /* fire selection change event */);
396 }
397
398 public void setSelected(PrimitiveId... osm) {
399 if (osm.length == 1 && osm[0] == null) {
400 setSelected();
401 return;
402 }
403 List<PrimitiveId> list = Arrays.asList(osm);
404 setSelected(list);
405 }
406
407 /**
408 * Adds the primitives in <code>selection</code> to the current selection
409 * and notifies all {@see SelectionChangedListener}.
410 *
411 * @param selection the selection
412 */
413 public void addSelected(Collection<? extends PrimitiveId> selection) {
414 addSelected(selection, true /* fire selection change event */);
415 }
416
417 public void addSelected(PrimitiveId... osm) {
418 addSelected(Arrays.asList(osm));
419 }
420
421 /**
422 * Adds the primitives in <code>selection</code> to the current selection.
423 * Notifies all {@see SelectionChangedListener} if <code>fireSelectionChangeEvent</code> is true.
424 *
425 * @param selection the selection
426 * @param fireSelectionChangeEvent true, if the selection change listeners are to be notified; false, otherwise
427 */
428 public void addSelected(Collection<? extends PrimitiveId> selection, boolean fireSelectionChangeEvent) {
429 boolean changed = false;
430 for (PrimitiveId id: selection) {
431 OsmPrimitive primitive = getPrimitiveByIdChecked(id);
432 if (primitive != null) {
433 changed = changed | selectedPrimitives.add(primitive);
434 }
435 }
436 if (fireSelectionChangeEvent && changed) {
437 fireSelectionChanged();
438 }
439 }
440
441 /**
442 * Remove the selection from every value in the collection.
443 * @param list The collection to remove the selection from.
444 */
445 public void clearSelection(PrimitiveId... osm) {
446 clearSelection(Arrays.asList(osm));
447 }
448 public void clearSelection(Collection<? extends PrimitiveId> list) {
449 boolean changed = false;
450 for (PrimitiveId id:list) {
451 OsmPrimitive primitive = getPrimitiveById(id);
452 if (primitive != null) {
453 changed = changed | selectedPrimitives.remove(primitive);
454 }
455 }
456 if (changed) {
457 fireSelectionChanged();
458 }
459 }
460 public void clearSelection() {
461 if (!selectedPrimitives.isEmpty()) {
462 selectedPrimitives.clear();
463 fireSelectionChanged();
464 }
465 }
466
467 /*------------------------------------------------------
468 * FILTERED / DISABLED HANDLING
469 *-----------------------------------------------------*/
470
471 public void setDisabled(OsmPrimitive... osm) {
472 if (osm.length == 1 && osm[0] == null) {
473 setDisabled();
474 return;
475 }
476 clearDisabled(nodes);
477 clearDisabled(ways);
478 clearDisabled(relations);
479 for (OsmPrimitive o : osm)
480 if (o != null) {
481 o.setDisabled(true);
482 }
483 }
484
485 public void setFiltered(Collection<? extends OsmPrimitive> selection) {
486 clearFiltered(nodes);
487 clearFiltered(ways);
488 clearFiltered(relations);
489 for (OsmPrimitive osm : selection) {
490 osm.setFiltered(true);
491 }
492 }
493
494 public void setFiltered(OsmPrimitive... osm) {
495 if (osm.length == 1 && osm[0] == null) {
496 setFiltered();
497 return;
498 }
499 clearFiltered(nodes);
500 clearFiltered(ways);
501 clearFiltered(relations);
502 for (OsmPrimitive o : osm)
503 if (o != null) {
504 o.setFiltered(true);
505 }
506 }
507
508 public void setDisabled(Collection<? extends OsmPrimitive> selection) {
509 clearDisabled(nodes);
510 clearDisabled(ways);
511 clearDisabled(relations);
512 for (OsmPrimitive osm : selection) {
513 osm.setDisabled(true);
514 }
515 }
516
517 /**
518 * Remove the filtered parameter from every value in the collection.
519 * @param list The collection to remove the filtered parameter from.
520 */
521 private void clearFiltered(Collection<? extends OsmPrimitive> list) {
522 if (list == null)
523 return;
524 for (OsmPrimitive osm : list) {
525 osm.setFiltered(false);
526 }
527 }
528 /**
529 * Remove the disabled parameter from every value in the collection.
530 * @param list The collection to remove the disabled parameter from.
531 */
532 private void clearDisabled(Collection<? extends OsmPrimitive> list) {
533 if (list == null)
534 return;
535 for (OsmPrimitive osm : list) {
536 osm.setDisabled(false);
537 }
538 }
539
540 @Override public DataSet clone() {
541 DataSet ds = new DataSet();
542 HashMap<OsmPrimitive, OsmPrimitive> primitivesMap = new HashMap<OsmPrimitive, OsmPrimitive>();
543 for (Node n : nodes) {
544 Node newNode = new Node(n);
545 primitivesMap.put(n, newNode);
546 ds.addPrimitive(newNode);
547 }
548 for (Way w : ways) {
549 Way newWay = new Way(w);
550 primitivesMap.put(w, newWay);
551 List<Node> newNodes = new ArrayList<Node>();
552 for (Node n: w.getNodes()) {
553 newNodes.add((Node)primitivesMap.get(n));
554 }
555 newWay.setNodes(newNodes);
556 ds.addPrimitive(newWay);
557 }
558 // Because relations can have other relations as members we first clone all relations
559 // and then get the cloned members
560 for (Relation r : relations) {
561 Relation newRelation = new Relation(r, r.isNew());
562 newRelation.setMembers(null);
563 primitivesMap.put(r, newRelation);
564 ds.addPrimitive(newRelation);
565 }
566 for (Relation r : relations) {
567 Relation newRelation = (Relation)primitivesMap.get(r);
568 List<RelationMember> newMembers = new ArrayList<RelationMember>();
569 for (RelationMember rm: r.getMembers()) {
570 newMembers.add(new RelationMember(rm.getRole(), primitivesMap.get(rm.getMember())));
571 }
572 newRelation.setMembers(newMembers);
573 }
574 for (DataSource source : dataSources) {
575 ds.dataSources.add(new DataSource(source.bounds, source.origin));
576 }
577 ds.version = version;
578 return ds;
579 }
580
581 /**
582 * Returns the total area of downloaded data (the "yellow rectangles").
583 * @return Area object encompassing downloaded data.
584 */
585 public Area getDataSourceArea() {
586 if (dataSources.isEmpty()) return null;
587 Area a = new Area();
588 for (DataSource source : dataSources) {
589 // create area from data bounds
590 a.add(new Area(source.bounds.asRect()));
591 }
592 return a;
593 }
594
595 /**
596 * returns a primitive with a given id from the data set. null, if no such primitive
597 * exists
598 *
599 * @param id uniqueId of the primitive. Might be < 0 for newly created primitives
600 * @param type the type of the primitive. Must not be null.
601 * @return the primitive
602 * @exception NullPointerException thrown, if type is null
603 */
604 public OsmPrimitive getPrimitiveById(long id, OsmPrimitiveType type) {
605 return getPrimitiveById(new SimplePrimitiveId(id, type), false);
606 }
607
608 public OsmPrimitive getPrimitiveById(PrimitiveId primitiveId) {
609 return getPrimitiveById(primitiveId, false);
610 }
611
612 public OsmPrimitive getPrimitiveById(PrimitiveId primitiveId, boolean createNew) {
613 OsmPrimitive result = primitivesMap.get(primitiveId);
614
615 if (result == null && createNew) {
616 switch (primitiveId.getType()) {
617 case NODE: result = new Node(primitiveId.getUniqueId(), true); break;
618 case WAY: result = new Way(primitiveId.getUniqueId(), true); break;
619 case RELATION: result = new Relation(primitiveId.getUniqueId(), true); break;
620 }
621 addPrimitive(result);
622 }
623
624 return result;
625 }
626
627 /**
628 * Show message and stack trace in log in case primitive is not found
629 * @param primitiveId
630 * @return Primitive by id.
631 */
632 private OsmPrimitive getPrimitiveByIdChecked(PrimitiveId primitiveId) {
633 OsmPrimitive result = getPrimitiveById(primitiveId);
634 if (result == null) {
635 System.out.println(tr("JOSM expected to find primitive [{0} {1}] in dataset but it is not there. Please report this "
636 + " at http://josm.openstreetmap.de . This is not a critical error, it should be safe to continue in your work.",
637 primitiveId.getType(), Long.toString(primitiveId.getUniqueId())));
638 new Exception().printStackTrace();
639 }
640
641 return result;
642 }
643
644 public Set<Long> getPrimitiveIds() {
645 HashSet<Long> ret = new HashSet<Long>();
646 for (OsmPrimitive primitive : nodes) {
647 ret.add(primitive.getId());
648 }
649 for (OsmPrimitive primitive : ways) {
650 ret.add(primitive.getId());
651 }
652 for (OsmPrimitive primitive : relations) {
653 ret.add(primitive.getId());
654 }
655 return ret;
656 }
657
658 protected void deleteWay(Way way) {
659 way.setNodes(null);
660 way.setDeleted(true);
661 }
662
663 /**
664 * removes all references from ways in this dataset to a particular node
665 *
666 * @param node the node
667 */
668 public void unlinkNodeFromWays(Node node) {
669 for (Way way: ways) {
670 List<Node> nodes = way.getNodes();
671 if (nodes.remove(node)) {
672 if (nodes.size() < 2) {
673 deleteWay(way);
674 } else {
675 way.setNodes(nodes);
676 }
677 }
678 }
679 }
680
681 /**
682 * removes all references from relations in this dataset to this primitive
683 *
684 * @param primitive the primitive
685 */
686 public void unlinkPrimitiveFromRelations(OsmPrimitive primitive) {
687 for (Relation relation : relations) {
688 List<RelationMember> members = relation.getMembers();
689
690 Iterator<RelationMember> it = members.iterator();
691 boolean removed = false;
692 while(it.hasNext()) {
693 RelationMember member = it.next();
694 if (member.getMember().equals(primitive)) {
695 it.remove();
696 removed = true;
697 }
698 }
699
700 if (removed) {
701 relation.setMembers(members);
702 }
703 }
704 }
705
706 /**
707 * removes all references from from other primitives to the
708 * referenced primitive
709 *
710 * @param referencedPrimitive the referenced primitive
711 */
712 public void unlinkReferencesToPrimitive(OsmPrimitive referencedPrimitive) {
713 if (referencedPrimitive instanceof Node) {
714 unlinkNodeFromWays((Node)referencedPrimitive);
715 unlinkPrimitiveFromRelations(referencedPrimitive);
716 } else {
717 unlinkPrimitiveFromRelations(referencedPrimitive);
718 }
719 }
720
721 /**
722 * Replies a list of parent relations which refer to the relation
723 * <code>child</code>. Replies an empty list if child is null.
724 *
725 * @param child the child relation
726 * @return a list of parent relations which refer to the relation
727 * <code>child</code>
728 */
729 public List<Relation> getParentRelations(Relation child) {
730 ArrayList<Relation> parents = new ArrayList<Relation>();
731 if (child == null)
732 return parents;
733 for (Relation parent : relations) {
734 if (parent == child) {
735 continue;
736 }
737 for (RelationMember member: parent.getMembers()) {
738 if (member.refersTo(child)) {
739 parents.add(parent);
740 break;
741 }
742 }
743 }
744 return parents;
745 }
746
747 /**
748 * Replies true if there is at least one primitive in this dataset with
749 * {@see OsmPrimitive#isModified()} == <code>true</code>.
750 *
751 * @return true if there is at least one primitive in this dataset with
752 * {@see OsmPrimitive#isModified()} == <code>true</code>.
753 */
754 public boolean isModified() {
755 for (Node n: nodes) {
756 if (n.isModified()) return true;
757 }
758 for (Way w: ways) {
759 if (w.isModified()) return true;
760 }
761 for (Relation r: relations) {
762 if (r.isModified()) return true;
763 }
764 return false;
765 }
766
767 private void reindexNode(Node node) {
768 nodes.remove(node);
769 nodes.add(node);
770 for (OsmPrimitive primitive: node.getReferrers()) {
771 if (primitive instanceof Way) {
772 reindexWay((Way)primitive);
773 } else {
774 reindexRelation((Relation) primitive);
775 }
776 }
777 }
778
779 private void reindexWay(Way way) {
780 BBox before = way.getBBox();
781 ways.remove(way);
782 way.updatePosition();
783 ways.add(way);
784 if (!way.getBBox().equals(before)) {
785 for (OsmPrimitive primitive: way.getReferrers()) {
786 reindexRelation((Relation)primitive);
787 }
788 }
789 }
790
791 private void reindexRelation(Relation relation) {
792 BBox before = relation.getBBox();
793 relation.updatePosition();
794 if (!before.equals(relation.getBBox())) {
795 for (OsmPrimitive primitive: relation.getReferrers()) {
796 reindexRelation((Relation) primitive);
797 }
798 }
799 }
800
801 public void addDataSetListener(DataSetListener dsl) {
802 listeners.add(dsl);
803 }
804
805 public void removeDataSetListener(DataSetListener dsl) {
806 listeners.remove(dsl);
807 }
808
809 /**
810 * Can be called before bigger changes on dataset. Events are disabled until {@link #endUpdate()}.
811 * {@link DataSetListener#dataChanged()} event is triggered after end of changes
812 * <br>
813 * Typical usecase should look like this:
814 * <pre>
815 * ds.beginUpdate();
816 * try {
817 * ...
818 * } finally {
819 * ds.endUpdate();
820 * }
821 * </pre>
822 */
823 public void beginUpdate() {
824 updateCount++;
825 }
826
827 /**
828 * @see DataSet#beginUpdate()
829 */
830 public void endUpdate() {
831 if (updateCount > 0) {
832 updateCount--;
833 if (updateCount == 0) {
834 fireDataChanged();
835 }
836 } else
837 throw new AssertionError("endUpdate called without beginUpdate");
838 }
839
840 private void fireEvent(AbstractDatasetChangedEvent event) {
841 if (updateCount == 0) {
842 for (DataSetListener dsl : listeners) {
843 event.fire(dsl);
844 }
845 }
846 }
847
848 private void fireDataChanged() {
849 fireEvent(new DataChangedEvent(this));
850 }
851
852 void firePrimitivesAdded(Collection<? extends OsmPrimitive> added, boolean wasIncomplete) {
853 fireEvent(new PrimitivesAddedEvent(this, added, wasIncomplete));
854 }
855
856 void firePrimitivesRemoved(Collection<? extends OsmPrimitive> removed, boolean wasComplete) {
857 fireEvent(new PrimitivesRemovedEvent(this, removed, wasComplete));
858 }
859
860 void fireTagsChanged(OsmPrimitive prim, Map<String, String> originalKeys) {
861 fireEvent(new TagsChangedEvent(this, prim, originalKeys));
862 }
863
864 void fireRelationMembersChanged(Relation r) {
865 reindexRelation(r);
866 fireEvent(new RelationMembersChangedEvent(this, r));
867 }
868
869 void fireNodeMoved(Node node) {
870 reindexNode(node);
871 fireEvent(new NodeMovedEvent(this, node));
872 }
873
874 void fireWayNodesChanged(Way way) {
875 reindexWay(way);
876 fireEvent(new WayNodesChangedEvent(this, way));
877 }
878
879 void fireChangesetIdChanged(OsmPrimitive primitive, int oldChangesetId, int newChangesetId) {
880 fireEvent(new ChangesetIdChangedEvent(this, Collections.singletonList(primitive), oldChangesetId, newChangesetId));
881 }
882
883 void fireHighlightingChanged(OsmPrimitive primitive) {
884 highlightUpdateCount++;
885 }
886
887 public void clenupDeletedPrimitives() {
888 if (cleanupDeleted(nodes.iterator())
889 | cleanupDeleted(ways.iterator())
890 | cleanupDeleted(relations.iterator())) {
891 fireSelectionChanged();
892 }
893 }
894
895 private boolean cleanupDeleted(Iterator<? extends OsmPrimitive> it) {
896 boolean changed = false;
897 while (it.hasNext()) {
898 OsmPrimitive primitive = it.next();
899 if (primitive.isDeleted()) {
900 selectedPrimitives.remove(primitive);
901 allPrimitives.remove(primitive);
902 primitive.setDataset(null);
903 changed = true;
904 it.remove();
905 }
906 }
907 return changed;
908 }
909
910 /**
911 * Removes all primitives from the dataset and resets the currently selected primitives
912 * to the empty collection. Also notifies selection change listeners if necessary.
913 *
914 */
915 public void clear() {
916 clearSelection();
917 for (OsmPrimitive primitive:allPrimitives) {
918 primitive.setDataset(null);
919 }
920 nodes.clear();
921 ways.clear();
922 relations.clear();
923 allPrimitives.clear();
924 }
925
926 // TODO Should be completely part of validator
927 private Map<OsmPrimitive, List<String>> errors = new HashMap<OsmPrimitive, List<String>>();
928
929 public void addError(OsmPrimitive primitive, String error) {
930 List<String> perrors = errors.get(primitive);
931 if (perrors == null) {
932 perrors = new ArrayList<String>();
933 }
934 perrors.add(error);
935 errors.put(primitive, perrors);
936 }
937
938 /**
939 * Replies the list of errors registered for this primitive.
940 *
941 * @param primitive the primitive for which errors are queried
942 * @return the list of errors. Never null.
943 * @deprecated should be moved to the validator plugin
944 */
945 @Deprecated
946 public List<String> getErrors(OsmPrimitive primitive) {
947 List<String> ret = errors.get(primitive);
948 if (ret == null) {
949 ret = Collections.emptyList();
950 }
951 return ret;
952 }
953
954 public void clearErrors()
955 {
956 errors.clear();
957 }
958}
Note: See TracBrowser for help on using the repository browser.