The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Tree::Persist::Base;

use strict;
use warnings;

use Scalar::Util qw( blessed );

our $VERSION = '1.12';

# ----------------------------------------------

sub new
{
	my($class) = shift;
	my($self)  = bless {}, $class;

	$self -> _init( @_ );

	return $self;

} # End of new.

# ----------------------------------------------

sub _init
{
	my($self) = shift;
	my($opts) = @_;

	$self->{_tree}       = undef;
	$self->{_autocommit} = (exists $opts->{autocommit} ? $opts->{autocommit} : 1);
	$self->{_changes}    = [];

	if ( exists $opts->{class} )
	{
		$self->{_class} = $opts->{class};
	}
	else
	{
		$self->{_class} = 'Tree';
	}

	if ( exists $opts->{tree} )
	{
		$self -> _set_tree( $opts->{tree} );
	}

	return $self;

} # End of _init.

# ----------------------------------------------

sub autocommit
{
	my($self) = shift;

	if ( @_ )
	{
		(my $old, $self->{_autocommit}) = ($self->{_autocommit}, (shift && 1) );

		return $old;
	}
	else
	{
		return $self->{_autocommit};
	}

} # End of autocommit.

# ----------------------------------------------

sub rollback
{
	my($self) = shift;

	if ( @{$self->{_changes} } )
	{
		$self -> _reload;

		$self->{_changes} = [];
	}

	return $self;

} # End of rollback.

# ----------------------------------------------

sub commit
{
	my($self) = shift;

	if ( @{$self->{_changes} } )
	{
		$self -> _commit;

		$self->{_changes} = [];
	}

	return $self;

} # End of commit.

# ----------------------------------------------

sub tree
{
	my($self) = shift;

	return $self->{_tree};

} # End of tree.

# ----------------------------------------------

sub _set_tree
{
	my($self)  = shift;
	my($value) = @_;

	$self->{_tree} = $value;

	$self->_install_handlers;

	return $self;

} # End of _set_tree.

# ----------------------------------------------

sub _install_handlers
{
	my($self) = shift;

	$self->{_tree} -> add_event_handler
	({
		add_child	 => $self -> _add_child_handler,
		remove_child => $self -> _remove_child_handler,
		value		 => $self -> _value_handler,
	});

	return $self;

} # End of _install_handlers.

# ----------------------------------------------

sub _strip_options
{
	my($self)   = shift;
	my($params) = @_;

	if ( @$params && ! blessed($params->[0]) && ref($params->[0]) eq 'HASH' )
	{
		return shift @$params;
	}
	else
	{
		return {};
	}

} # End of _strip_options.

# ----------------------------------------------

sub _add_child_handler
{
	my($self) = shift;

	return sub
	{
		my($parent, @children) = @_;
		my($options)           = $self -> _strip_options( \@children );

		push @{$self->{_changes} },
		{
			action   => 'add_child',
			children => [ @children ],
			options  => $options,
			parent   => $parent,
		};

		$self -> commit if ($self -> autocommit);
	};

} # End of _add_child_handler.

# ----------------------------------------------

sub _remove_child_handler
{
	my($self) = shift;

	return sub
	{
		my($parent, @children) = @_;
		my($options)           = $self -> _strip_options( \@children );

		push @{$self->{_changes} },
		{
			action   => 'remove_child',
			children => [ @children ],
			options  => $options,
			parent   => $parent,
		};

		$self -> commit if ($self -> autocommit);
	};

} # End of _remove_child_handler.

# ----------------------------------------------

sub _value_handler
{
	my($self) = shift;

	return sub
	{
		my($node, $old, $new) = @_;

		push @{$self->{_changes} },
		{
			action    => 'change_value',
			new_value => $node->value,
			node      => $node,
			old_value => $old,
		};

		$self -> commit if ($self -> autocommit);
	};

} # End of _value_handler.

# ----------------------------------------------

1;

__END__

=head1 NAME

Tree::Persist::Base - The base class for the Tree persistence plugin hierarchy

=head1 SYNOPSIS

See L<Tree::Persist/SYNOPSIS> or scripts/xml.demo.pl for sample code.

=head1 DESCRIPTION

This provides a useful baseclass for all the L<Tree::Persist> plugins.

Existing plugins are:

=over 4

=item * L<Tree::Persist::DB::SelfReferential>

=item * L<Tree::Persist::File::XML>

=back

=head1 PARAMETERS

Parameters are used in the call to L<Tree::Persist/connect({%opts})> or L<Tree::Persist/create_datastore({%opts})>.

These are the parameters provided for by this class. These are in addition to
whatever parameters sub-classes may use.

=over 4

=item * autocommit (optional)

This will be the initial setting for the autocommit value. (Please see
L</autocommit([$autocommit])> for more info.)

=item * class (optional)

This is the class that will be used to bless the nodes into, unless the
datastore specifies otherwise. It will default to 'Tree'.

=item * tree (required when calling create_datastore(), not used when calling connect() )

This is the object of type L<Tree> which is the root of the tree to write to the store.

=back

=head1 METHODS

=head2 new({%opts})

This is the constructor. C<%opts> is the set of parameters as described above.
Sub-classes may add their own optional or mandatory parameters.

End-user code never calls new(). You always call L<Tree::Persist/connect({%opts})>
or L<Tree::Persist/create_datastore({%opts})>

=head2 autocommit([$autocommit])

Here, [] indicate an optional parameter.

If called without any parameters, this will return the current autocommit
setting. If called with a parameter, it will set the autocommit flag to the
truth value of the parameter, then return the I<old> setting.

Autocommit, if turned on, will write any changes made to the tree directly to
the datastore. If it's off, you will have to explicitly issue a commit.

NOTE: If you turn autocommit off, then back on, it will B<not> issue a commit
until the next change occurs. At that time, it will commit all changes that
have occurred since the last commit.

=head2 commit()

If any changes are queued up, this will write them to the database. If there
are no changes, this is a no-op.

=head2 rollback()

If there are any changes queued up, this will discard those changes and reload
the tree from the datastore. If there are no changes, this is a no-op.

=head2 tree()

This will return the tree that is being persisted.

=head1 CODE COVERAGE

Please see the relevant section of L<Tree::Persist>.

=head1 SUPPORT

Please see the relevant section of L<Tree::Persist>.

=head1 AUTHORS

Rob Kinyon E<lt>rob.kinyon@iinteractive.comE<gt>

Stevan Little E<lt>stevan.little@iinteractive.comE<gt>

Thanks to Infinity Interactive for generously donating our time.

Co-maintenance since V 1.01 is by Ron Savage <rsavage@cpan.org>.
Uses of 'I' in previous versions is not me, but will be hereafter.

=head1 COPYRIGHT AND LICENSE

Copyright 2004, 2005 by Infinity Interactive, Inc.

L<http://www.iinteractive.com>

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

=cut