Index: src/org/openstreetmap/josm/data/osm/Node.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/data/osm/Node.java b/src/org/openstreetmap/josm/data/osm/Node.java
--- a/src/org/openstreetmap/josm/data/osm/Node.java	(revision 18270)
+++ b/src/org/openstreetmap/josm/data/osm/Node.java	(date 1633962468351)
@@ -232,7 +232,7 @@
      * Merges the technical and semantical attributes from <code>other</code> onto this.
      *
      * Both this and other must be new, or both must be assigned an OSM ID. If both this and <code>other</code>
-     * have an assigend OSM id, the IDs have to be the same.
+     * have an assigned OSM id, the IDs have to be the same.
      *
      * @param other the other primitive. Must not be null.
      * @throws IllegalArgumentException if other is null.
Index: src/org/openstreetmap/josm/data/osm/TagMap.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/data/osm/TagMap.java b/src/org/openstreetmap/josm/data/osm/TagMap.java
--- a/src/org/openstreetmap/josm/data/osm/TagMap.java	(revision 18270)
+++ b/src/org/openstreetmap/josm/data/osm/TagMap.java	(date 1633424182312)
@@ -40,7 +40,7 @@
          */
         private final String[] tags;
         /**
-         * Current tag index. Always a multiple of 2.
+         * Current tag index. Always multiple of 2.
          */
         private int currentIndex;
 
Index: src/org/openstreetmap/josm/data/osm/Way.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/data/osm/Way.java b/src/org/openstreetmap/josm/data/osm/Way.java
--- a/src/org/openstreetmap/josm/data/osm/Way.java	(revision 18270)
+++ b/src/org/openstreetmap/josm/data/osm/Way.java	(date 1633866055636)
@@ -650,6 +650,26 @@
         return length;
     }
 
+    /**
+     * Calculates the segment lengths of the way as computed by {@link LatLon#greatCircleDistance}.
+     * @return The segment lengths of a way in metres, following way direction
+     * @since xxx
+     */
+    public double[] getSegmentLengths() {
+        double[] segmentLengths = new double[nodes.length - 1];
+        Node prevNode = this.firstNode();
+
+        for (int i = 1; i < nodes.length; i++) {
+            Node n = nodes[i];
+
+            double distance = n.getCoor().greatCircleDistance(prevNode.getCoor());
+            segmentLengths[i - 1] = distance;
+            prevNode = n;
+        }
+
+        return segmentLengths;
+    }
+
     /**
      * Replies the length of the longest segment of the way, in metres, as computed by {@link LatLon#greatCircleDistance}.
      * @return The length of the segment, in metres
Index: src/org/openstreetmap/josm/data/validation/tests/PowerLines.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/data/validation/tests/PowerLines.java b/src/org/openstreetmap/josm/data/validation/tests/PowerLines.java
--- a/src/org/openstreetmap/josm/data/validation/tests/PowerLines.java	(revision 18270)
+++ b/src/org/openstreetmap/josm/data/validation/tests/PowerLines.java	(date 1633976447303)
@@ -1,77 +1,106 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.validation.tests;
 
+import static org.openstreetmap.josm.gui.MainApplication.getLayerManager;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.LinkedHashSet;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.WaySegment;
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
+import org.openstreetmap.josm.data.preferences.CachingProperty;
+import org.openstreetmap.josm.data.preferences.DoubleProperty;
+import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
 import org.openstreetmap.josm.data.validation.Severity;
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.tools.Geometry;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Pair;
 
 /**
- * Checks for nodes in power lines/minor_lines that do not have a power=tower/pole tag.<br>
- * See #7812 for discussions about this test.
+ * Checks for
+ * <ul>
+ * <li>nodes in power lines/minor_lines that do not have a power=tower/pole/portal tag
+ * <li>nodes where the reference numbering not consistent
+ * <li>ways where are unusually long segments without line support feature
+ * <li>ways where the line type is possibly misused
+ * </ul>
+ * See #7812 and #20716 for discussions about this test.
  */
 public class PowerLines extends Test {
 
-    /** Test identifier */
-    protected static final int POWER_LINES = 2501;
+    // Test identifiers
+    protected static final int POWER_SUPPORT = 2501;
     protected static final int POWER_CONNECTION = 2502;
+    protected static final int POWER_SEGMENT_LENGTH = 2503;
+    protected static final int POWER_LOCAL_REF_CONTINUITY = 2504;
+    protected static final int POWER_WAY_REF_CONTINUITY = 2505;
+    protected static final int POWER_LINE_TYPE = 2506;
+
+    protected static final CachingProperty<Double> HILLY_COMPENSATION = new DoubleProperty(
+            ValidatorPrefHelper.PREFIX + ".powerlines.hilly_compensation",
+            0.2).cached();
+    protected static final CachingProperty<Double> HILLY_THRESHOLD = new DoubleProperty(
+            ValidatorPrefHelper.PREFIX + ".powerlines.hilly_threshold",
+            4.0).cached();
 
     /** Values for {@code power} key interpreted as power lines */
     static final Collection<String> POWER_LINE_TAGS = Arrays.asList("line", "minor_line");
     /** Values for {@code power} key interpreted as power towers */
-    static final Collection<String> POWER_TOWER_TAGS = Arrays.asList("tower", "pole");
+    static final Collection<String> POWER_TOWER_TAGS = Arrays.asList("catenary_mast", "pole", "portal", "tower");
     /** Values for {@code power} key interpreted as power stations */
-    static final Collection<String> POWER_STATION_TAGS = Arrays.asList("station", "sub_station", "substation", "plant", "generator");
+    static final Collection<String> POWER_STATION_TAGS = Arrays.asList("generator", "plant", "substation");
     /** Values for {@code building} key interpreted as power stations */
     static final Collection<String> BUILDING_STATION_TAGS = Arrays.asList("transformer_tower");
     /** Values for {@code power} key interpreted as allowed power items */
-    static final Collection<String> POWER_ALLOWED_TAGS = Arrays.asList("switch", "transformer", "busbar", "generator", "switchgear",
-            "portal", "terminal", "insulator");
+    static final Collection<String> POWER_INFRASTRUCTURE_TAGS = Arrays.asList("compensator", "converter",
+            "generator", "insulator", "switch", "switchgear", "terminal", "transformer");
 
-    private final Set<Node> badConnections = new LinkedHashSet<>();
-    private final Set<Node> missingTowerOrPole = new LinkedHashSet<>();
+    private final Set<Node> badConnections = new HashSet<>();
+    private final Set<Node> missingTags = new HashSet<>();
+    private final Set<Way> wrongLineType = new HashSet<>();
+    private final Set<OsmPrimitive> refDiscontinuities = new HashSet<>();
+    private final List<Set<Node>> segmentRefDiscontinuities = new ArrayList<>();
+    private final Set<WaySegment> missingNodes = new HashSet<>();
 
     private final List<OsmPrimitive> powerStations = new ArrayList<>();
 
+    private final Collection<Way> datasetWaterways = new HashSet<>(64);
+
     /**
      * Constructs a new {@code PowerLines} test.
      */
     public PowerLines() {
-        super(tr("Power lines"), tr("Checks for nodes in power lines that do not have a power=tower/pole tag."));
-    }
-
-    @Override
-    public void visit(Way w) {
-        if (w.isUsable()) {
-            if (isPowerLine(w) && !w.hasTag("location", "underground")) {
-                for (Node n : w.getNodes()) {
-                    if (!isPowerTower(n) && !isPowerAllowed(n) && IN_DOWNLOADED_AREA.test(n)
-                        && (!w.isFirstLastNode(n) || !isPowerStation(n))) {
-                        missingTowerOrPole.add(n);
-                    }
-                }
-            } else if (w.isClosed() && isPowerStation(w)) {
-                powerStations.add(w);
-            }
-        }
+        super(tr("Power lines"), tr("Checks if power line missing a support node and " +
+                "for nodes in power lines that do not have a power=tower/pole tag"));
+    }
+
+    /**
+     * Power line support features ref=* numbering direction.
+     */
+    private enum NumberingDirection {
+        /** No direction */
+        NONE,
+        /** Numbering follows way direction */
+        SAME,
+        /** Numbering goes opposite way direction */
+        OPPOSITE
     }
 
     @Override
@@ -89,18 +118,77 @@
             badConnections.add(n);
     }
 
-    private static boolean isRelatedToPower(Way way) {
-        if (way.hasTag("power") || way.hasTag("building"))
-            return true;
-        for (OsmPrimitive ref : way.getReferrers()) {
-            if (ref instanceof Relation && ref.isMultipolygon() && (ref.hasTag("power") || ref.hasTag("building"))) {
-                for (RelationMember rm : ((Relation) ref).getMembers()) {
-                    if (way == rm.getMember())
-                        return true;
+    @Override
+    public void visit(Way w) {
+        if (!isPrimitiveUsable(w)) return;
+
+        if (isPowerLine(w) && !w.hasKey("line") && !w.hasTag("location", "underground")) {
+            final int segmentCount = w.getNodesCount() - 1;
+            final double mean = w.getLength() / segmentCount;
+            final double stdDev = calculateStdDev(w.getSegmentLengths(), mean);
+            int poleCount = 0;
+            int towerCount = 0;
+            Node prevNode = w.firstNode();
+
+            double baseThreshold = w.hasTag("power", "line") ? 1.6 : 1.8;
+            if (mean / stdDev < HILLY_THRESHOLD.get()) {
+                //compensate for possibly hilly areas where towers can't be put anywhere
+                baseThreshold += HILLY_COMPENSATION.get();
+            }
+
+            for (int i = 1; i < w.getRealNodesCount(); i++) {
+                final Node n = w.getNode(i);
+
+                /// handle power station line connections (eg. power=line + line=*)
+                if (isConnectedToStationLine(n, w)) {
+                    prevNode = n;
+                    continue;   // skip, it would be false positive
                 }
+
+                /// handle missing power line support tags (e.g. tower)
+                if (!isPowerTower(n) && !isPowerInfrastructure(n) && IN_DOWNLOADED_AREA.test(n)
+                        && (!w.isFirstLastNode(n) || !isPowerStation(n)))
+                    missingTags.add(n);
+
+                /// handle missing nodes
+                double segmentLen = n.getCoor().greatCircleDistance(prevNode.getCoor());
+                final Pair<Node, Node> pair = Pair.create(prevNode, n);
+                final Set<Way> crossingWaterWays = new HashSet<>(8);
+                final Set<Node> crossingPositions = new HashSet<>();
+                findCrossings(datasetWaterways, w, pair, crossingWaterWays, crossingPositions);
+
+                if (!crossingWaterWays.isEmpty()) {
+                    double compensation = calculateIntersectingLen(prevNode, crossingPositions);
+                    segmentLen -= compensation;
+                }
+
+                if (segmentCount > 4
+                        && segmentLen > mean * baseThreshold
+                        && !isPowerInfrastructure(n)
+                        && IN_DOWNLOADED_AREA.test(n))
+                    missingNodes.add(WaySegment.forNodePair(w, prevNode, n));
+
+                /// handle wrong line types
+                if (n.hasTag("power", "pole"))
+                    poleCount++;
+                else if (n.hasTag("power", "tower", "portal"))
+                    towerCount++;
+
+                prevNode = n;
             }
+
+            /// handle ref=* numbering discontinuities
+            if (detectDiscontinuity(w, refDiscontinuities, segmentRefDiscontinuities))
+                refDiscontinuities.add(w);
+
+            /// handle wrong line types
+            if ((poleCount > towerCount && w.hasTag("power", "line")) ||
+                    (poleCount < towerCount && w.hasTag("power", "minor_line")))
+                wrongLineType.add(w);
+
+        } else if (w.isClosed() && isPowerStation(w)) {
+            powerStations.add(w);
         }
-        return false;
     }
 
     @Override
@@ -113,15 +201,34 @@
     @Override
     public void startTest(ProgressMonitor progressMonitor) {
         super.startTest(progressMonitor);
-        clearCollections();
+        // the test run can take a bit of time, show detailed progress
+        setShowElements(true);
+
+        // collect all waterways
+        getLayerManager()
+                .getActiveDataSet()
+                .getWays()
+                .parallelStream()
+                .filter(way ->
+                        way.hasTag("water", "river", "lake") ||
+                                way.hasKey("waterway") ||
+                                way.hasTag("natural", "coastline") ||
+                                way.referrers(Relation.class)
+                                        .anyMatch(relation ->
+                                                relation.hasTag("water", "river", "lake") ||
+                                                        relation.hasKey("waterway") ||
+                                                        relation.hasTag("natural", "coastline")
+                                        ))
+                .forEach(datasetWaterways::add);
     }
 
     @Override
     public void endTest() {
-        for (Node n : missingTowerOrPole) {
+        for (Node n : missingTags) {
             if (!isInPowerStation(n)) {
-                errors.add(TestError.builder(this, Severity.WARNING, POWER_LINES)
-                        .message(tr("Missing power tower/pole within power line"))
+                errors.add(TestError.builder(this, Severity.WARNING, POWER_SUPPORT)
+                        // the "missing tag" grouping can become broken if the MapCSS message get reworded
+                        .message(tr("missing tag"), tr("node without power=*"))
                         .primitives(n)
                         .build());
             }
@@ -130,13 +237,337 @@
         for (Node n : badConnections) {
             errors.add(TestError.builder(this, Severity.WARNING, POWER_CONNECTION)
                     .message(tr("Node connects a power line or cable with an object "
-                            + "which is not related to the power infrastructure."))
-                    .primitives(n).build());
+                            + "which is not related to the power infrastructure"))
+                    .primitives(n)
+                    .build());
+        }
+
+        for (WaySegment s : missingNodes) {
+            errors.add(TestError.builder(this, Severity.WARNING, POWER_SEGMENT_LENGTH)
+                    .message(tr("Possibly missing line support node within power line"))
+                    .primitives(s.getFirstNode(), s.getSecondNode())
+                    .highlightWaySegments(new HashSet<>(Collections.singleton(s)))
+                    .build());
         }
-        clearCollections();
+
+        for (OsmPrimitive p : refDiscontinuities) {
+            if (p instanceof Way)
+                errors.add(TestError.builder(this, Severity.WARNING, POWER_WAY_REF_CONTINUITY)
+                        .message(tr("Mixed reference numbering"))
+                        .primitives(p)
+                        .build());
+        }
+
+        final String discontinuityMsg = tr("Reference numbering don''t match majority of way''s nodes");
+
+        for (OsmPrimitive p : refDiscontinuities) {
+            if (p instanceof Node)
+                errors.add(TestError.builder(this, Severity.WARNING, POWER_LOCAL_REF_CONTINUITY)
+                        .message(discontinuityMsg)
+                        .primitives(p)
+                        .build());
+        }
+
+        for (Set<Node> nodes : segmentRefDiscontinuities) {
+            errors.add(TestError.builder(this, Severity.WARNING, POWER_LOCAL_REF_CONTINUITY)
+                    .message(discontinuityMsg)
+                    .primitives(nodes)
+                    .build());
+        }
+
+        for (Way w : wrongLineType) {
+            errors.add(TestError.builder(this, Severity.WARNING, POWER_LINE_TYPE)
+                    .message(tr("Possibly wrong power line type used"))
+                    .primitives(w)
+                    .build());
+        }
+
         super.endTest();
     }
 
+    /**
+     * Calculates the standard deviation from the given values.
+     * @param values An array of values
+     * @param mean Precalculated average value of the array
+     * @return standard deviation of the given array
+     */
+    protected static double calculateStdDev(double[] values, double mean) {
+        double standardDeviation = 0;
+
+        for (double length : values) {
+            standardDeviation += Math.pow(length - mean, 2);
+        }
+
+        return Math.sqrt(standardDeviation / values.length);
+    }
+
+    /**
+     * The summarized length (in metres) of a way where a power line hangs over a water area.
+     * @param ref Reference point
+     * @param crossingNodes Crossing nodes, unordered
+     * @return The summarized length (in metres) of a way where a power line hangs over a water area
+     */
+    private static double calculateIntersectingLen(Node ref, Set<Node> crossingNodes) {
+        double min = Double.POSITIVE_INFINITY;
+        double max = Double.NEGATIVE_INFINITY;
+
+        for (Node n : crossingNodes) {
+            double dist = ref.getCoor().greatCircleDistance(n.getCoor());
+
+            if (dist < min)
+                min = dist;
+            if (dist > max)
+                max = dist;
+        }
+        return max - min;
+    }
+
+    /**
+     * Searches for way intersections, which intersect the {@code pair} attribute.
+     * @param ways collection of ways to search
+     * @param parent parent way for {@code pair} param
+     * @param pair {@link Node} pair among which search for another way
+     * @param crossingWays found crossing ways
+     * @param crossingPositions collection of the crossing positions
+     * @implNote Inspired by {@code utilsplugin2/selection/NodeWayUtils.java#addWaysIntersectingWay()}
+     */
+    private static void findCrossings(Collection<Way> ways, Way parent, Pair<Node, Node> pair, Set<Way> crossingWays,
+                                      Set<Node> crossingPositions) {
+        for (Way way : ways) {
+            if (way.isUsable()
+                    && !crossingWays.contains(way)
+                    && way.getBBox().intersects(parent.getBBox())) {
+                for (Pair<Node, Node> pair2 : way.getNodePairs(false)) {
+                    EastNorth eastNorth = Geometry.getSegmentSegmentIntersection(
+                            pair.a.getEastNorth(), pair.b.getEastNorth(),
+                            pair2.a.getEastNorth(), pair2.b.getEastNorth());
+                    if (eastNorth != null) {
+                        crossingPositions.add(new Node(eastNorth));
+                        crossingWays.add(way);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Helper class for reference numbering test. Used for storing continuous reference segment info.
+     */
+    private static class SegmentInfo {
+        /** Follows ways direction */
+        private final int firstNodeIndex;
+        private final int length;
+        private final int startingRef;
+        private final NumberingDirection direction;
+
+        SegmentInfo(int firstNodeIndex, int length, int ref, NumberingDirection direction) {
+            this.firstNodeIndex = firstNodeIndex;
+            this.length = length;
+            this.direction = direction;
+
+            if (direction == NumberingDirection.SAME)
+                this.startingRef = ref - length;
+            else
+                this.startingRef = ref + length;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("SegmentInfo{firstNodeIndex=%d, length=%d, startingRef=%d, direction=%s}",
+                    firstNodeIndex, length, startingRef, direction);
+        }
+    }
+
+    /**
+     * Detects ref=* numbering discontinuities in the given way.
+     * @param way checked way
+     * @param nRefDiscontinuities single node ref=* discontinuities
+     * @param sRefDiscontinuities continuous node ref=* discontinuities
+     * @return {@code true} if warning needs to be issued for the whole way
+     */
+    static boolean detectDiscontinuity(Way way, Set<OsmPrimitive> nRefDiscontinuities, List<Set<Node>> sRefDiscontinuities) {
+        final ArrayList<SegmentInfo> segments = new RefChecker(way).getSegments();
+        final Set<NumberingDirection> directions = new HashSet<>(4);
+        SegmentInfo referenceSegment = null;
+        int longestSegmentLen = 0;
+        int counter = 0;
+
+        // calculate longest segment
+        for (SegmentInfo segment : segments) {
+            if (segment.length > longestSegmentLen) {
+                longestSegmentLen = segment.length;
+                referenceSegment = segment;
+                directions.clear();
+                directions.add(segment.direction);
+                counter = 0;
+            } else if (segment.length == longestSegmentLen) {
+                counter++;
+                directions.add(segment.direction);
+            }
+        }
+
+        // there are multiple segments with the same longest length and their directions don't match
+        if (counter > 0 && directions.size() > 1)
+            return true;
+
+        // if reference is null, but we have segments, select one randomly
+        if (referenceSegment == null && !segments.isEmpty())
+            referenceSegment = segments.iterator().next();
+
+        // collect disconnected ref segments which are not align up to the reference
+        for (SegmentInfo segment : segments) {
+            if (!isSegmentAlign(referenceSegment, segment)) {
+                if (referenceSegment.length == 0)
+                    return true;
+
+                if (segment.length == 0) {
+                    nRefDiscontinuities.add(way.getNode(segment.firstNodeIndex));
+                } else {
+                    final Set<Node> nodeGroup = new HashSet<>();
+
+                    for (int i = segment.firstNodeIndex; i <= segment.firstNodeIndex + segment.length; i++) {
+                        nodeGroup.add(way.getNode(i));
+                    }
+                    sRefDiscontinuities.add(nodeGroup);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks if parameter segments align.
+     * @param reference Reference segment to check against
+     * @param candidate Candidate segment
+     * @return {@code true} if the two segments ref=* numbering align
+     */
+    private static boolean isSegmentAlign(SegmentInfo reference, SegmentInfo candidate) {
+        return candidate.firstNodeIndex - reference.firstNodeIndex == candidate.startingRef - reference.startingRef;
+    }
+
+    /**
+     * Detects continuous reference numbering sequences. Ignores the first and last node because
+     * ways can be connected, and the connection nodes can have different numbering.
+     * <p>
+     * If the numbering switches in the middle of the way, this can also be seen as error,
+     * because line relations would require split ways.
+     */
+    static class RefChecker {
+        final ArrayList<SegmentInfo> segments = new ArrayList<>();
+
+        NumberingDirection direction = NumberingDirection.NONE;
+        Integer startIndex = null;
+        Integer previousRef = null;
+
+        RefChecker(final Way way) {
+            run(way);
+        }
+
+        private void run(Way way) {
+            final int wayLength = way.getNodesCount();
+
+            // first and last node skipped
+            for (int i = 1; i < wayLength - 1; i++) {
+                Node n = way.getNode(i);
+                maintain(parseRef(n.get("ref")), i);
+            }
+
+            // needed for creation of the last segment
+            maintain(null, wayLength - 1);
+        }
+
+        private Integer parseRef(String value) {
+            try {
+                return Integer.parseInt(value);
+            } catch (NumberFormatException ignore) {
+                Logging.trace("The PowerLines.RefChecker couldn't parse ref=" + value + ", consider rewriting the parser");
+                return null;
+            }
+        }
+
+        /**
+         * Maintains the class variables a constructs a new segment when necessary.
+         * @param ref recognised ref=* number
+         * @param index {@link Node} index in a way
+         */
+        private void maintain(Integer ref, int index) {
+            if (this.startIndex == null) {
+                startIndex = index;
+            }
+
+            if (ref == null) {
+                if (previousRef == null) {
+                    reset();
+                    return;
+                }
+
+                segments.add(new SegmentInfo(startIndex, index - 1 - startIndex, previousRef, direction));
+                reset();
+                return;
+            }
+
+            if (previousRef == null) {
+                previousRef = ref;
+                return;
+            }
+
+            if (Math.abs(ref - previousRef) != 1) {
+                segments.add(new SegmentInfo(startIndex, index - 1 - startIndex, previousRef, direction));
+                startIndex = index;
+            }
+
+            if (ref > previousRef)
+                direction = NumberingDirection.SAME;
+            else if (ref < previousRef)
+                direction = NumberingDirection.OPPOSITE;
+
+            previousRef = ref;
+        }
+
+        private void reset() {
+            this.direction = NumberingDirection.NONE;
+            this.startIndex = null;
+            this.previousRef = null;
+        }
+
+        ArrayList<SegmentInfo> getSegments() {
+            return segments;
+        }
+    }
+
+    private static boolean isRelatedToPower(Way way) {
+        if (way.hasTag("power") || way.hasTag("building"))
+            return true;
+        for (OsmPrimitive ref : way.getReferrers()) {
+            if (ref instanceof Relation && ref.isMultipolygon() && (ref.hasTag("power") || ref.hasTag("building"))) {
+                for (RelationMember rm : ((Relation) ref).getMembers()) {
+                    if (way == rm.getMember())
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determines if the current node connected to a line which used usually used inside power stations.
+     * @param n node to check
+     * @param w parent way of {@code n}
+     * @return {@code true} if {@code n} connected to power=line + line=*
+     */
+    private static boolean isConnectedToStationLine(Node n, Way w) {
+        for (OsmPrimitive p : n.getReferrers()) {
+            if (p instanceof Way && !p.equals(w) && isPowerLine((Way) p) && p.hasKey("line"))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the given node is inside a power station.
+     * @param n Node to be checked
+     */
     protected final boolean isInPowerStation(Node n) {
         for (OsmPrimitive station : powerStations) {
             List<List<Node>> nodesLists = new ArrayList<>();
@@ -171,16 +602,16 @@
     /**
      * Determines if the specified primitive denotes a power station.
      * @param p The primitive to be tested
-     * @return {@code true} if power key is set and equal to station/sub_station/plant
+     * @return {@code true} if power key is set and equal to generator/substation/plant
      */
     protected static final boolean isPowerStation(OsmPrimitive p) {
         return isPowerIn(p, POWER_STATION_TAGS) || isBuildingIn(p, BUILDING_STATION_TAGS);
     }
 
     /**
-     * Determines if the specified node denotes a power tower/pole.
+     * Determines if the specified node denotes a power support feature.
      * @param n The node to be tested
-     * @return {@code true} if power key is set and equal to tower/pole
+     * @return {@code true} if power key is set and equal to pole/tower/portal/catenary_mast
      */
     protected static final boolean isPowerTower(Node n) {
         return isPowerIn(n, POWER_TOWER_TAGS);
@@ -189,10 +620,11 @@
     /**
      * Determines if the specified node denotes a power infrastructure allowed on a power line.
      * @param n The node to be tested
-     * @return True if power key is set and equal to switch/tranformer/busbar/generator
+     * @return {@code true} if power key is set and equal to compensator/converter/generator/insulator
+     * /switch/switchgear/terminal/transformer
      */
-    protected static final boolean isPowerAllowed(Node n) {
-        return isPowerIn(n, POWER_ALLOWED_TAGS);
+    protected static final boolean isPowerInfrastructure(Node n) {
+        return isPowerIn(n, POWER_INFRASTRUCTURE_TAGS);
     }
 
     /**
@@ -215,9 +647,16 @@
         return p.hasTag("building", values);
     }
 
-    private void clearCollections() {
+    @Override
+    public void clear() {
+        super.clear();
         powerStations.clear();
         badConnections.clear();
-        missingTowerOrPole.clear();
+        missingTags.clear();
+        missingNodes.clear();
+        wrongLineType.clear();
+        refDiscontinuities.clear();
+        segmentRefDiscontinuities.clear();
+        datasetWaterways.clear();
     }
 }
Index: src/org/openstreetmap/josm/data/validation/Test.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/data/validation/Test.java b/src/org/openstreetmap/josm/data/validation/Test.java
--- a/src/org/openstreetmap/josm/data/validation/Test.java	(revision 18270)
+++ b/src/org/openstreetmap/josm/data/validation/Test.java	(date 1633968492165)
@@ -371,6 +371,10 @@
         errors.clear();
     }
 
+    /**
+     * Sets the validator progress bar elements counter visibility.
+     * @param b True if element counter shown
+     */
     protected void setShowElements(boolean b) {
         showElementCount = b;
     }
