source: josm/trunk/src/org/openstreetmap/josm/data/osm/Way.java@ 12809

Last change on this file since 12809 was 12809, checked in by bastiK, 7 years ago

replace abstract class AbstractVisitor by interface OsmPrimitiveVisitor; deprecate Visitor

  • data.osm.visitor.Visitor awkwardly mixes OsmPrimitive types and Changeset class; this may have been used in the past, but is no longer needed; AbstractVisitor should have been a super-interface of Visitor in the first place
  • hopefully, this is binary compatible and plugins can be updated gracefully
  • Property svn:eol-style set to native
File size: 24.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.HashSet;
9import java.util.List;
10import java.util.Map;
11import java.util.Set;
12
13import org.openstreetmap.josm.Main;
14import org.openstreetmap.josm.data.coor.LatLon;
15import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
16import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
17import org.openstreetmap.josm.tools.CopyList;
18import org.openstreetmap.josm.tools.Pair;
19import org.openstreetmap.josm.tools.Utils;
20
21/**
22 * One full way, consisting of a list of way {@link Node nodes}.
23 *
24 * @author imi
25 * @since 64
26 */
27public final class Way extends OsmPrimitive implements IWay {
28
29 /**
30 * All way nodes in this way
31 *
32 */
33 private Node[] nodes = new Node[0];
34 private BBox bbox;
35
36 /**
37 *
38 * You can modify returned list but changes will not be propagated back
39 * to the Way. Use {@link #setNodes(List)} to update this way
40 * @return Nodes composing the way
41 * @since 1862
42 */
43 public List<Node> getNodes() {
44 return new CopyList<>(nodes);
45 }
46
47 /**
48 * Set new list of nodes to way. This method is preferred to multiple calls to addNode/removeNode
49 * and similar methods because nodes are internally saved as array which means lower memory overhead
50 * but also slower modifying operations.
51 * @param nodes New way nodes. Can be null, in that case all way nodes are removed
52 * @since 1862
53 */
54 public void setNodes(List<Node> nodes) {
55 boolean locked = writeLock();
56 try {
57 for (Node node:this.nodes) {
58 node.removeReferrer(this);
59 node.clearCachedStyle();
60 }
61
62 if (nodes == null) {
63 this.nodes = new Node[0];
64 } else {
65 this.nodes = nodes.toArray(new Node[nodes.size()]);
66 }
67 for (Node node: this.nodes) {
68 node.addReferrer(this);
69 node.clearCachedStyle();
70 }
71
72 clearCachedStyle();
73 fireNodesChanged();
74 } finally {
75 writeUnlock(locked);
76 }
77 }
78
79 /**
80 * Prevent directly following identical nodes in ways.
81 * @param nodes list of nodes
82 * @return {@code nodes} with consecutive identical nodes removed
83 */
84 private static List<Node> removeDouble(List<Node> nodes) {
85 Node last = null;
86 int count = nodes.size();
87 for (int i = 0; i < count && count > 2;) {
88 Node n = nodes.get(i);
89 if (last == n) {
90 nodes.remove(i);
91 --count;
92 } else {
93 last = n;
94 ++i;
95 }
96 }
97 return nodes;
98 }
99
100 @Override
101 public int getNodesCount() {
102 return nodes.length;
103 }
104
105 /**
106 * Replies the real number of nodes in this way (full number of nodes minus one if this way is closed)
107 *
108 * @return the real number of nodes in this way.
109 *
110 * @see #getNodesCount()
111 * @see #isClosed()
112 * @since 5847
113 */
114 public int getRealNodesCount() {
115 int count = getNodesCount();
116 return isClosed() ? count-1 : count;
117 }
118
119 /**
120 * Replies the node at position <code>index</code>.
121 *
122 * @param index the position
123 * @return the node at position <code>index</code>
124 * @throws IndexOutOfBoundsException if <code>index</code> &lt; 0
125 * or <code>index</code> &gt;= {@link #getNodesCount()}
126 * @since 1862
127 */
128 public Node getNode(int index) {
129 return nodes[index];
130 }
131
132 @Override
133 public long getNodeId(int idx) {
134 return nodes[idx].getUniqueId();
135 }
136
137 /**
138 * Replies true if this way contains the node <code>node</code>, false
139 * otherwise. Replies false if <code>node</code> is null.
140 *
141 * @param node the node. May be null.
142 * @return true if this way contains the node <code>node</code>, false
143 * otherwise
144 * @since 1911
145 */
146 public boolean containsNode(Node node) {
147 if (node == null) return false;
148
149 Node[] nodes = this.nodes;
150 for (Node n : nodes) {
151 if (n.equals(node))
152 return true;
153 }
154 return false;
155 }
156
157 /**
158 * Return nodes adjacent to <code>node</code>
159 *
160 * @param node the node. May be null.
161 * @return Set of nodes adjacent to <code>node</code>
162 * @since 4671
163 */
164 public Set<Node> getNeighbours(Node node) {
165 Set<Node> neigh = new HashSet<>();
166
167 if (node == null) return neigh;
168
169 Node[] nodes = this.nodes;
170 for (int i = 0; i < nodes.length; i++) {
171 if (nodes[i].equals(node)) {
172 if (i > 0)
173 neigh.add(nodes[i-1]);
174 if (i < nodes.length-1)
175 neigh.add(nodes[i+1]);
176 }
177 }
178 return neigh;
179 }
180
181 /**
182 * Replies the ordered {@link List} of chunks of this way. Each chunk is replied as a {@link Pair} of {@link Node nodes}.
183 * @param sort If true, the nodes of each pair are sorted as defined by {@link Pair#sort}.
184 * If false, Pair.a and Pair.b are in the way order
185 * (i.e for a given Pair(n), Pair(n-1).b == Pair(n).a, Pair(n).b == Pair(n+1).a, etc.)
186 * @return The ordered list of chunks of this way.
187 * @since 3348
188 */
189 public List<Pair<Node, Node>> getNodePairs(boolean sort) {
190 List<Pair<Node, Node>> chunkSet = new ArrayList<>();
191 if (isIncomplete()) return chunkSet;
192 Node lastN = null;
193 Node[] nodes = this.nodes;
194 for (Node n : nodes) {
195 if (lastN == null) {
196 lastN = n;
197 continue;
198 }
199 Pair<Node, Node> np = new Pair<>(lastN, n);
200 if (sort) {
201 Pair.sort(np);
202 }
203 chunkSet.add(np);
204 lastN = n;
205 }
206 return chunkSet;
207 }
208
209 @Deprecated
210 @Override public void accept(org.openstreetmap.josm.data.osm.visitor.Visitor visitor) {
211 visitor.visit(this);
212 }
213
214 @Override public void accept(OsmPrimitiveVisitor visitor) {
215 visitor.visit(this);
216 }
217
218 @Override public void accept(PrimitiveVisitor visitor) {
219 visitor.visit(this);
220 }
221
222 protected Way(long id, boolean allowNegative) {
223 super(id, allowNegative);
224 }
225
226 /**
227 * Contructs a new {@code Way} with id 0.
228 * @since 86
229 */
230 public Way() {
231 super(0, false);
232 }
233
234 /**
235 * Contructs a new {@code Way} from an existing {@code Way}.
236 * @param original The original {@code Way} to be identically cloned. Must not be null
237 * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
238 * If {@code false}, does nothing
239 * @since 2410
240 */
241 public Way(Way original, boolean clearMetadata) {
242 super(original.getUniqueId(), true);
243 cloneFrom(original);
244 if (clearMetadata) {
245 clearOsmMetadata();
246 }
247 }
248
249 /**
250 * Contructs a new {@code Way} from an existing {@code Way} (including its id).
251 * @param original The original {@code Way} to be identically cloned. Must not be null
252 * @since 86
253 */
254 public Way(Way original) {
255 this(original, false);
256 }
257
258 /**
259 * Contructs a new {@code Way} for the given id. If the id &gt; 0, the way is marked
260 * as incomplete. If id == 0 then way is marked as new
261 *
262 * @param id the id. &gt;= 0 required
263 * @throws IllegalArgumentException if id &lt; 0
264 * @since 343
265 */
266 public Way(long id) {
267 super(id, false);
268 }
269
270 /**
271 * Contructs a new {@code Way} with given id and version.
272 * @param id the id. &gt;= 0 required
273 * @param version the version
274 * @throws IllegalArgumentException if id &lt; 0
275 * @since 2620
276 */
277 public Way(long id, int version) {
278 super(id, version, false);
279 }
280
281 @Override
282 public void load(PrimitiveData data) {
283 if (!(data instanceof WayData))
284 throw new IllegalArgumentException("Not a way data: " + data);
285 boolean locked = writeLock();
286 try {
287 super.load(data);
288
289 WayData wayData = (WayData) data;
290
291 if (!wayData.getNodes().isEmpty() && getDataSet() == null) {
292 throw new AssertionError("Data consistency problem - way without dataset detected");
293 }
294
295 List<Node> newNodes = new ArrayList<>(wayData.getNodes().size());
296 for (Long nodeId : wayData.getNodes()) {
297 Node node = (Node) getDataSet().getPrimitiveById(nodeId, OsmPrimitiveType.NODE);
298 if (node != null) {
299 newNodes.add(node);
300 } else {
301 throw new AssertionError("Data consistency problem - way with missing node detected");
302 }
303 }
304 setNodes(newNodes);
305 } finally {
306 writeUnlock(locked);
307 }
308 }
309
310 @Override
311 public WayData save() {
312 WayData data = new WayData();
313 saveCommonAttributes(data);
314 for (Node node:nodes) {
315 data.getNodes().add(node.getUniqueId());
316 }
317 return data;
318 }
319
320 @Override
321 public void cloneFrom(OsmPrimitive osm) {
322 if (!(osm instanceof Way))
323 throw new IllegalArgumentException("Not a way: " + osm);
324 boolean locked = writeLock();
325 try {
326 super.cloneFrom(osm);
327 Way otherWay = (Way) osm;
328 setNodes(otherWay.getNodes());
329 } finally {
330 writeUnlock(locked);
331 }
332 }
333
334 @Override
335 public String toString() {
336 String nodesDesc = isIncomplete() ? "(incomplete)" : ("nodes=" + Arrays.toString(nodes));
337 return "{Way id=" + getUniqueId() + " version=" + getVersion()+ ' ' + getFlagsAsString() + ' ' + nodesDesc + '}';
338 }
339
340 @Override
341 public boolean hasEqualSemanticAttributes(OsmPrimitive other, boolean testInterestingTagsOnly) {
342 if (!(other instanceof Way))
343 return false;
344 Way w = (Way) other;
345 if (getNodesCount() != w.getNodesCount()) return false;
346 if (!super.hasEqualSemanticAttributes(other, testInterestingTagsOnly))
347 return false;
348 for (int i = 0; i < getNodesCount(); i++) {
349 if (!getNode(i).hasEqualSemanticAttributes(w.getNode(i)))
350 return false;
351 }
352 return true;
353 }
354
355 @Override
356 public int compareTo(OsmPrimitive o) {
357 if (o instanceof Relation)
358 return 1;
359 return o instanceof Way ? Long.compare(getUniqueId(), o.getUniqueId()) : -1;
360 }
361
362 /**
363 * Removes the given {@link Node} from this way. Ignored, if n is null.
364 * @param n The node to remove. Ignored, if null
365 * @since 1463
366 */
367 public void removeNode(Node n) {
368 if (n == null || isIncomplete()) return;
369 boolean locked = writeLock();
370 try {
371 boolean closed = lastNode() == n && firstNode() == n;
372 int i;
373 List<Node> copy = getNodes();
374 while ((i = copy.indexOf(n)) >= 0) {
375 copy.remove(i);
376 }
377 i = copy.size();
378 if (closed && i > 2) {
379 copy.add(copy.get(0));
380 } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
381 copy.remove(i-1);
382 }
383 setNodes(removeDouble(copy));
384 n.clearCachedStyle();
385 } finally {
386 writeUnlock(locked);
387 }
388 }
389
390 /**
391 * Removes the given set of {@link Node nodes} from this way. Ignored, if selection is null.
392 * @param selection The selection of nodes to remove. Ignored, if null
393 * @since 5408
394 */
395 public void removeNodes(Set<? extends Node> selection) {
396 if (selection == null || isIncomplete()) return;
397 boolean locked = writeLock();
398 try {
399 boolean closed = isClosed() && selection.contains(lastNode());
400 List<Node> copy = new ArrayList<>();
401
402 for (Node n: nodes) {
403 if (!selection.contains(n)) {
404 copy.add(n);
405 }
406 }
407
408 int i = copy.size();
409 if (closed && i > 2) {
410 copy.add(copy.get(0));
411 } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
412 copy.remove(i-1);
413 }
414 setNodes(removeDouble(copy));
415 for (Node n : selection) {
416 n.clearCachedStyle();
417 }
418 } finally {
419 writeUnlock(locked);
420 }
421 }
422
423 /**
424 * Adds a node to the end of the list of nodes. Ignored, if n is null.
425 *
426 * @param n the node. Ignored, if null
427 * @throws IllegalStateException if this way is marked as incomplete. We can't add a node
428 * to an incomplete way
429 * @since 1313
430 */
431 public void addNode(Node n) {
432 if (n == null) return;
433
434 boolean locked = writeLock();
435 try {
436 if (isIncomplete())
437 throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
438 clearCachedStyle();
439 n.addReferrer(this);
440 nodes = Utils.addInArrayCopy(nodes, n);
441 n.clearCachedStyle();
442 fireNodesChanged();
443 } finally {
444 writeUnlock(locked);
445 }
446 }
447
448 /**
449 * Adds a node at position offs.
450 *
451 * @param offs the offset
452 * @param n the node. Ignored, if null.
453 * @throws IllegalStateException if this way is marked as incomplete. We can't add a node
454 * to an incomplete way
455 * @throws IndexOutOfBoundsException if offs is out of bounds
456 * @since 1313
457 */
458 public void addNode(int offs, Node n) {
459 if (n == null) return;
460
461 boolean locked = writeLock();
462 try {
463 if (isIncomplete())
464 throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
465
466 clearCachedStyle();
467 n.addReferrer(this);
468 Node[] newNodes = new Node[nodes.length + 1];
469 System.arraycopy(nodes, 0, newNodes, 0, offs);
470 System.arraycopy(nodes, offs, newNodes, offs + 1, nodes.length - offs);
471 newNodes[offs] = n;
472 nodes = newNodes;
473 n.clearCachedStyle();
474 fireNodesChanged();
475 } finally {
476 writeUnlock(locked);
477 }
478 }
479
480 @Override
481 public void setDeleted(boolean deleted) {
482 boolean locked = writeLock();
483 try {
484 for (Node n:nodes) {
485 if (deleted) {
486 n.removeReferrer(this);
487 } else {
488 n.addReferrer(this);
489 }
490 n.clearCachedStyle();
491 }
492 fireNodesChanged();
493 super.setDeleted(deleted);
494 } finally {
495 writeUnlock(locked);
496 }
497 }
498
499 @Override
500 public boolean isClosed() {
501 if (isIncomplete()) return false;
502
503 Node[] nodes = this.nodes;
504 return nodes.length >= 3 && nodes[nodes.length-1] == nodes[0];
505 }
506
507 /**
508 * Determines if this way denotes an area (closed way with at least three distinct nodes).
509 * @return {@code true} if this way is closed and contains at least three distinct nodes
510 * @see #isClosed
511 * @since 5490
512 */
513 public boolean isArea() {
514 if (this.nodes.length >= 4 && isClosed()) {
515 Node distinctNode = null;
516 for (int i = 1; i < nodes.length-1; i++) {
517 if (distinctNode == null && nodes[i] != nodes[0]) {
518 distinctNode = nodes[i];
519 } else if (distinctNode != null && nodes[i] != nodes[0] && nodes[i] != distinctNode) {
520 return true;
521 }
522 }
523 }
524 return false;
525 }
526
527 /**
528 * Returns the last node of this way.
529 * The result equals <tt>{@link #getNode getNode}({@link #getNodesCount getNodesCount} - 1)</tt>.
530 * @return the last node of this way
531 * @since 1400
532 */
533 public Node lastNode() {
534 Node[] nodes = this.nodes;
535 if (isIncomplete() || nodes.length == 0) return null;
536 return nodes[nodes.length-1];
537 }
538
539 /**
540 * Returns the first node of this way.
541 * The result equals {@link #getNode getNode}{@code (0)}.
542 * @return the first node of this way
543 * @since 1400
544 */
545 public Node firstNode() {
546 Node[] nodes = this.nodes;
547 if (isIncomplete() || nodes.length == 0) return null;
548 return nodes[0];
549 }
550
551 /**
552 * Replies true if the given node is the first or the last one of this way, false otherwise.
553 * @param n The node to test
554 * @return true if the {@code n} is the first or the last node, false otherwise.
555 * @since 1400
556 */
557 public boolean isFirstLastNode(Node n) {
558 Node[] nodes = this.nodes;
559 if (isIncomplete() || nodes.length == 0) return false;
560 return n == nodes[0] || n == nodes[nodes.length -1];
561 }
562
563 /**
564 * Replies true if the given node is an inner node of this way, false otherwise.
565 * @param n The node to test
566 * @return true if the {@code n} is an inner node, false otherwise.
567 * @since 3515
568 */
569 public boolean isInnerNode(Node n) {
570 Node[] nodes = this.nodes;
571 if (isIncomplete() || nodes.length <= 2) return false;
572 /* circular ways have only inner nodes, so return true for them! */
573 if (n == nodes[0] && n == nodes[nodes.length-1]) return true;
574 for (int i = 1; i < nodes.length - 1; ++i) {
575 if (nodes[i] == n) return true;
576 }
577 return false;
578 }
579
580 @Override
581 public String getDisplayName(NameFormatter formatter) {
582 return formatter.format(this);
583 }
584
585 @Override
586 public OsmPrimitiveType getType() {
587 return OsmPrimitiveType.WAY;
588 }
589
590 @Override
591 public OsmPrimitiveType getDisplayType() {
592 return isClosed() ? OsmPrimitiveType.CLOSEDWAY : OsmPrimitiveType.WAY;
593 }
594
595 private void checkNodes() {
596 DataSet dataSet = getDataSet();
597 if (dataSet != null) {
598 Node[] nodes = this.nodes;
599 for (Node n: nodes) {
600 if (n.getDataSet() != dataSet)
601 throw new DataIntegrityProblemException("Nodes in way must be in the same dataset",
602 tr("Nodes in way must be in the same dataset"));
603 if (n.isDeleted())
604 throw new DataIntegrityProblemException("Deleted node referenced: " + toString(),
605 "<html>" + tr("Deleted node referenced by {0}",
606 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(this)) + "</html>");
607 }
608 if (Main.pref.getBoolean("debug.checkNullCoor", true)) {
609 for (Node n: nodes) {
610 if (n.isVisible() && !n.isIncomplete() && !n.isLatLonKnown())
611 throw new DataIntegrityProblemException("Complete visible node with null coordinates: " + toString(),
612 "<html>" + tr("Complete node {0} with null coordinates in way {1}",
613 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(n),
614 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(this)) + "</html>");
615 }
616 }
617 }
618 }
619
620 private void fireNodesChanged() {
621 checkNodes();
622 if (getDataSet() != null) {
623 getDataSet().fireWayNodesChanged(this);
624 }
625 }
626
627 @Override
628 void setDataset(DataSet dataSet) {
629 super.setDataset(dataSet);
630 checkNodes();
631 }
632
633 @Override
634 public BBox getBBox() {
635 if (getDataSet() == null)
636 return new BBox(this);
637 if (bbox == null) {
638 bbox = new BBox(this);
639 }
640 return new BBox(bbox);
641 }
642
643 @Override
644 protected void addToBBox(BBox box, Set<PrimitiveId> visited) {
645 box.add(getBBox());
646 }
647
648 @Override
649 public void updatePosition() {
650 bbox = new BBox(this);
651 }
652
653 /**
654 * Replies true if this way has incomplete nodes, false otherwise.
655 * @return true if this way has incomplete nodes, false otherwise.
656 * @since 2587
657 */
658 public boolean hasIncompleteNodes() {
659 Node[] nodes = this.nodes;
660 for (Node node : nodes) {
661 if (node.isIncomplete())
662 return true;
663 }
664 return false;
665 }
666
667 @Override
668 public boolean isUsable() {
669 return super.isUsable() && !hasIncompleteNodes();
670 }
671
672 @Override
673 public boolean isDrawable() {
674 return super.isDrawable() && !hasIncompleteNodes();
675 }
676
677 /**
678 * Replies the length of the way, in metres, as computed by {@link LatLon#greatCircleDistance}.
679 * @return The length of the way, in metres
680 * @since 4138
681 */
682 public double getLength() {
683 double length = 0;
684 Node lastN = null;
685 for (Node n:nodes) {
686 if (lastN != null) {
687 LatLon lastNcoor = lastN.getCoor();
688 LatLon coor = n.getCoor();
689 if (lastNcoor != null && coor != null) {
690 length += coor.greatCircleDistance(lastNcoor);
691 }
692 }
693 lastN = n;
694 }
695 return length;
696 }
697
698 /**
699 * Replies the length of the longest segment of the way, in metres, as computed by {@link LatLon#greatCircleDistance}.
700 * @return The length of the segment, in metres
701 * @since 8320
702 */
703 public double getLongestSegmentLength() {
704 double length = 0;
705 Node lastN = null;
706 for (Node n:nodes) {
707 if (lastN != null) {
708 LatLon lastNcoor = lastN.getCoor();
709 LatLon coor = n.getCoor();
710 if (lastNcoor != null && coor != null) {
711 double l = coor.greatCircleDistance(lastNcoor);
712 if (l > length) {
713 length = l;
714 }
715 }
716 }
717 lastN = n;
718 }
719 return length;
720 }
721
722 /**
723 * Tests if this way is a oneway.
724 * @return {@code 1} if the way is a oneway,
725 * {@code -1} if the way is a reversed oneway,
726 * {@code 0} otherwise.
727 * @since 5199
728 */
729 public int isOneway() {
730 String oneway = get("oneway");
731 if (oneway != null) {
732 if ("-1".equals(oneway)) {
733 return -1;
734 } else {
735 Boolean isOneway = OsmUtils.getOsmBoolean(oneway);
736 if (isOneway != null && isOneway) {
737 return 1;
738 }
739 }
740 }
741 return 0;
742 }
743
744 /**
745 * Replies the first node of this way, respecting or not its oneway state.
746 * @param respectOneway If true and if this way is a reversed oneway, replies the last node. Otherwise, replies the first node.
747 * @return the first node of this way, according to {@code respectOneway} and its oneway state.
748 * @since 5199
749 */
750 public Node firstNode(boolean respectOneway) {
751 return !respectOneway || isOneway() != -1 ? firstNode() : lastNode();
752 }
753
754 /**
755 * Replies the last node of this way, respecting or not its oneway state.
756 * @param respectOneway If true and if this way is a reversed oneway, replies the first node. Otherwise, replies the last node.
757 * @return the last node of this way, according to {@code respectOneway} and its oneway state.
758 * @since 5199
759 */
760 public Node lastNode(boolean respectOneway) {
761 return !respectOneway || isOneway() != -1 ? lastNode() : firstNode();
762 }
763
764 @Override
765 public boolean concernsArea() {
766 return hasAreaTags();
767 }
768
769 @Override
770 public boolean isOutsideDownloadArea() {
771 for (final Node n : nodes) {
772 if (n.isOutsideDownloadArea()) {
773 return true;
774 }
775 }
776 return false;
777 }
778
779 @Override
780 protected void keysChangedImpl(Map<String, String> originalKeys) {
781 super.keysChangedImpl(originalKeys);
782 clearCachedNodeStyles();
783 }
784
785 /**
786 * Clears all cached styles for all nodes of this way. This should not be called from outside.
787 * @see Node#clearCachedStyle()
788 */
789 public void clearCachedNodeStyles() {
790 for (final Node n : nodes) {
791 n.clearCachedStyle();
792 }
793 }
794}
Note: See TracBrowser for help on using the repository browser.