public void handleNewReportedNodeState(ClusterState currentClusterState,
                                           NodeInfo node,
                                           NodeState reportedState,
                                           NodeListener nodeListener) {
        NodeState currentState = currentClusterState.getNodeState(node.getNode());
        Level level = (currentState.equals(reportedState) && node.getVersion() == 0) ? FINEST : FINE;
        log.log(level, () -> String.format("Got nodestate reply from %s: %s (Current state is %s)",
                                           node, node.getReportedState().getTextualDifference(reportedState), currentState.toString(true)));
        long currentTime = timer.getCurrentTimeInMillis();

        if (reportedState.getState().equals(DOWN)) {
            node.setTimeOfFirstFailingConnectionAttempt(currentTime);
        }

        // *** LOGGING ONLY
        if ( ! reportedState.similarTo(node.getReportedState())) {
            if (reportedState.getState().equals(DOWN)) {
                eventLog.addNodeOnlyEvent(NodeEvent.forBaseline(node, "Failed to get node state: " + reportedState.toString(true), NodeEvent.Type.REPORTED, currentTime), Level.INFO);
            } else {
                eventLog.addNodeOnlyEvent(NodeEvent.forBaseline(node, "Now reporting state " + reportedState.toString(true), NodeEvent.Type.REPORTED, currentTime), FINE);
            }
        }

        if (reportedState.equals(node.getReportedState()) &&  ! reportedState.getState().equals(INITIALIZING)) {
            return;
        }

        updateNodeInfoFromReportedState(node, currentState, reportedState, nodeListener);

        if (reportedState.getMinUsedBits() != currentState.getMinUsedBits()) {
            int oldCount = currentState.getMinUsedBits();
            int newCount = reportedState.getMinUsedBits();
            log.log(FINE,
                    () -> String.format("Altering node state to reflect that min distribution bit count has changed from %d to %d", oldCount, newCount));
            eventLog.add(NodeEvent.forBaseline(node, String.format("Altered min distribution bit count from %d to %d", oldCount, newCount),
                         NodeEvent.Type.CURRENT, currentTime), isMaster);
        } else {
            log.log(FINE, () -> String.format("Not altering state of %s in cluster state because new state is too similar: %s",
                                              node, currentState.getTextualDifference(reportedState)));
        }

        stateMayHaveChanged = true;
    }
This class gets node state updates and timer events and uses these to decide
whether a new cluster state should be generated.

TODO refactor logic out into smaller, separate components. Still state duplication
between ClusterStateGenerator and StateChangeHandler, especially for temporal
state transition configuration parameters.
clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/StateChangeHandler.java