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

Last change on this file since 13207 was 13206, checked in by Don-vip, 6 years ago

enable PMD rule OptimizableToArrayCall

  • Property svn:eol-style set to native
File size: 24.7 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.data.coor.LatLon;
14import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
15import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
16import org.openstreetmap.josm.spi.preferences.Config;
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[0]);
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 @Override public void accept(OsmPrimitiveVisitor visitor) {
210 visitor.visit(this);
211 }
212
213 @Override public void accept(PrimitiveVisitor visitor) {
214 visitor.visit(this);
215 }
216
217 protected Way(long id, boolean allowNegative) {
218 super(id, allowNegative);
219 }
220
221 /**
222 * Contructs a new {@code Way} with id 0.
223 * @since 86
224 */
225 public Way() {
226 super(0, false);
227 }
228
229 /**
230 * Contructs a new {@code Way} from an existing {@code Way}.
231 * @param original The original {@code Way} to be identically cloned. Must not be null
232 * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
233 * If {@code false}, does nothing
234 * @since 2410
235 */
236 public Way(Way original, boolean clearMetadata) {
237 super(original.getUniqueId(), true);
238 cloneFrom(original);
239 if (clearMetadata) {
240 clearOsmMetadata();
241 }
242 }
243
244 /**
245 * Contructs a new {@code Way} from an existing {@code Way} (including its id).
246 * @param original The original {@code Way} to be identically cloned. Must not be null
247 * @since 86
248 */
249 public Way(Way original) {
250 this(original, false);
251 }
252
253 /**
254 * Contructs a new {@code Way} for the given id. If the id &gt; 0, the way is marked
255 * as incomplete. If id == 0 then way is marked as new
256 *
257 * @param id the id. &gt;= 0 required
258 * @throws IllegalArgumentException if id &lt; 0
259 * @since 343
260 */
261 public Way(long id) {
262 super(id, false);
263 }
264
265 /**
266 * Contructs a new {@code Way} with given id and version.
267 * @param id the id. &gt;= 0 required
268 * @param version the version
269 * @throws IllegalArgumentException if id &lt; 0
270 * @since 2620
271 */
272 public Way(long id, int version) {
273 super(id, version, false);
274 }
275
276 @Override
277 public void load(PrimitiveData data) {
278 if (!(data instanceof WayData))
279 throw new IllegalArgumentException("Not a way data: " + data);
280 boolean locked = writeLock();
281 try {
282 super.load(data);
283
284 WayData wayData = (WayData) data;
285
286 if (!wayData.getNodes().isEmpty() && getDataSet() == null) {
287 throw new AssertionError("Data consistency problem - way without dataset detected");
288 }
289
290 List<Node> newNodes = new ArrayList<>(wayData.getNodes().size());
291 for (Long nodeId : wayData.getNodes()) {
292 Node node = (Node) getDataSet().getPrimitiveById(nodeId, OsmPrimitiveType.NODE);
293 if (node != null) {
294 newNodes.add(node);
295 } else {
296 throw new AssertionError("Data consistency problem - way with missing node detected");
297 }
298 }
299 setNodes(newNodes);
300 } finally {
301 writeUnlock(locked);
302 }
303 }
304
305 @Override
306 public WayData save() {
307 WayData data = new WayData();
308 saveCommonAttributes(data);
309 for (Node node:nodes) {
310 data.getNodes().add(node.getUniqueId());
311 }
312 return data;
313 }
314
315 @Override
316 public void cloneFrom(OsmPrimitive osm) {
317 if (!(osm instanceof Way))
318 throw new IllegalArgumentException("Not a way: " + osm);
319 boolean locked = writeLock();
320 try {
321 super.cloneFrom(osm);
322 Way otherWay = (Way) osm;
323 setNodes(otherWay.getNodes());
324 } finally {
325 writeUnlock(locked);
326 }
327 }
328
329 @Override
330 public String toString() {
331 String nodesDesc = isIncomplete() ? "(incomplete)" : ("nodes=" + Arrays.toString(nodes));
332 return "{Way id=" + getUniqueId() + " version=" + getVersion()+ ' ' + getFlagsAsString() + ' ' + nodesDesc + '}';
333 }
334
335 @Override
336 public boolean hasEqualSemanticAttributes(OsmPrimitive other, boolean testInterestingTagsOnly) {
337 if (!(other instanceof Way))
338 return false;
339 Way w = (Way) other;
340 if (getNodesCount() != w.getNodesCount()) return false;
341 if (!super.hasEqualSemanticAttributes(other, testInterestingTagsOnly))
342 return false;
343 for (int i = 0; i < getNodesCount(); i++) {
344 if (!getNode(i).hasEqualSemanticAttributes(w.getNode(i)))
345 return false;
346 }
347 return true;
348 }
349
350 @Override
351 public int compareTo(OsmPrimitive o) {
352 if (o instanceof Relation)
353 return 1;
354 return o instanceof Way ? Long.compare(getUniqueId(), o.getUniqueId()) : -1;
355 }
356
357 /**
358 * Removes the given {@link Node} from this way. Ignored, if n is null.
359 * @param n The node to remove. Ignored, if null
360 * @since 1463
361 */
362 public void removeNode(Node n) {
363 if (n == null || isIncomplete()) return;
364 boolean locked = writeLock();
365 try {
366 boolean closed = lastNode() == n && firstNode() == n;
367 int i;
368 List<Node> copy = getNodes();
369 while ((i = copy.indexOf(n)) >= 0) {
370 copy.remove(i);
371 }
372 i = copy.size();
373 if (closed && i > 2) {
374 copy.add(copy.get(0));
375 } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
376 copy.remove(i-1);
377 }
378 setNodes(removeDouble(copy));
379 n.clearCachedStyle();
380 } finally {
381 writeUnlock(locked);
382 }
383 }
384
385 /**
386 * Removes the given set of {@link Node nodes} from this way. Ignored, if selection is null.
387 * @param selection The selection of nodes to remove. Ignored, if null
388 * @since 5408
389 */
390 public void removeNodes(Set<? extends Node> selection) {
391 if (selection == null || isIncomplete()) return;
392 boolean locked = writeLock();
393 try {
394 boolean closed = isClosed() && selection.contains(lastNode());
395 List<Node> copy = new ArrayList<>();
396
397 for (Node n: nodes) {
398 if (!selection.contains(n)) {
399 copy.add(n);
400 }
401 }
402
403 int i = copy.size();
404 if (closed && i > 2) {
405 copy.add(copy.get(0));
406 } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
407 copy.remove(i-1);
408 }
409 setNodes(removeDouble(copy));
410 for (Node n : selection) {
411 n.clearCachedStyle();
412 }
413 } finally {
414 writeUnlock(locked);
415 }
416 }
417
418 /**
419 * Adds a node to the end of the list of nodes. Ignored, if n is null.
420 *
421 * @param n the node. Ignored, if null
422 * @throws IllegalStateException if this way is marked as incomplete. We can't add a node
423 * to an incomplete way
424 * @since 1313
425 */
426 public void addNode(Node n) {
427 if (n == null) return;
428
429 boolean locked = writeLock();
430 try {
431 if (isIncomplete())
432 throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
433 clearCachedStyle();
434 n.addReferrer(this);
435 nodes = Utils.addInArrayCopy(nodes, n);
436 n.clearCachedStyle();
437 fireNodesChanged();
438 } finally {
439 writeUnlock(locked);
440 }
441 }
442
443 /**
444 * Adds a node at position offs.
445 *
446 * @param offs the offset
447 * @param n the node. Ignored, if null.
448 * @throws IllegalStateException if this way is marked as incomplete. We can't add a node
449 * to an incomplete way
450 * @throws IndexOutOfBoundsException if offs is out of bounds
451 * @since 1313
452 */
453 public void addNode(int offs, Node n) {
454 if (n == null) return;
455
456 boolean locked = writeLock();
457 try {
458 if (isIncomplete())
459 throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
460
461 clearCachedStyle();
462 n.addReferrer(this);
463 Node[] newNodes = new Node[nodes.length + 1];
464 System.arraycopy(nodes, 0, newNodes, 0, offs);
465 System.arraycopy(nodes, offs, newNodes, offs + 1, nodes.length - offs);
466 newNodes[offs] = n;
467 nodes = newNodes;
468 n.clearCachedStyle();
469 fireNodesChanged();
470 } finally {
471 writeUnlock(locked);
472 }
473 }
474
475 @Override
476 public void setDeleted(boolean deleted) {
477 boolean locked = writeLock();
478 try {
479 for (Node n:nodes) {
480 if (deleted) {
481 n.removeReferrer(this);
482 } else {
483 n.addReferrer(this);
484 }
485 n.clearCachedStyle();
486 }
487 fireNodesChanged();
488 super.setDeleted(deleted);
489 } finally {
490 writeUnlock(locked);
491 }
492 }
493
494 @Override
495 public boolean isClosed() {
496 if (isIncomplete()) return false;
497
498 Node[] nodes = this.nodes;
499 return nodes.length >= 3 && nodes[nodes.length-1] == nodes[0];
500 }
501
502 /**
503 * Determines if this way denotes an area (closed way with at least three distinct nodes).
504 * @return {@code true} if this way is closed and contains at least three distinct nodes
505 * @see #isClosed
506 * @since 5490
507 */
508 public boolean isArea() {
509 if (this.nodes.length >= 4 && isClosed()) {
510 Node distinctNode = null;
511 for (int i = 1; i < nodes.length-1; i++) {
512 if (distinctNode == null && nodes[i] != nodes[0]) {
513 distinctNode = nodes[i];
514 } else if (distinctNode != null && nodes[i] != nodes[0] && nodes[i] != distinctNode) {
515 return true;
516 }
517 }
518 }
519 return false;
520 }
521
522 /**
523 * Returns the last node of this way.
524 * The result equals <tt>{@link #getNode getNode}({@link #getNodesCount getNodesCount} - 1)</tt>.
525 * @return the last node of this way
526 * @since 1400
527 */
528 public Node lastNode() {
529 Node[] nodes = this.nodes;
530 if (isIncomplete() || nodes.length == 0) return null;
531 return nodes[nodes.length-1];
532 }
533
534 /**
535 * Returns the first node of this way.
536 * The result equals {@link #getNode getNode}{@code (0)}.
537 * @return the first node of this way
538 * @since 1400
539 */
540 public Node firstNode() {
541 Node[] nodes = this.nodes;
542 if (isIncomplete() || nodes.length == 0) return null;
543 return nodes[0];
544 }
545
546 /**
547 * Replies true if the given node is the first or the last one of this way, false otherwise.
548 * @param n The node to test
549 * @return true if the {@code n} is the first or the last node, false otherwise.
550 * @since 1400
551 */
552 public boolean isFirstLastNode(Node n) {
553 Node[] nodes = this.nodes;
554 if (isIncomplete() || nodes.length == 0) return false;
555 return n == nodes[0] || n == nodes[nodes.length -1];
556 }
557
558 /**
559 * Replies true if the given node is an inner node of this way, false otherwise.
560 * @param n The node to test
561 * @return true if the {@code n} is an inner node, false otherwise.
562 * @since 3515
563 */
564 public boolean isInnerNode(Node n) {
565 Node[] nodes = this.nodes;
566 if (isIncomplete() || nodes.length <= 2) return false;
567 /* circular ways have only inner nodes, so return true for them! */
568 if (n == nodes[0] && n == nodes[nodes.length-1]) return true;
569 for (int i = 1; i < nodes.length - 1; ++i) {
570 if (nodes[i] == n) return true;
571 }
572 return false;
573 }
574
575 @Override
576 public String getDisplayName(NameFormatter formatter) {
577 return formatter.format(this);
578 }
579
580 @Override
581 public OsmPrimitiveType getType() {
582 return OsmPrimitiveType.WAY;
583 }
584
585 @Override
586 public OsmPrimitiveType getDisplayType() {
587 return isClosed() ? OsmPrimitiveType.CLOSEDWAY : OsmPrimitiveType.WAY;
588 }
589
590 private void checkNodes() {
591 DataSet dataSet = getDataSet();
592 if (dataSet != null) {
593 Node[] nodes = this.nodes;
594 for (Node n: nodes) {
595 if (n.getDataSet() != dataSet)
596 throw new DataIntegrityProblemException("Nodes in way must be in the same dataset",
597 tr("Nodes in way must be in the same dataset"));
598 if (n.isDeleted())
599 throw new DataIntegrityProblemException("Deleted node referenced: " + toString(),
600 "<html>" + tr("Deleted node referenced by {0}",
601 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(this)) + "</html>");
602 }
603 if (Config.getPref().getBoolean("debug.checkNullCoor", true)) {
604 for (Node n: nodes) {
605 if (n.isVisible() && !n.isIncomplete() && !n.isLatLonKnown())
606 throw new DataIntegrityProblemException("Complete visible node with null coordinates: " + toString(),
607 "<html>" + tr("Complete node {0} with null coordinates in way {1}",
608 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(n),
609 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(this)) + "</html>");
610 }
611 }
612 }
613 }
614
615 private void fireNodesChanged() {
616 checkNodes();
617 if (getDataSet() != null) {
618 getDataSet().fireWayNodesChanged(this);
619 }
620 }
621
622 @Override
623 void setDataset(DataSet dataSet) {
624 super.setDataset(dataSet);
625 checkNodes();
626 }
627
628 @Override
629 public BBox getBBox() {
630 if (getDataSet() == null)
631 return new BBox(this);
632 if (bbox == null) {
633 bbox = new BBox(this);
634 }
635 return new BBox(bbox);
636 }
637
638 @Override
639 protected void addToBBox(BBox box, Set<PrimitiveId> visited) {
640 box.add(getBBox());
641 }
642
643 @Override
644 public void updatePosition() {
645 bbox = new BBox(this);
646 }
647
648 /**
649 * Replies true if this way has incomplete nodes, false otherwise.
650 * @return true if this way has incomplete nodes, false otherwise.
651 * @since 2587
652 */
653 public boolean hasIncompleteNodes() {
654 Node[] nodes = this.nodes;
655 for (Node node : nodes) {
656 if (node.isIncomplete())
657 return true;
658 }
659 return false;
660 }
661
662 /**
663 * Replies true if all nodes of the way have known lat/lon, false otherwise.
664 * @return true if all nodes of the way have known lat/lon, false otherwise
665 * @since 13033
666 */
667 public boolean hasOnlyLocatableNodes() {
668 Node[] nodes = this.nodes;
669 for (Node node : nodes) {
670 if (!node.isLatLonKnown())
671 return false;
672 }
673 return true;
674 }
675
676 @Override
677 public boolean isUsable() {
678 return super.isUsable() && !hasIncompleteNodes();
679 }
680
681 @Override
682 public boolean isDrawable() {
683 return super.isDrawable() && hasOnlyLocatableNodes();
684 }
685
686 /**
687 * Replies the length of the way, in metres, as computed by {@link LatLon#greatCircleDistance}.
688 * @return The length of the way, in metres
689 * @since 4138
690 */
691 public double getLength() {
692 double length = 0;
693 Node lastN = null;
694 for (Node n:nodes) {
695 if (lastN != null) {
696 LatLon lastNcoor = lastN.getCoor();
697 LatLon coor = n.getCoor();
698 if (lastNcoor != null && coor != null) {
699 length += coor.greatCircleDistance(lastNcoor);
700 }
701 }
702 lastN = n;
703 }
704 return length;
705 }
706
707 /**
708 * Replies the length of the longest segment of the way, in metres, as computed by {@link LatLon#greatCircleDistance}.
709 * @return The length of the segment, in metres
710 * @since 8320
711 */
712 public double getLongestSegmentLength() {
713 double length = 0;
714 Node lastN = null;
715 for (Node n:nodes) {
716 if (lastN != null) {
717 LatLon lastNcoor = lastN.getCoor();
718 LatLon coor = n.getCoor();
719 if (lastNcoor != null && coor != null) {
720 double l = coor.greatCircleDistance(lastNcoor);
721 if (l > length) {
722 length = l;
723 }
724 }
725 }
726 lastN = n;
727 }
728 return length;
729 }
730
731 /**
732 * Tests if this way is a oneway.
733 * @return {@code 1} if the way is a oneway,
734 * {@code -1} if the way is a reversed oneway,
735 * {@code 0} otherwise.
736 * @since 5199
737 */
738 public int isOneway() {
739 String oneway = get("oneway");
740 if (oneway != null) {
741 if ("-1".equals(oneway)) {
742 return -1;
743 } else {
744 Boolean isOneway = OsmUtils.getOsmBoolean(oneway);
745 if (isOneway != null && isOneway) {
746 return 1;
747 }
748 }
749 }
750 return 0;
751 }
752
753 /**
754 * Replies the first node of this way, respecting or not its oneway state.
755 * @param respectOneway If true and if this way is a reversed oneway, replies the last node. Otherwise, replies the first node.
756 * @return the first node of this way, according to {@code respectOneway} and its oneway state.
757 * @since 5199
758 */
759 public Node firstNode(boolean respectOneway) {
760 return !respectOneway || isOneway() != -1 ? firstNode() : lastNode();
761 }
762
763 /**
764 * Replies the last node of this way, respecting or not its oneway state.
765 * @param respectOneway If true and if this way is a reversed oneway, replies the first node. Otherwise, replies the last node.
766 * @return the last node of this way, according to {@code respectOneway} and its oneway state.
767 * @since 5199
768 */
769 public Node lastNode(boolean respectOneway) {
770 return !respectOneway || isOneway() != -1 ? lastNode() : firstNode();
771 }
772
773 @Override
774 public boolean concernsArea() {
775 return hasAreaTags();
776 }
777
778 @Override
779 public boolean isOutsideDownloadArea() {
780 for (final Node n : nodes) {
781 if (n.isOutsideDownloadArea()) {
782 return true;
783 }
784 }
785 return false;
786 }
787
788 @Override
789 protected void keysChangedImpl(Map<String, String> originalKeys) {
790 super.keysChangedImpl(originalKeys);
791 clearCachedNodeStyles();
792 }
793
794 /**
795 * Clears all cached styles for all nodes of this way. This should not be called from outside.
796 * @see Node#clearCachedStyle()
797 */
798 public void clearCachedNodeStyles() {
799 for (final Node n : nodes) {
800 n.clearCachedStyle();
801 }
802 }
803}
Note: See TracBrowser for help on using the repository browser.