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

Last change on this file since 2845 was 2845, checked in by mjulius, 14 years ago

fix messages for data

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