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

Last change on this file since 5339 was 5266, checked in by bastiK, 12 years ago

fixed majority of javadoc warnings by replacing "{@see" by "{@link"

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