private void onEventInternal(@NotNull Object buildId, @NotNull BuildEvent event) {
    Set<ExecutionNode> structureChanged = new SmartHashSet<>();
    final ExecutionNode parentNode = getOrMaybeCreateParentNode(event, structureChanged);
    final Object eventId = event.getId();
    ExecutionNode currentNode = nodesMap.get(eventId);
    ExecutionNode buildProgressRootNode = getBuildProgressRootNode();
    Runnable selectErrorNodeTask = null;
    boolean isMessageEvent = event instanceof MessageEvent;
    if (event instanceof StartEvent || isMessageEvent) {
      if (currentNode == null) {
        if (event instanceof DuplicateMessageAware) {
          if (myFinishedBuildEventReceived.get()) {
            if (parentNode != null &&
                parentNode.findFirstChild(node -> event.getMessage().equals(node.getName())) != null) {
              return;
            }
          }
          else {
            myDeferredEvents.add(event);
            return;
          }
        }
        if (event instanceof StartBuildEvent) {
          currentNode = buildProgressRootNode;
          installContextMenu();
          currentNode.setTitle(myBuildDescriptor.getTitle());
        }
        else {
          currentNode = new ExecutionNode(myProject, parentNode, false, this::isCorrectThread);

          if (isMessageEvent) {
            currentNode.setAlwaysLeaf(event instanceof FileMessageEvent);
            MessageEvent messageEvent = (MessageEvent)event;
            currentNode.setStartTime(messageEvent.getEventTime());
            currentNode.setEndTime(messageEvent.getEventTime(), false);
            Navigatable messageEventNavigatable = messageEvent.getNavigatable(myProject);
            currentNode.setNavigatable(messageEventNavigatable);
            MessageEventResult messageEventResult = messageEvent.getResult();
            addIfNotNull(structureChanged, currentNode.setResult(messageEventResult));

            if (messageEventResult instanceof FailureResult) {
              for (Failure failure : ((FailureResult)messageEventResult).getFailures()) {
                selectErrorNodeTask =
                  selectErrorNodeTask != null ? selectErrorNodeTask : showErrorIfFirst(currentNode, failure.getNavigatable());
              }
            }
            if (messageEvent.getKind() == MessageEvent.Kind.ERROR) {
              selectErrorNodeTask =
                selectErrorNodeTask != null ? selectErrorNodeTask : showErrorIfFirst(currentNode, messageEventNavigatable);
            }

            if (parentNode != null) {
              if (parentNode != buildProgressRootNode) {
                myConsoleViewHandler.addOutput(parentNode, buildId, event);
                myConsoleViewHandler.addOutput(parentNode);
              }
              reportMessageKind(messageEvent.getKind(), parentNode, structureChanged);
            }
            myConsoleViewHandler.addOutput(currentNode, buildId, event);
          }
          if (parentNode != null) {
            structureChanged.add(parentNode);
            parentNode.add(currentNode);
          }
        }
        nodesMap.put(eventId, currentNode);
      }
      else {
        if (LOG.isDebugEnabled()) {
          LOG.debug("start event id collision found:" + eventId + ", was also in node: " + currentNode.getTitle());
        }
        return;
      }
    }
    else {
      boolean isProgress = event instanceof ProgressBuildEvent;
      currentNode = nodesMap.get(eventId);
      if (currentNode == null) {
        if (isProgress) {
          currentNode = new ExecutionNode(myProject, parentNode, parentNode == buildProgressRootNode, this::isCorrectThread);
          nodesMap.put(eventId, currentNode);
          if (parentNode != null) {
            structureChanged.add(parentNode);
            parentNode.add(currentNode);
          }
        }
        else if (event instanceof OutputBuildEvent && parentNode != null) {
          myConsoleViewHandler.addOutput(parentNode, buildId, event);
        }
        else if (event instanceof PresentableBuildEvent) {
          currentNode =
            addAsPresentableEventNode((PresentableBuildEvent)event, structureChanged, parentNode, eventId, buildProgressRootNode);
        }
      }

      if (isProgress) {
        ProgressBuildEvent progressBuildEvent = (ProgressBuildEvent)event;
        long total = progressBuildEvent.getTotal();
        long progress = progressBuildEvent.getProgress();
        if (currentNode == myBuildProgressRootNode) {
          myConsoleViewHandler.updateProgressBar(total, progress);
        }
      }
    }

    if (currentNode == null) {
      return;
    }

    currentNode.setName(event.getMessage());
    currentNode.setHint(event.getHint());
    if (currentNode.getStartTime() == 0) {
      currentNode.setStartTime(event.getEventTime());
    }

    if (event instanceof FinishEvent) {
      EventResult result = ((FinishEvent)event).getResult();
      if (result instanceof DerivedResult) {
        result = calculateDerivedResult((DerivedResult)result, currentNode);
      }
      currentNode.setResult(result, false);
      addIfNotNull(structureChanged, currentNode.setEndTime(event.getEventTime()));
      SkippedResult skippedResult = new SkippedResultImpl();
      finishChildren(structureChanged, currentNode, skippedResult);
      if (result instanceof FailureResult) {
        for (Failure failure : ((FailureResult)result).getFailures()) {
          Runnable task = addChildFailureNode(currentNode, failure, event.getMessage(), event.getEventTime(), structureChanged);
          if (selectErrorNodeTask == null) selectErrorNodeTask = task;
        }
      }
    }

    if (event instanceof FinishBuildEvent) {
      myFinishedBuildEventReceived.set(true);
      String aHint = event.getHint();
      String time = DateFormatUtil.formatDateTime(event.getEventTime());
      aHint = aHint == null ?
              LangBundle.message("build.event.message.at", time) :
              LangBundle.message("build.event.message.0.at.1", aHint, time);
      currentNode.setHint(aHint);
      myDeferredEvents.forEach(buildEvent -> onEventInternal(buildId, buildEvent));
      if (myConsoleViewHandler.myExecutionNode == null) {
        invokeLater(() -> myConsoleViewHandler.setNode(buildProgressRootNode));
      }
      myConsoleViewHandler.stopProgressBar();
    }
    if (structureChanged.isEmpty()) {
      scheduleUpdate(currentNode, false);
    }
    else {
      for (ExecutionNode node : structureChanged) {
        scheduleUpdate(node, true);
      }
    }
    if (selectErrorNodeTask != null) {
      myExpandedFirstMessage.set(true);
      Runnable finalSelectErrorTask = selectErrorNodeTask;
      myTreeModel.invalidate(getRootElement(), true).onProcessed(p -> finalSelectErrorTask.run());
    }
    else {
      if (isMessageEvent && myExpandedFirstMessage.compareAndSet(false, true)) {
        ExecutionNode finalCurrentNode = currentNode;
        myTreeModel.invalidate(getRootElement(), false).onProcessed(p -> {
          TreeUtil.promiseMakeVisible(myTree, visitor(finalCurrentNode));
        });
      }
    }
  }
private void onEventInternal(@NotNull Object buildId, @NotNull BuildEvent event)