private function computeChildChanges($mapping, $child, $parentId, $nodename, $parent = null)
    {
        $targetClass = $this->dm->getClassMetadata(get_class($child));
        $state = $this->getDocumentState($child);

        switch ($state) {
            case self::STATE_NEW:
                // cascade persist is implicit on children, no check for cascading

                // check if we have conflicting nodename information on creation.
                if ($targetClass->nodename) {
                    $assignedName = $targetClass->getFieldValue($child, $targetClass->nodename);
                    if ($assignedName && $assignedName != $nodename) {
                        throw IdException::conflictingChildName(
                            $parentId,
                            $mapping['fieldName'],
                            $nodename,
                            $child,
                            $assignedName
                        );
                    }
                }
                $childId = $parentId.'/'.$nodename;
                $targetClass->setIdentifierValue($child, $childId);

                if ($this->getDocumentById($childId)) {
                    $child = $this->merge($child);
                } else {
                    $this->persistNew($targetClass, $child, null, $parent);
                }

                $this->computeChangeSet($targetClass, $child);
                break;
            case self::STATE_DETACHED:
                throw new InvalidArgumentException('A detached document was found through a child relationship during cascading a persist operation: '.self::objToStr($child, $this->dm));
            default:
                if (PathHelper::getParentPath($this->getDocumentId($child)) !== $parentId) {
                    throw PHPCRException::cannotMoveByAssignment(self::objToStr($child, $this->dm));
                }
        }

        return $child;
    }