private function restoreNode(
        NodeInterface $node,
        NodeInterface $frozenNode,
        $contentPropertyPrefix,
        $systemPropertyPrefix
    ) {
        // remove the properties for the given language, so that values being added since the last version are removed
        foreach ($node->getProperties() as $property) {
            if ($this->isRestoreProperty($property->getName(), $contentPropertyPrefix, $systemPropertyPrefix)) {
                $property->remove();
            }
        }

        // set all the properties from the saved version to the node
        foreach ($frozenNode->getPropertiesValues() as $name => $value) {
            if ($this->isRestoreProperty($name, $contentPropertyPrefix, $systemPropertyPrefix)) {
                $node->setProperty($name, $value);
            }
        }

        /** @var NodeInterface $childNode */
        foreach ($frozenNode->getNodes() as $childNode) {
            // create new node if it not exists
            if (!$node->hasNode($childNode->getName())) {
                $newNode = $node->addNode($childNode->getName());
                $newNode->setMixins($childNode->getPropertyValueWithDefault('jcr:frozenMixinTypes', []));
            }

            $this->restoreNode(
                $node->getNode($childNode->getName()),
                $childNode,
                $contentPropertyPrefix,
                $systemPropertyPrefix
            );
        }

        // remove child-nodes which do not exists in frozen-node
        foreach ($node->getNodes() as $childNode) {
            if (OnParentVersionAction::COPY !== $childNode->getDefinition()->getOnParentVersion()
                || $frozenNode->hasNode($childNode->getName())
            ) {
                continue;
            }

            $childNode->remove();
        }
    }