void update(@NotNull FilePartNode parent, @NotNull FilePartNodeRoot root, @NotNull String debugSource, @Nullable Object debugInvalidationReason) {
    boolean oldCaseSensitive = isCaseSensitive();
    Object fileOrUrl = myFileOrUrl;
    VirtualFile file = myFile(fileOrUrl);
    boolean changed = false;
    boolean nameChanged = false;
    boolean fileIsValid = false;
    if (file != null) {
      fileIsValid = file.isValid();
      if (fileIsValid && file.getParent() == null && file.getFileSystem() instanceof ArchiveFileSystem) {
        VirtualFile local = ((ArchiveFileSystem)file.getFileSystem()).getLocalByEntry(file);
        fileIsValid = local != null;
      }
      if (!fileIsValid) {
        file = null;
        changed = true;
      }
    }

    Object parentFileOrUrl;
    parentFileOrUrl = parent.myFileOrUrl;
    String myName = getName().toString();
    String url = null;
    String parentUrl = null;

    VirtualFile parentFile = myFile(parentFileOrUrl);
    if (file == null) {
      file = parentFile == null || !parentFile.isValid() ? null : findChildThroughJar(parentFile, myName, myFS);
      if (file == null) {
        parentUrl = myUrl(parentFileOrUrl);
        url = childUrl(parentUrl, myName, myFS);
        changed |= nameChanged = !Comparing.strEqual(url, myUrl(fileOrUrl));
      }
      else {
        changed = true;
      }
      fileIsValid = file != null && file.isValid();
    }
    if (parent.nameId != -1 && !(parentFileOrUrl instanceof VirtualFile) && file != null) {
      // if the parent file can't be found, then the child is not valid either
      file = null;
      fileIsValid = false;
      url = myUrl(fileOrUrl);
    }
    if (file != null) {
      if (fileIsValid) {
        changed |= nameChanged = !StringUtil.equals(file.getNameSequence(), myName);
      }
      else {
        file = null; // can't find, try next time
        changed = true;
        url = myUrl(fileOrUrl);
      }
    }
    Object result = file == null ? url : file;
    changed |= !Objects.equals(fileOrUrl, result);
    FilePartNode thisNode = this;
    if (changed) {
      VirtualFile oldFile = myFile(fileOrUrl);
      if (oldFile != null && file == null && debugInvalidationReason != null) {
        ((VirtualFileSystemEntry)oldFile).appendInvalidationReason(debugSource, debugInvalidationReason);
      }
      myFileOrUrl = result;
      if (file != null && (this instanceof UrlPartNode || nameChanged)) {
        // replace with FPPN if the actual file's appeared on disk to save memory with nameIds
        thisNode = replaceWithFPPN(file, parent);
      }
    }
    if (file != null && !Objects.equals(getParentThroughJar(file, myFS), parentFile)) {
      // this node file must be moved to the other dir. remove and re-insert from the root to the correct path, preserving all children
      FilePartNode newNode = root.findOrCreateByFile(file).node;
      processPointers(p -> newNode.addLeaf(p));
      newNode.children = children;
      children = EMPTY_ARRAY;
      changed = true;
      root.removeEmptyNodesByPath(VfsUtilCore.urlToPath(childUrl(parentUrl = myUrl(parentFileOrUrl), myName, myFS)));
      thisNode = newNode;
      nameChanged = true;
    }
    if (nameChanged) {
      String myOldPath = VfsUtilCore.urlToPath(childUrl(parentUrl == null ? myUrl(parentFileOrUrl) : parentUrl, myName, myFS));
      String myNewPath = VfsUtilCore.urlToPath(myUrl(result));
      // fix UrlPartNodes with (now) wrong url start
      thisNode.fixUrlPartNodes(myOldPath, myNewPath);
    }

    FilePartNode[] children = thisNode.children;
    VirtualFile toReplaceParent = null;
    for (int i = 0; i < children.length; i++) {
      FilePartNode child = children[i];
      if (changed) {
        child.update(thisNode, root, debugSource, debugInvalidationReason);
        if (i >= thisNode.children.length) {
          break;
        }
        child = thisNode.children[i];
      }
      if (file == null) {
        VirtualFile childFile = child.myFile();
        if (childFile != null) {
          // child found which has a file but this node doesn't, should replace me with FPPN
          toReplaceParent = getParentThroughJar(childFile, child.myFS);
        }
      }
    }
    if (toReplaceParent != null) {
      thisNode = replaceWithFPPN(toReplaceParent, parent);
    }
    // sometimes the case sensitivity of the directory is established only after its children.
    // when it's changed, we should re-sort children according to the new case sensitivity
    VirtualFile newFile = thisNode.myFile();
    boolean newCaseSensitive = thisNode.isCaseSensitive();
    if (newFile != null && newCaseSensitive != oldCaseSensitive) {
      Arrays.sort(thisNode.children, (c1, c2) -> StringUtil.compare(c1.getName(), c2.getName(), !newCaseSensitive));
    }
  }
void update(@NotNull FilePartNode parent, @NotNull FilePartNodeRoot root, @NotNull String debugSource, @Nullable Object debugInvalidationReason)