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

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

Fire primitivesAdded event after also after primitive was downloaded (incomplete set to false). Remove DataChangeListener from RelationListDialog (should not be necessary anymore, was there only for catching incomplete state changes)

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