source: osm/applications/editors/josm/plugins/reltoolbox/src/relcontext/actions/TheRing.java

Last change on this file was 36483, checked in by stoecker, 7 months ago

set eol-style, fix checkstyle issues, add ignores

  • Property svn:eol-style set to native
File size: 23.7 KB
RevLine 
[32395]1// License: GPL. For details, see LICENSE file.
[26816]2package relcontext.actions;
3
[32398]4import static org.openstreetmap.josm.tools.I18n.tr;
5
[29535]6import java.util.ArrayList;
7import java.util.Collection;
[36132]8import java.util.Collections;
[29535]9import java.util.HashMap;
10import java.util.List;
11import java.util.Map;
12
[26834]13import javax.swing.JOptionPane;
[29535]14
15import org.openstreetmap.josm.command.AddCommand;
16import org.openstreetmap.josm.command.ChangeCommand;
[36217]17import org.openstreetmap.josm.command.ChangeMembersCommand;
18import org.openstreetmap.josm.command.ChangeNodesCommand;
19import org.openstreetmap.josm.command.ChangePropertyCommand;
[29535]20import org.openstreetmap.josm.command.Command;
21import org.openstreetmap.josm.command.DeleteCommand;
[33694]22import org.openstreetmap.josm.data.osm.DataSet;
[29535]23import org.openstreetmap.josm.data.osm.Node;
24import org.openstreetmap.josm.data.osm.OsmPrimitive;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.RelationMember;
27import org.openstreetmap.josm.data.osm.Way;
[33694]28import org.openstreetmap.josm.gui.MainApplication;
[34551]29import org.openstreetmap.josm.spi.preferences.Config;
[26816]30import org.openstreetmap.josm.tools.Geometry;
[26837]31import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
[33530]32import org.openstreetmap.josm.tools.Logging;
[26816]33
34/**
35 * One ring that contains segments forming an outer way of multipolygon.
36 * This class is used in {@link CreateMultipolygonAction#makeManySimpleMultipolygons(java.util.Collection)}.
[32395]37 *
[26816]38 * @author Zverik
39 */
40public class TheRing {
41 private static final String PREF_MULTIPOLY = "reltoolbox.multipolygon.";
42
[36102]43 private final Way source;
44 private final List<RingSegment> segments;
[36132]45 private Relation relation;
[26816]46
[32395]47 public TheRing(Way source) {
48 this.source = source;
49 segments = new ArrayList<>(1);
50 segments.add(new RingSegment(source));
[26816]51 }
[26837]52
[32395]53 public static boolean areAllOfThoseRings(Collection<Way> ways) {
54 List<Way> rings = new ArrayList<>();
55 for (Way way : ways) {
[32398]56 if (way.isClosed()) {
[32395]57 rings.add(way);
58 } else
59 return false;
60 }
[32398]61 if (rings.isEmpty() || ways.size() == 1)
[30738]62 return false;
[32395]63
64 // check for non-containment of rings
65 for (int i = 0; i < rings.size() - 1; i++) {
66 for (int j = i + 1; j < rings.size(); j++) {
67 PolygonIntersection intersection = Geometry.polygonIntersection(rings.get(i).getNodes(), rings.get(j).getNodes());
[32398]68 if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND || intersection == PolygonIntersection.SECOND_INSIDE_FIRST)
[32395]69 return false;
70 }
[30738]71 }
[26837]72
[32395]73 return true;
[26837]74 }
75
76 /**
77 * Creates ALOT of Multipolygons and pets him gently.
78 * @return list of new relations.
79 */
[32395]80 public static List<Relation> makeManySimpleMultipolygons(Collection<Way> selection, List<Command> commands) {
81 log("---------------------------------------");
82 List<TheRing> rings = new ArrayList<>(selection.size());
[32398]83 for (Way w : selection) {
[32395]84 rings.add(new TheRing(w));
85 }
[32398]86 for (int i = 0; i < rings.size() - 1; i++) {
87 for (int j = i + 1; j < rings.size(); j++) {
[32395]88 rings.get(i).collide(rings.get(j));
89 }
90 }
91 redistributeSegments(rings);
92 List<Relation> relations = new ArrayList<>();
93 Map<Relation, Relation> relationCache = new HashMap<>();
94 for (TheRing r : rings) {
95 commands.addAll(r.getCommands(relationCache));
96 relations.add(r.getRelation());
97 }
98 updateCommandsWithRelations(commands, relationCache);
99 return relations;
[26837]100 }
101
[32395]102 public void collide(TheRing other) {
103 boolean collideNoted = false;
104 for (int i = 0; i < segments.size(); i++) {
105 RingSegment segment1 = segments.get(i);
106 if (!segment1.isReference()) {
107 for (int j = 0; j < other.segments.size(); j++) {
108 RingSegment segment2 = other.segments.get(j);
109 if (!segment2.isReference()) {
110 log("Comparing " + segment1 + " and " + segment2);
111 Node[] split = getSplitNodes(segment1.getNodes(), segment2.getNodes(), segment1.isRing(), segment2.isRing());
112 if (split != null) {
113 if (!collideNoted) {
114 log("Rings for ways " + source.getUniqueId() + " and " + other.source.getUniqueId() + " collide.");
115 collideNoted = true;
116 }
117 RingSegment segment = splitRingAt(i, split[0], split[1]);
118 RingSegment otherSegment = other.splitRingAt(j, split[2], split[3]);
[32398]119 if (!areSegmentsEqual(segment, otherSegment))
120 throw new IllegalArgumentException(
121 "Error: algorithm gave incorrect segments: " + segment + " and " + otherSegment);
[32395]122 segment.makeReference(otherSegment);
123 }
124 }
[32398]125 if (segment1.isReference()) {
[32395]126 break;
127 }
[30738]128 }
129 }
130 }
[26816]131 }
[32395]132
[26844]133 /**
134 * Returns array of {start1, last1, start2, last2} or null if there is no common nodes.
135 */
[32395]136 public static Node[] getSplitNodes(List<Node> nodes1, List<Node> nodes2, boolean isRing1, boolean isRing2) {
137 int pos = 0;
[32398]138 while (pos < nodes1.size() && !nodes2.contains(nodes1.get(pos))) {
[32395]139 pos++;
[30738]140 }
[32395]141 boolean collideFound = pos == nodes1.size();
142 if (pos == 0 && isRing1) {
143 // rewind a bit
144 pos = nodes1.size() - 1;
[32398]145 while (pos > 0 && nodes2.contains(nodes1.get(pos))) {
[32395]146 pos--;
147 }
148 if (pos == 0 && nodes1.size() == nodes2.size()) {
[34551]149 JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
[32398]150 tr("Two rings are equal, and this must not be."), tr("Multipolygon from rings"), JOptionPane.ERROR_MESSAGE);
[32395]151 return null;
152 }
153 pos = pos == nodes1.size() - 1 ? 0 : pos + 1;
[30738]154 }
[32395]155 int firstPos = isRing1 ? pos : nodes1.size();
[32398]156 while (!collideFound) {
[32395]157 log("pos=" + pos);
158 int start1 = pos;
159 int start2 = nodes2.indexOf(nodes1.get(start1));
160 int last1 = incrementBy(start1, 1, nodes1.size(), isRing1);
161 int last2 = start2;
162 int increment2 = 0;
163 if (last1 >= 0) {
164 last2 = incrementBy(start2, -1, nodes2.size(), isRing2);
[32398]165 if (last2 >= 0 && nodes1.get(last1).equals(nodes2.get(last2))) {
[32395]166 increment2 = -1;
167 } else {
168 last2 = incrementBy(start2, 1, nodes2.size(), isRing2);
[32398]169 if (last2 >= 0 && nodes1.get(last1).equals(nodes2.get(last2))) {
[32395]170 increment2 = 1;
171 }
172 }
[30738]173 }
[32395]174 log("last1=" + last1 + " last2=" + last2 + " increment2=" + increment2);
175 if (increment2 != 0) {
176 // find the first nodes
177 boolean reachedEnd = false;
[32398]178 while (!reachedEnd) {
[32395]179 int newLast1 = incrementBy(last1, 1, nodes1.size(), isRing1);
180 int newLast2 = incrementBy(last2, increment2, nodes2.size(), isRing2);
[32398]181 if (newLast1 < 0 || newLast2 < 0 || !nodes1.get(newLast1).equals(nodes2.get(newLast2))) {
[32395]182 reachedEnd = true;
183 } else {
184 last1 = newLast1;
185 last2 = newLast2;
186 }
187 }
188 log("last1=" + last1 + " last2=" + last2);
189 if (increment2 < 0) {
190 int tmp = start2;
191 start2 = last2;
192 last2 = tmp;
193 }
194 return new Node[] {nodes1.get(start1), nodes1.get(last1), nodes2.get(start2), nodes2.get(last2)};
195 } else {
196 pos = last1;
[32398]197 while (pos != firstPos && pos >= 0 && !nodes2.contains(nodes1.get(pos))) {
[32395]198 pos = incrementBy(pos, 1, nodes1.size(), isRing1);
199 }
[32398]200 if (pos < 0 || pos == firstPos || !nodes2.contains(nodes1.get(pos))) {
[32395]201 collideFound = true;
202 }
203 }
[30738]204 }
[32395]205 return null;
[30738]206 }
[32395]207
208 private static int incrementBy(int value, int increment, int limit1, boolean isRing) {
209 int result = value + increment;
[32398]210 if (result < 0)
[32395]211 return isRing ? result + limit1 : -1;
[32398]212 else if (result >= limit1)
[32395]213 return isRing ? result - limit1 : -1;
214 else
215 return result;
[30738]216 }
[32395]217
[36132]218 private static boolean areSegmentsEqual(RingSegment seg1, RingSegment seg2) {
[32395]219 List<Node> nodes1 = seg1.getNodes();
220 List<Node> nodes2 = seg2.getNodes();
221 int size = nodes1.size();
[32398]222 if (size != nodes2.size())
[32395]223 return false;
224 boolean reverse = size > 1 && !nodes1.get(0).equals(nodes2.get(0));
[32398]225 for (int i = 0; i < size; i++) {
226 if (!nodes1.get(i).equals(nodes2.get(reverse ? size-1-i : i)))
[32395]227 return false;
[32398]228 }
[32395]229 return true;
[26832]230 }
[26816]231
232 /**
233 * Split the segment in this ring at those nodes.
234 * @return The segment between nodes.
235 */
[32395]236 private RingSegment splitRingAt(int segmentIndex, Node n1, Node n2) {
[32398]237 if (n1.equals(n2))
[32395]238 throw new IllegalArgumentException("Both nodes are equal, id=" + n1.getUniqueId());
239 RingSegment segment = segments.get(segmentIndex);
240 boolean isRing = segment.isRing();
241 log("Split segment " + segment + " at nodes " + n1.getUniqueId() + " and " + n2.getUniqueId());
242 boolean reversed = segment.getNodes().indexOf(n2) < segment.getNodes().indexOf(n1);
243 if (reversed && !isRing) {
244 // order nodes
245 Node tmp = n1;
246 n1 = n2;
247 n2 = tmp;
248 }
249 RingSegment secondPart = isRing ? segment.split(n1, n2) : segment.split(n1);
250 // if secondPart == null, then n1 == firstNode
251 RingSegment thirdPart = isRing ? null : secondPart == null ? segment.split(n2) : secondPart.split(n2);
252 // if secondPart == null, then thirdPart is between n1 and n2
253 // otherwise, thirdPart is between n2 and lastNode
254 // if thirdPart == null, then n2 == lastNode
255 int pos = segmentIndex + 1;
[32398]256 if (secondPart != null) {
[32395]257 segments.add(pos++, secondPart);
258 }
[32398]259 if (thirdPart != null) {
[32395]260 segments.add(pos++, thirdPart);
261 }
262 return isRing || secondPart == null ? segment : secondPart;
[26816]263 }
264
265 /**
266 * Tries to arrange segments in order for each ring to have at least one.
267 * Also, sets source way for all rings.
[36132]268 * <p>
[26844]269 * If this method is not called, do not forget to call {@link #putSourceWayFirst()} for all rings.
[26816]270 */
[32395]271 public static void redistributeSegments(List<TheRing> rings) {
272 // build segments map
273 Map<RingSegment, TheRing> segmentMap = new HashMap<>();
[32398]274 for (TheRing ring : rings) {
275 for (RingSegment seg : ring.segments) {
276 if (!seg.isReference()) {
[32395]277 segmentMap.put(seg, ring);
278 }
[32398]279 }
[32395]280 }
[26816]281
[32395]282 // rearrange references
[36102]283 for (TheRing ring : rings) {
[32395]284 if (ring.countNonReferenceSegments() == 0) {
285 // need to find one non-reference segment
286 for (RingSegment seg : ring.segments) {
287 TheRing otherRing = segmentMap.get(seg.references);
288 if (otherRing.countNonReferenceSegments() > 1) {
289 // we could check for >0, but it is prone to deadlocking
290 seg.swapReference();
291 }
292 }
[30738]293 }
294 }
[32395]295
296 // initializing source way for each ring
[32398]297 for (TheRing ring : rings) {
[32395]298 ring.putSourceWayFirst();
[30738]299 }
300 }
[26816]301
302 private int countNonReferenceSegments() {
[32395]303 int count = 0;
[32398]304 for (RingSegment seg : segments) {
305 if (!seg.isReference()) {
[32395]306 count++;
307 }
[32398]308 }
[32395]309 return count;
[26816]310 }
311
[26844]312 public void putSourceWayFirst() {
[32395]313 for (RingSegment seg : segments) {
314 if (!seg.isReference()) {
315 seg.overrideWay(source);
316 return;
317 }
[30738]318 }
[26816]319 }
[32395]320
[26844]321 public List<Command> getCommands() {
[32395]322 return getCommands(true, null);
[26844]323 }
[32395]324
325 public List<Command> getCommands(Map<Relation, Relation> relationChangeMap) {
326 return getCommands(true, relationChangeMap);
[26888]327 }
[32395]328
[26816]329 /**
330 * Returns a list of commands to make a new relation and all newly created ways.
331 * The first way is copied from the source one, ChangeCommand is issued in this case.
332 */
[32395]333 public List<Command> getCommands(boolean createMultipolygon, Map<Relation, Relation> relationChangeMap) {
334 Way sourceCopy = new Way(source);
[36217]335 Map<String, String> tagsToRemove = new HashMap<>();
[32395]336 if (createMultipolygon) {
[36102]337 Collection<String> linearTags = Config.getPref().getList(PREF_MULTIPOLY + "lineartags",
338 CreateMultipolygonAction.DEFAULT_LINEAR_TAGS);
[32395]339 relation = new Relation();
340 relation.put("type", "multipolygon");
[36217]341 for (String key : source.keySet()) {
342 if (linearTags.contains(key)
343 || ("natural".equals(key) && "coastline".equals(source.get("natural"))))
[32395]344 continue;
[36217]345
346 relation.put(key, source.get(key));
[29459]347 sourceCopy.remove(key);
[36217]348 tagsToRemove.put(key, null);
[32395]349 }
[30738]350 }
[26816]351
[32395]352 // build a map of referencing relations
353 Map<Relation, Integer> referencingRelations = new HashMap<>();
354 List<Command> relationCommands = new ArrayList<>();
355 for (OsmPrimitive p : source.getReferrers()) {
356 if (p instanceof Relation) {
[36102]357 Relation rel;
[32395]358 if (relationChangeMap != null) {
[36217]359 rel = relationChangeMap.get(p);
360 if (rel == null) {
[32398]361 rel = new Relation((Relation) p);
362 relationChangeMap.put((Relation) p, rel);
[32395]363 }
364 } else {
[32398]365 rel = new Relation((Relation) p);
[36217]366 relationCommands.add(new ChangeCommand(p, rel)); // should not happen
[32395]367 }
[32398]368 for (int i = 0; i < rel.getMembersCount(); i++) {
369 if (rel.getMember(i).getMember().equals(source)) {
[36102]370 referencingRelations.put(rel, i);
[32395]371 }
[32398]372 }
[30738]373 }
374 }
[26816]375
[33694]376 DataSet ds = MainApplication.getLayerManager().getEditDataSet();
[32395]377 List<Command> commands = new ArrayList<>();
378 boolean foundOwnWay = false;
379 for (RingSegment seg : segments) {
380 boolean needAdding = !seg.isWayConstructed();
381 Way w = seg.constructWay(seg.isReference() ? null : sourceCopy);
[32398]382 if (needAdding) {
[33694]383 commands.add(new AddCommand(ds, w));
[32395]384 }
385 if (w.equals(source)) {
386 if (createMultipolygon || !seg.getWayNodes().equals(source.getNodes())) {
387 sourceCopy.setNodes(seg.getWayNodes());
[36217]388 if (!tagsToRemove.isEmpty()) {
389 commands.add(new ChangePropertyCommand(Collections.singleton(source), tagsToRemove));
390 }
391 if (!sourceCopy.getNodes().equals(source.getNodes()))
392 commands.add(new ChangeNodesCommand(source, sourceCopy.getNodes()));
[32395]393 }
394 foundOwnWay = true;
395 } else {
[36132]396 for (Map.Entry<Relation, Integer> entry : referencingRelations.entrySet()) {
397 Relation rel = entry.getKey();
398 int relIndex = entry.getValue();
[32395]399 rel.addMember(new RelationMember(rel.getMember(relIndex).getRole(), w));
400 }
401 }
[32398]402 if (createMultipolygon) {
[32395]403 relation.addMember(new RelationMember("outer", w));
404 }
[30738]405 }
[36217]406 sourceCopy.setNodes(null); // see #19885
[32398]407 if (!foundOwnWay) {
[36134]408 final Command deleteCommand = DeleteCommand.delete(Collections.singleton(source));
409 if (deleteCommand != null) {
410 commands.add(deleteCommand);
411 }
[30738]412 }
[32395]413 commands.addAll(relationCommands);
[32398]414 if (createMultipolygon) {
[33694]415 commands.add(new AddCommand(ds, relation));
[30738]416 }
[32395]417 return commands;
[26816]418 }
[32395]419
420 public static void updateCommandsWithRelations(List<Command> commands, Map<Relation, Relation> relationCache) {
[36132]421 for (Map.Entry<Relation, Relation> entry : relationCache.entrySet()) {
[36217]422 Relation oldRel = entry.getKey();
423 Relation newRel = entry.getValue();
424 if (oldRel.getKeys().equals(newRel.getKeys())) {
425 commands.add(new ChangeMembersCommand(oldRel, newRel.getMembers()));
426 newRel.setMembers(null); // see #19885
427 } else {
428 commands.add(new ChangeCommand(oldRel, newRel)); // should not happen
429 }
[32395]430 }
[30738]431 }
[26816]432
433 /**
434 * Returns the relation created in {@link #getCommands()}.
435 */
436 public Relation getRelation() {
[32395]437 return relation;
[26816]438 }
439
440 @Override
441 public String toString() {
[32395]442 StringBuilder sb = new StringBuilder("TheRing@");
443 sb.append(this.hashCode()).append('[').append("wayId: ").append(source == null ? "null" : source.getUniqueId()).append("; segments: ");
[32398]444 if (segments.isEmpty()) {
[32395]445 sb.append("empty");
446 } else {
447 sb.append(segments.get(0));
[32398]448 for (int i = 1; i < segments.size(); i++) {
[32395]449 sb.append(", ").append(segments.get(i));
450 }
451 }
452 return sb.append(']').toString();
[26816]453 }
454
[32395]455 private static void log(String s) {
[33530]456 Logging.debug(s);
[26919]457 }
[32395]458
[26816]459 private static class RingSegment {
[32395]460 private List<Node> nodes;
461 private RingSegment references;
[36132]462 private Way resultingWay;
463 private boolean wasTemplateApplied;
[32395]464 private boolean isRing;
[26816]465
[32398]466 RingSegment(Way w) {
[32395]467 this(w.getNodes());
468 }
[26816]469
[32398]470 RingSegment(List<Node> nodes) {
[32395]471 this.nodes = nodes;
472 isRing = nodes.size() > 1 && nodes.get(0).equals(nodes.get(nodes.size() - 1));
[32398]473 if (isRing) {
[32395]474 nodes.remove(nodes.size() - 1);
475 }
476 references = null;
477 }
[26816]478
[32395]479 /**
480 * Splits this segment at node n. Retains nodes 0..n and moves
481 * nodes n..N to a separate segment that is returned.
482 * @param n node at which to split.
483 * @return new segment, {@code null} if splitting is unnecessary.
484 */
485 public RingSegment split(Node n) {
[32398]486 if (nodes == null)
[32395]487 throw new IllegalArgumentException("Cannot split segment: it is a reference");
488 int pos = nodes.indexOf(n);
[32398]489 if (pos <= 0 || pos >= nodes.size() - 1)
[32395]490 return null;
491 List<Node> newNodes = new ArrayList<>(nodes.subList(pos, nodes.size()));
492 nodes.subList(pos + 1, nodes.size()).clear();
493 return new RingSegment(newNodes);
494 }
[26816]495
[32395]496 /**
497 * Split this segment as a way at two nodes. If one of them is null or at the end,
498 * split as an arc. Note: order of nodes is important.
499 * @return A new segment from n2 to n1.
500 */
501 public RingSegment split(Node n1, Node n2) {
[32398]502 if (nodes == null)
[32395]503 throw new IllegalArgumentException("Cannot split segment: it is a reference");
504 if (!isRing) {
[32398]505 if (n1 == null || nodes.get(0).equals(n1) || nodes.get(nodes.size() - 1).equals(n1))
[32395]506 return split(n2);
[32398]507 if (n2 == null || nodes.get(0).equals(n2) || nodes.get(nodes.size() - 1).equals(n2))
[32395]508 return split(n1);
509 throw new IllegalArgumentException("Split for two nodes is called for not-ring: " + this);
510 }
511 int pos1 = nodes.indexOf(n1);
512 int pos2 = nodes.indexOf(n2);
[32398]513 if (pos1 == pos2)
[32395]514 return null;
515
516 List<Node> newNodes = new ArrayList<>();
517 if (pos2 > pos1) {
518 newNodes.addAll(nodes.subList(pos2, nodes.size()));
519 newNodes.addAll(nodes.subList(0, pos1 + 1));
[32398]520 if (pos2 + 1 < nodes.size()) {
[32395]521 nodes.subList(pos2 + 1, nodes.size()).clear();
522 }
[32398]523 if (pos1 > 0) {
[32395]524 nodes.subList(0, pos1).clear();
525 }
526 } else {
527 newNodes.addAll(nodes.subList(pos2, pos1 + 1));
528 nodes.addAll(new ArrayList<>(nodes.subList(0, pos2 + 1)));
529 nodes.subList(0, pos1).clear();
530 }
531 isRing = false;
532 return new RingSegment(newNodes);
[30738]533 }
[26816]534
[32395]535 public List<Node> getNodes() {
536 return nodes == null ? references.nodes : nodes;
[30738]537 }
[26816]538
[32395]539 public List<Node> getWayNodes() {
[32398]540 if (nodes == null)
[32395]541 throw new IllegalArgumentException("Won't give you wayNodes: it is a reference");
542 List<Node> wayNodes = new ArrayList<>(nodes);
[32398]543 if (isRing) {
[32395]544 wayNodes.add(wayNodes.get(0));
545 }
546 return wayNodes;
547 }
[26816]548
[32395]549 public boolean isReference() {
550 return nodes == null;
551 }
[26844]552
[32395]553 public boolean isRing() {
554 return isRing;
555 }
[26816]556
[32395]557 public void makeReference(RingSegment segment) {
558 log(this + " was made a reference to " + segment);
559 this.nodes = null;
560 this.references = segment;
561 }
[26816]562
[32395]563 public void swapReference() {
564 this.nodes = references.nodes;
565 references.nodes = null;
566 references.references = this;
567 this.references = null;
568 }
[26816]569
[32395]570 public boolean isWayConstructed() {
571 return isReference() ? references.isWayConstructed() : resultingWay != null;
572 }
[26816]573
[32395]574 public Way constructWay(Way template) {
[32398]575 if (isReference())
[32395]576 return references.constructWay(template);
577 if (resultingWay == null) {
578 resultingWay = new Way();
579 resultingWay.setNodes(getWayNodes());
580 }
581 if (template != null && !wasTemplateApplied) {
582 resultingWay.setKeys(template.getKeys());
583 wasTemplateApplied = true;
584 }
585 return resultingWay;
[30738]586 }
[26816]587
[32395]588 public void overrideWay(Way source) {
[32398]589 if (isReference()) {
[32395]590 references.overrideWay(source);
591 } else {
592 resultingWay = source;
593 wasTemplateApplied = true;
594 }
[30738]595 }
[26816]596
[32395]597 @Override
598 public String toString() {
599 StringBuilder sb = new StringBuilder("RingSegment@");
600 sb.append(this.hashCode()).append('[');
[32398]601 if (isReference()) {
[32395]602 sb.append("references ").append(references.hashCode());
[32398]603 } else if (nodes.isEmpty()) {
[32395]604 sb.append("empty");
605 } else {
[32398]606 if (isRing) {
[32395]607 sb.append("ring:");
608 }
609 sb.append(nodes.get(0).getUniqueId());
[32398]610 for (int i = 1; i < nodes.size(); i++) {
[32395]611 sb.append(',').append(nodes.get(i).getUniqueId());
612 }
613 }
614 return sb.append(']').toString();
[30738]615 }
[26816]616 }
617}
Note: See TracBrowser for help on using the repository browser.