package VCP::Filter::svnlabeller;

=head1 NAME

VCP::Filter::svnlabeller - Create placeholder revisisions for labels

=head1 SYNOPSIS

  ## From the command line:
   vcp <source> svnlabeller: ...options... -- <dest>

  ## In a .vcp file:

    SVNLabeller:

=head1 DESCRIPTION

The filter is meant to be used with the L<VCP::Dest::svn> driver.

For each label in a revision, it generates a placeholder revision just
like the one used in branch. This allows the svn destination driver to
reuse the logic for creating branches to do tagging.

However, one label may be tagged from different branch for different
files in the tree. This makes it somtimes impossible for the mapping
to svn be a single C<svn cp>.

The svnlabeller filter assigns the placeholder's branch to
<originating branch>/<tag name>. This will make
L<VCP::Filter::changeset> groups the same tags from different branches
separately.

Also, the number of files being tagged from different branches are
counted. svnlabeller makes the branch contains the most files being
tagged the first for L<VCP::Dest::svn> to see, so it somehow produces
the optimal C<svn cp>.

Note that the placeholder revisions generated by svnlabeller contain a
single label attribute. Thisis invalid in the current revml dtd. In
other words, it won't work if you use svnlabeller with the revml
destination driver.

=cut

$VERSION = 1 ;

use strict ;
use VCP::Logger qw( lg pr );
use VCP::Debug qw( :debug );
use VCP::Utils qw( empty );
use VCP::Filter;
use VCP::Rev;
use base qw( VCP::Filter );

use fields (
   'LABELS',
   'LABEL_TIME',
);

sub new {
    my $class = ref $_[0] ? ref shift : shift;
    my $self = $class->SUPER::new( @_ ) ;

    return $self ;
}

sub handle_header {
    my VCP::Filter::svnlabeller $self = shift;
    $self->revs->set;  ## clear the list
    $self->SUPER::handle_header( @_ );
}

sub handle_rev {
    my VCP::Filter::svnlabeller $self = shift;
    my $r = shift;
    my $i = 0;
    my @labels = $r->labels;
    $r->set_labels ([]);

    $self->revs->add($r);
    for my $l (@labels) {
	debug "concoct label $l for ".$r->as_string if debugging;

	my $rev_id = $r->rev_id . ".$i";
	my $branch_id =
	    (defined $r->branch_id ? $r->branch_id : 'trunk')."/$l/";
	my $lr;

	unless ( $lr = $self->{LABELS}{$l}{$branch_id}[0]) {
	    $lr = VCP::Rev->new(
		action		=> "placeholder",
		id		=> '/'.$r->source_name . '#' . $rev_id,
		vcp_source_scm_fn	=> $r->vcp_source_scm_fn,
		name		=> $r->name,
		source_name	=> $r->source_name,
		source_filebranch_id=> $r->source_filebranch_id,
		source_repo_id	=> $r->source_repo_id,
		time		=> $r->time,
		branch_id	=> $branch_id,
		source_branch_id=> $r->source_branch_id,
		rev_id		=> $rev_id,
		source_rev_id	=> $rev_id,
		previous_id	=> $r->id,
		previous	=> $r,
		user_id		=> 'vcp',
	    );
	    $lr->set_svn_info([]);
	    $lr->set_labels([$l]);
	    $self->revs->add( $lr );
	    $self->{LABELS}{$l}{$branch_id}[0] = $lr;
	}

	++$self->{LABELS}{$l}{$branch_id}[1];

	push @{$lr->svn_info}, {
	    id		=> '/'.$r->source_name . '#' . $rev_id,
	    name	=> $r->name,
	    previous_id	=> $r->id,
	    previous	=> $r,
	    rev_id	=> $rev_id,
	    ref		=> $lr,
	};

	$self->{LABEL_TIME}{$l} = $r->time if !defined $self->{LABEL_TIME}{$l}
		|| $self->{LABEL_TIME}{$l} < $r->time;

	++$i;
    }
}

sub handle_footer {
    my VCP::Filter::svnlabeller $self = shift;

    for my $label (keys %{$self->{LABELS}}) {
	my $labelrev = $self->{LABELS}{$label};
	my @labelpref = sort {$labelrev->{$b}[1] <=> $labelrev->{$a}[1]}
		keys %$labelrev;

	my $i = 1;
	for (@{$labelrev}{@labelpref}) {
	    $_->[0]->time($self->{LABEL_TIME}{$label} + $i);
	    ++$i;
	}
    }

    undef $self->{LABELS};
    undef $self->{LABEL_TIME};

    $self->SUPER::handle_rev( $_ ) for $self->revs->get;
    $self->revs->remove_all;
    $self->SUPER::handle_footer( @_ );
}

=head1 AUTHOR

Chia-liang Kao <clkao@clkao.org>

=head1 COPYRIGHT

Copyright (c) 2003 Chia-liang Kao. All rights reserved.

=cut

1
