private void doMergeChildrenChanges(ExternalSystemNode<?> currentNode, ExternalSystemNode newNode) {
    final ExternalSystemNode<?>[] cached = currentNode.getCached();
    if (cached == null) {
      //noinspection unchecked
      currentNode.mergeWith(newNode);
      return;
    }
    final List<Object> duplicates = new ArrayList<>();
    final Map<Object, ExternalSystemNode<?>> oldDataMap = new LinkedHashMap<>();
    for (ExternalSystemNode<?> node : cached) {
      Object key = node.getData() != null ? node.getData() : node.getName();
      final Object systemNode = oldDataMap.put(key, node);
      if (systemNode != null) {
        duplicates.add(key);
      }
    }

    // new data comparing to old node
    Map<Object, ExternalSystemNode<?>> newDataMap = new LinkedHashMap<>();
    // old nodes that exist in the old node as well as in the new node
    Map<Object, ExternalSystemNode<?>> unchangedNewDataMap = new LinkedHashMap<>();
    for (ExternalSystemNode<?> node : newNode.getChildren()) {
      Object key = node.getData() != null ? node.getData() : node.getName();
      if (oldDataMap.remove(key) == null) {
        newDataMap.put(key, node);
      }
      else {
        unchangedNewDataMap.put(key, node);
      }
    }

    for (Object duplicate : duplicates) {
      newDataMap.remove(duplicate);
    }

    currentNode.removeAll(oldDataMap.values());

    for (ExternalSystemNode<?> node : currentNode.getChildren()) {
      Object key = node.getData() != null ? node.getData() : node.getName();
      final ExternalSystemNode<?> unchangedNewNode = unchangedNewDataMap.get(key);
      if (unchangedNewNode != null) {
        doMergeChildrenChanges(node, unchangedNewNode);
      }
    }

    updateFrom(currentNode);
    //noinspection unchecked
    currentNode.mergeWith(newNode);
    currentNode.addAll(newDataMap.values());
  }
private void doMergeChildrenChanges(ExternalSystemNode<?> currentNode, ExternalSystemNode newNode)