/usr/local/CPAN/XUL-Node/XUL/Node/Server/NodeState.pm


package XUL::Node::Server::NodeState;

use strict;
use warnings;
use Carp;

use constant {
	SEPERATOR      => chr(2),
	PART_SEPERATOR => chr(1),
};

sub new {
	my ($class, %params) = @_;
	croak "cannot create state with no id" unless $params{id};
	return bless {
		id           => $params{id}, # unique id of node
		is_new       => 1,           # true until flushed 1st time
		is_destroyed => 0,           # false until destroyed
		buffer       => [],          # attributes changed since last flush
	}, $class;
}

sub flush {
	my $self = shift;
	my $out = $self->as_command;
	$self->set_old;
	$self->clear_buffer;
	return $out;
}

# command building ------------------------------------------------------------

sub as_command {
	my $self = shift;
	my $is_new       = $self->is_new;
	my $is_destroyed = $self->is_destroyed;
	return
		$is_new && $is_destroyed?
			'':
			$is_destroyed?
				$self->make_command_bye:
				$self->make_command_new. $self->get_buffer_as_commands;
}

sub make_command_new {
	my $self = shift;
	return '' unless $self->is_new;
	croak "cannot make new command with no tag on node state"
		unless $self->get_tag;
	my @args =
		($self->get_id, new => $self->get_tag, ($self->get_parent_id || 0));
	push(@args, $self->{index}) if exists $self->{index};
	make_command(@args);
}

sub make_command_bye {
	my $self = shift;
	my $parent_id = $self->get_parent_id || 0;
	make_command($self->get_id, 'bye');
}

sub get_buffer_as_commands {
	my $self = shift;
	local $_;
	return join '', map { $self->make_command_set(@$_) } $self->get_buffer;
}

sub make_command_set {
	my ($self, $key, $value) = @_;
	$value = '' unless defined $value;
	for ($value) {
		s/${\( SEPERATOR )}/_/g;
		s/${\( PART_SEPERATOR )}/_/g;
	}
	make_command($self->get_id, set => $key, $value);
}

# also used by tests to create oracle commands
sub make_command { join(PART_SEPERATOR, @_). SEPERATOR }

# accessors -------------------------------------------------------------------

sub get_id        { shift->{id}           }
sub get_tag       { shift->{tag}          }
sub is_new        { shift->{is_new}       }
sub get_parent_id { shift->{parent_id}    }
sub get_buffer    { @{shift->{buffer}}    }
sub is_destroyed  { shift->{is_destroyed} }

# modifiers -------------------------------------------------------------------

sub set_tag       { shift->{tag}          = lc pop          }
sub set_old       { shift->{is_new}       = 0               }
sub set_index     { shift->{index}        = pop             }
sub clear_buffer  { shift->{buffer}       = []              }
sub set_parent_id { shift->{parent_id}    = pop             }
sub set_destroyed { shift->{is_destroyed} = 1               }
sub set_attribute { push @{$_[0]->{buffer}}, [$_[1], $_[2]] }

1;