source: josm/trunk/src/org/openstreetmap/josm/data/osm/Relation.java@ 3153

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

Return copy of bbox in Way.getBBox (to make sure internal copy won't be modified)

  • Property svn:eol-style set to native
File size: 12.6 KB
Line 
1package org.openstreetmap.josm.data.osm;
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.HashSet;
6import java.util.List;
7import java.util.Set;
8
9import org.openstreetmap.josm.Main;
10import org.openstreetmap.josm.data.osm.visitor.Visitor;
11import org.openstreetmap.josm.tools.CopyList;
12
13/**
14 * An relation, having a set of tags and any number (0...n) of members.
15 *
16 * @author Frederik Ramm <frederik@remote.org>
17 */
18public final class Relation extends OsmPrimitive {
19
20 private final List<RelationMember> members = new ArrayList<RelationMember>();
21
22 private BBox bbox;
23
24 /**
25 * @return Members of the relation. Changes made in returned list are not mapped
26 * back to the primitive, use setMembers() to modify the members
27 * @since 1925
28 */
29 public List<RelationMember> getMembers() {
30 return new CopyList<RelationMember>(members.toArray(new RelationMember[members.size()]));
31 }
32
33 /**
34 *
35 * @param members Can be null, in that case all members are removed
36 * @since 1925
37 */
38 public void setMembers(List<RelationMember> members) {
39 for (RelationMember rm:this.members) {
40 rm.getMember().removeReferrer(this);
41 }
42
43 this.members.clear();
44 if (members != null) {
45 this.members.addAll(members);
46 }
47 for (RelationMember rm:this.members) {
48 rm.getMember().addReferrer(this);
49 }
50
51 fireMembersChanged();
52 }
53
54 /**
55 *
56 * @since 1926
57 */
58 public int getMembersCount() {
59 return members.size();
60 }
61
62 /**
63 *
64 * @param index
65 * @return
66 * @since 1926
67 */
68 public RelationMember getMember(int index) {
69 return members.get(index);
70 }
71
72 /**
73 *
74 * @param member
75 * @since 1951
76 */
77 public void addMember(RelationMember member) {
78 members.add(member);
79 member.getMember().addReferrer(this);
80 fireMembersChanged();
81 }
82
83 /**
84 *
85 * @param index
86 * @param member
87 * @since 1951
88 */
89 public void addMember(int index, RelationMember member) {
90 members.add(index, member);
91 member.getMember().addReferrer(this);
92 fireMembersChanged();
93 }
94
95 /**
96 * Replace member at position specified by index.
97 * @param index
98 * @param member
99 * @return Member that was at the position
100 * @since 1951
101 */
102 public RelationMember setMember(int index, RelationMember member) {
103 RelationMember result = members.set(index, member);
104 if (result.getMember() != member.getMember()) {
105 member.getMember().addReferrer(this);
106 result.getMember().removeReferrer(this);
107 fireMembersChanged();
108 }
109 return result;
110 }
111
112 /**
113 * Removes member at specified position.
114 * @param index
115 * @return Member that was at the position
116 * @since 1951
117 */
118 public RelationMember removeMember(int index) {
119 RelationMember result = members.remove(index);
120 for (RelationMember rm:members) {
121 // Do not remove referrer if this primitive is used in relation twice
122 if (rm.getMember() == result.getMember())
123 return result;
124 }
125 result.getMember().removeReferrer(this);
126 fireMembersChanged();
127 return result;
128 }
129
130 @Override public void visit(Visitor visitor) {
131 visitor.visit(this);
132 }
133
134 protected Relation(long id, boolean allowNegative) {
135 super(id, allowNegative);
136 }
137
138 /**
139 * Create a new relation with id 0
140 */
141 public Relation() {
142 super(0, false);
143 }
144
145 public Relation(Relation clone, boolean clearId) {
146 super(clone.getUniqueId(), true);
147 cloneFrom(clone);
148 if (clearId) {
149 clearOsmId();
150 }
151 }
152
153 /**
154 * Create an identical clone of the argument (including the id)
155 */
156 public Relation(Relation clone) {
157 this(clone, false);
158 }
159
160 /**
161 * Creates a new relation for the given id. If the id > 0, the way is marked
162 * as incomplete.
163 *
164 * @param id the id. > 0 required
165 * @throws IllegalArgumentException thrown if id < 0
166 */
167 public Relation(long id) throws IllegalArgumentException {
168 super(id, false);
169 }
170
171 /**
172 * Creates new relation
173 * @param id
174 * @param version
175 */
176 public Relation(long id, int version) {
177 super(id, version, false);
178 }
179
180 @Override public void cloneFrom(OsmPrimitive osm) {
181 super.cloneFrom(osm);
182 // It's not necessary to clone members as RelationMember class is immutable
183 setMembers(((Relation)osm).getMembers());
184 }
185
186 @Override public void load(PrimitiveData data) {
187 super.load(data);
188
189 RelationData relationData = (RelationData) data;
190
191 List<RelationMember> newMembers = new ArrayList<RelationMember>();
192 for (RelationMemberData member : relationData.getMembers()) {
193 OsmPrimitive primitive = getDataSet().getPrimitiveById(member);
194 if (primitive == null)
195 throw new AssertionError("Data consistency problem - relation with missing member detected");
196 newMembers.add(new RelationMember(member.getRole(), primitive));
197 }
198 setMembers(newMembers);
199 }
200
201 @Override public RelationData save() {
202 RelationData data = new RelationData();
203 saveCommonAttributes(data);
204 for (RelationMember member:getMembers()) {
205 data.getMembers().add(new RelationMemberData(member.getRole(), member.getMember()));
206 }
207 return data;
208 }
209
210 @Override public String toString() {
211 StringBuilder result = new StringBuilder();
212 result.append("{Relation id=");
213 result.append(getUniqueId());
214 result.append(" version=");
215 result.append(getVersion());
216 result.append(" ");
217 result.append(getFlagsAsString());
218 result.append(" [");
219 for (RelationMember rm:getMembers()) {
220 result.append(OsmPrimitiveType.from(rm.getMember()));
221 result.append(" ");
222 result.append(rm.getMember().getUniqueId());
223 result.append(", ");
224 }
225 result.delete(result.length()-2, result.length());
226 result.append("]");
227 result.append("}");
228 return result.toString();
229 }
230
231 @Override
232 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
233 if (other == null || ! (other instanceof Relation) )
234 return false;
235 if (! super.hasEqualSemanticAttributes(other))
236 return false;
237 Relation r = (Relation)other;
238 return members.equals(r.members);
239 }
240
241 public int compareTo(OsmPrimitive o) {
242 return o instanceof Relation ? Long.valueOf(getUniqueId()).compareTo(o.getUniqueId()) : -1;
243 }
244
245 public RelationMember firstMember() {
246 if (isIncomplete()) return null;
247 return (members.size() == 0) ? null : members.get(0);
248 }
249 public RelationMember lastMember() {
250 if (isIncomplete()) return null;
251 return (members.size() == 0) ? null : members.get(members.size() -1);
252 }
253
254 /**
255 * removes all members with member.member == primitive
256 *
257 * @param primitive the primitive to check for
258 */
259 public void removeMembersFor(OsmPrimitive primitive) {
260 if (primitive == null)
261 return;
262
263 ArrayList<RelationMember> todelete = new ArrayList<RelationMember>();
264 for (RelationMember member: members) {
265 if (member.getMember() == primitive) {
266 todelete.add(member);
267 }
268 }
269 primitive.removeReferrer(this);
270 members.removeAll(todelete);
271 fireMembersChanged();
272 }
273
274 @Override
275 public void setDeleted(boolean deleted) {
276 for (RelationMember rm:members) {
277 if (deleted) {
278 rm.getMember().removeReferrer(this);
279 } else {
280 rm.getMember().addReferrer(this);
281 }
282 }
283 super.setDeleted(deleted);
284 }
285
286 /**
287 * removes all members with member.member == primitive
288 *
289 * @param primitives the primitives to check for
290 */
291 public void removeMembersFor(Collection<OsmPrimitive> primitives) {
292 if (primitives == null || primitives.isEmpty())
293 return;
294
295 ArrayList<RelationMember> todelete = new ArrayList<RelationMember>();
296 for (RelationMember member: members) {
297 if (primitives.contains(member.getMember())) {
298 todelete.add(member);
299 }
300 }
301 members.removeAll(todelete);
302 for (OsmPrimitive primitive:primitives) {
303 primitive.removeReferrer(this);
304 }
305 fireMembersChanged();
306 }
307
308 @Override
309 public String getDisplayName(NameFormatter formatter) {
310 return formatter.format(this);
311 }
312
313 /**
314 * Replies the set of {@see OsmPrimitive}s referred to by at least one
315 * member of this relation
316 *
317 * @return the set of {@see OsmPrimitive}s referred to by at least one
318 * member of this relation
319 */
320 public Set<OsmPrimitive> getMemberPrimitives() {
321 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
322 for (RelationMember m: members) {
323 if (m.getMember() != null) {
324 ret.add(m.getMember());
325 }
326 }
327 return ret;
328 }
329
330 public OsmPrimitiveType getType() {
331 return OsmPrimitiveType.RELATION;
332 }
333
334 @Override
335 public BBox getBBox() {
336 if (members.isEmpty())
337 return new BBox(0, 0, 0, 0);
338 if (getDataSet() == null)
339 return calculateBBox(new HashSet<PrimitiveId>());
340 else {
341 if (bbox == null) {
342 bbox = calculateBBox(new HashSet<PrimitiveId>());
343 }
344 return new BBox(bbox);
345 }
346 }
347
348 private BBox calculateBBox(Set<PrimitiveId> visitedRelations) {
349 if (visitedRelations.contains(this))
350 return null;
351 visitedRelations.add(this);
352 if (members.isEmpty())
353 return null;
354 else {
355 BBox result = null;
356 for (RelationMember rm:members) {
357 BBox box = rm.isRelation()?rm.getRelation().calculateBBox(visitedRelations):rm.getMember().getBBox();
358 if (box != null) {
359 if (result == null) {
360 result = box;
361 } else {
362 result.add(box);
363 }
364 }
365 }
366 return result;
367 }
368 }
369
370 @Override
371 public void updatePosition() {
372 bbox = calculateBBox(new HashSet<PrimitiveId>());
373 }
374
375 @Override
376 public void setDataset(DataSet dataSet) {
377 super.setDataset(dataSet);
378 checkMembers();
379 bbox = null; // bbox might have changed if relation was in ds, was removed, modified, added back to dataset
380 }
381
382 private void checkMembers() {
383 DataSet dataSet = getDataSet();
384 if (dataSet != null) {
385 for (RelationMember rm: members) {
386 if (rm.getMember().getDataSet() != dataSet)
387 throw new DataIntegrityProblemException(String.format("Relation member must be part of the same dataset as relation(%s, %s)", getPrimitiveId(), rm.getMember().getPrimitiveId()));
388 }
389 if (Main.pref.getBoolean("debug.checkDeleteReferenced", true)) {
390 for (RelationMember rm: members) {
391 if (rm.getMember().isDeleted())
392 throw new DataIntegrityProblemException("Deleted member referenced: " + toString());
393 }
394 }
395 }
396 }
397
398 private void fireMembersChanged() {
399 checkMembers();
400 if (getDataSet() != null) {
401 getDataSet().fireRelationMembersChanged(this);
402 }
403 }
404
405 /**
406 * Replies true if at least one child primitive is incomplete
407 *
408 * @return true if at least one child primitive is incomplete
409 */
410 public boolean hasIncompleteMembers() {
411 for (RelationMember rm: members) {
412 if (rm.getMember().isIncomplete()) return true;
413 }
414 return false;
415 }
416
417 /**
418 * Replies a collection with the incomplete children this relation
419 * refers to
420 *
421 * @return the incomplete children. Empty collection if no children are incomplete.
422 */
423 public Collection<OsmPrimitive> getIncompleteMembers() {
424 Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
425 for (RelationMember rm: members) {
426 if (!rm.getMember().isIncomplete()) {
427 continue;
428 }
429 ret.add(rm.getMember());
430 }
431 return ret;
432 }
433}
Note: See TracBrowser for help on using the repository browser.