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

Last change on this file since 3734 was 3719, checked in by bastiK, 13 years ago

added missing license information

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