public function publish()
    {
        if (!class_exists($this->ObjectClass)) {
            throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
        }

        // Logical checks prior to publish
        if ($this->VersionBefore || $this->VersionAfter) {
            throw new BadMethodCallException("This ChangeSetItem has already been published");
        }

        // Skip unversioned records
        if (!$this->isVersioned()) {
            $this->VersionBefore = 0;
            $this->VersionAfter = 0;
            $this->write();
            return;
        }

        // Record state changed
        $this->VersionAfter = Versioned::get_versionnumber_by_stage(
            $this->ObjectClass,
            Versioned::DRAFT,
            $this->ObjectID,
            false
        );
        $this->VersionBefore = Versioned::get_versionnumber_by_stage(
            $this->ObjectClass,
            Versioned::LIVE,
            $this->ObjectID,
            false
        );

        // Enact change
        $changeType = $this->getChangeType();
        switch ($changeType) {
            case static::CHANGE_NONE: {
                break;
            }
            case static::CHANGE_DELETED: {
                // Non-recursive delete
                $object = $this->getObjectInStage(Versioned::LIVE);
                $object->deleteFromStage(Versioned::LIVE);
                break;
            }
            case static::CHANGE_MODIFIED:
            case static::CHANGE_CREATED: {
                // Non-recursive publish
                $object = $this->getObjectInStage(Versioned::DRAFT);
                $object->publishSingle();

                // Point after version to the published version actually created, not the
                // version copied from draft.
                $this->VersionAfter = Versioned::get_versionnumber_by_stage(
                    $this->ObjectClass,
                    Versioned::LIVE,
                    $this->ObjectID,
                    false
                );
                break;
            }
            default:
                throw new LogicException("Invalid change type: {$changeType}");
        }

        $this->write();
    }