package SVN::Dump::Record; use strict; use warnings; use SVN::Dump::Headers; use SVN::Dump::Property; use SVN::Dump::Text; my $NL = "\012"; sub new { my ($class, @args) = @_; return bless {}, $class } for my $attr (qw( headers_block property_block text_block included_record )) { no strict 'refs'; *{"set_$attr"} = sub { $_[0]->{$attr} = $_[1]; }; *{"get_$attr"} = sub { $_[0]->{$attr} }; } sub type { my ($self) = @_; return $self->{headers_block} ? $self->{headers_block}->type() : ''; } sub has_text { return defined $_[0]->get_text_block(); } sub has_prop { return defined $_[0]->get_property_block(); } sub has_prop_only { return defined $_[0]->get_property_block() && !defined $_[0]->get_text_block(); } sub has_prop_or_text { return defined $_[0]->get_property_block() || defined $_[0]->get_text_block(); } # length methods sub property_length { my ($self) = @_; my $prop = $self->get_property_block(); return defined $prop ? length( $prop->as_string() ) : 0; } sub text_length { my ($self) = @_; my $text = $self->get_text(); return defined $text ? length($text) : 0; } sub as_string { my ($self) = @_; my $headers_block = $self->get_headers_block(); # the headers block my $string = $headers_block->as_string(); # the properties $string .= $self->get_property_block()->as_string() if $self->has_prop(); # the text $string .= $self->get_text_block()->as_string() if $self->has_text(); # is there an included record? if( my $included = $self->get_included_record() ) { $string .= $included->as_string() . $NL; } # add a record separator if needed my $type = $self->type(); return $string if $type =~ /\A(?:format|uuid)\z/; $string .= $type eq 'revision' ? $NL : $self->has_prop_or_text() ? $NL x 2 : $NL ; return $string; } sub update_headers { my ($self) = @_; my $proplen = $self->property_length(); my $textlen = $self->text_length(); $self->set_header( 'Text-content-length' => $textlen ) if defined $self->get_text_block(); $self->set_header( 'Prop-content-length', $proplen ); $self->set_header( 'Content-length' => $proplen + $textlen ); } # access methods to the inner blocks sub set_header { my ($self, $h, $v) = @_; my $headers = $self->get_headers_block() || $self->set_headers_block( SVN::Dump::Headers->new() ); $headers->set( $h, $v ); } sub get_header { my ($self, $h) = @_; return $self->get_headers_block()->get($h); } sub set_property { my ( $self, $k, $v ) = @_; my $prop = $self->get_property_block() || $self->set_property_block( SVN::Dump::Property->new() ); $prop->set( $k, $v ); $self->update_headers(); return $v; } sub get_property { my ($self, $k) = @_; return $self->get_property_block()->get($k); } sub delete_property { my ( $self, @keys ) = @_; my $prop = $self->get_property_block() || $self->set_property_block( SVN::Dump::Property->new() ); my @result = $prop->delete(@keys); $self->update_headers(); return wantarray ? @result : pop @result; # behave like delete() } sub set_text { my ($self, $t) = @_; my $text_block = $self->get_text_block() || $self->set_text_block( SVN::Dump::Text->new() ); $text_block->set( $t ); $self->update_headers(); return $t; } sub get_text { my ($self) = @_; my $text_block = $self->get_text_block(); return defined $text_block ? $text_block->get() : undef; } 1; __END__ =head1 NAME SVN::Dump::Record - A SVN dump record =head1 SYNOPSIS # SVN::Dump::Record objects are returns by the next_record() # method of SVN::Dump =head1 DESCRIPTION An C object represents a Subversion dump record. =head1 METHODS C provides the following gourps of methods: =head2 Record methods =over 4 =item new() Create a new empty C object. =item type() Return the record type, as guessed from its headers. The method dies if the record type cannot be determined. =item set_header( $h, $v ) Set the header C<$h> to the value C<$v>. =item get_header( $h ) Get the value of header C<$h>. =item set_property( $p, $v ) Set the property C<$p> to the value C<$v>. =item get_property( $p ) Get the value of property C<$p>. =item delete_property( @k ) Delete the =item set_text( $t ) Set the value of the text block. =item get_text() Get the value of the text block. =back =head2 Inner blocks manipulation A C is composed of several inner blocks of various kinds: C, C and C. The following methods provide access to these blocks: =over 4 =item set_headers_block( $headers ) =item get_headers_block() Get or set the C object that represents the record headers. =item set_property_block( $property ) =item get_property_block() Get or set the C object that represents the record property block. =item delete_property( @keys ) Delete the given properties. Behave like the builtin C. =item set_text_block( $text ) =item get_text_block() Get or set the C object that represents the record text block. =item set_included_record( $record ) =item get_included_record() Some special record are actually output recursiveley by B. The "record in the record" is stored within the parent record, so they are parsed as a single record with an included record. C / C give access to the included record. According to the Subversion sources (F), this is a "delete original, then add-with-history" node. The dump looks like this: Node-path: tags/mytag/myfile Node-kind: file Node-action: delete Node-path: tags/mytag/myfile Node-kind: file Node-action: add Node-copyfrom-rev: 23 Node-copyfrom-path: trunk/myfile Note that there is a single blank line after the first header block, and four after the included one. =item update_headers() Update the various C<...-length> headers. Used internally. B will be inconsistent.> =back =head2 Information methods =over 4 =item has_prop() Return a boolean value indicating if the record has a property block. =item has_text() Return a boolean value indicating if the record has a text block. =item has_prop_only() Return a boolean value indicating if the record has only a property block (and no text block). =item has_prop_or_text() Return a boolean value indicating if the record has a property block or a text block. =item property_length() Return the length of the property block. =item text_length() Return the length of the text block. =back =head2 Output method =over 4 =item as_string() Return a string representation of the record. B dumping a record currenly gives back the information that was read from the original dump. Which means that if you modified the property or text block of a record, the headers will be inconstent. =back =head1 ENCAPSULATION When using C to manipulate a SVN dump, one should not directly access the C, C and C components of a C object, but use the appropriate C and C methods of the record object. These methods compute the appropriate modifications of the header values, so that the C method outputs the correct information after any modification of the record. =head1 SEE ALSO C, C, C. =head1 COPYRIGHT & LICENSE Copyright 2006 Philippe 'BooK' Bruhat, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut