Subversion Repositories DevTools

Rev

Rev 1293 | Blame | Last modification | View Log | RSS feed

###############################################################################
# Codestriker: Copyright (c) 2001, 2002 David Sitsky.  All rights reserved.
# sits@users.sourceforge.net
#
# This program is free software; you can redistribute it and modify it under
# the terms of the GPL.

# Model object for handling topic file data.

package Codestriker::Model::Delta;

use strict;
use Encode qw(decode_utf8);

# Create the appropriate delta rows for this review. This gets called
# by File::get_deltas. It passes in the result of a sql query on the
# delta table.
sub new {
    my $class = shift;
    my $self = {};
    bless $self;
    
    $self->{filename} = decode_utf8($_[1]);
    $self->{revision} = $_[2];
    $self->{binary} = $_[3];
    $self->{old_linenumber} = $_[4];
    $self->{new_linenumber} = $_[5];
    $self->{text} = decode_utf8($_[6]);
    $self->{description} = (defined $_[7]) ? decode_utf8($_[7]) : "";
    $self->{filenumber} = $_[8];
    $self->{repmatch} = $_[9];
    $self->{only_delta_in_file} = 0;
    
    return $self;
}

# Retrieve the number of lines in the data for a given file.
# Input -1 for filenumber for a full file list of a specific topic.
sub get_delta_size($$$) {
    my ($type, $topicid, $filenumber) = @_;

    my @deltas = ();
    @deltas = $type->get_deltas($topicid, $filenumber);

    my $addedLines = 0;
    my $removedLines = 0;

    for (my $i = 0; $i <= $#deltas; $i++) {
        my $delta = $deltas[$i];
        my @lines = split '\n', $delta->{text};

        for (my $j = 0; $j <= $#lines; $j++) {
            my @aLine = $lines[$j];
            if (scalar( grep !/^\+/, @aLine ) == 0) {
                $addedLines++;
            } elsif (scalar( grep !/^\-/, @aLine ) == 0) {
                $removedLines++;
            }
        }
    }

    my $numLines = "";

    # Return the changes if one is non zero.
    if (($removedLines != 0) || ($addedLines != 0)) {
        $numLines = "+$addedLines,-$removedLines";
    }

    return $numLines;
}


# Retrieve the ordered list of deltas that comprise this review.
# Input -1 for filenumber for a full file list of a specific topic.
sub get_delta_set($$$) {
    my ($type, $topicid, $filenumber) = @_;
    return $type->get_deltas($topicid, $filenumber);
}

# Retrieve the delta for the specific filename and linenumber.
sub get_delta($$$) {
    my ($type, $topicid, $filenumber, $linenumber, $new) = @_;

    # Grab all the deltas for this file, and grab the delta with the highest
    # starting line number lower than or equal to the specific linenumber,
    # and matching the same file number.
    my @deltas = $type->get_deltas($topicid, $filenumber);
    my $found_delta = undef;
    for (my $i = 0; $i <= $#deltas; $i++) {
        my $delta = $deltas[$i];
        my $delta_linenumber = $new ?
            $delta->{new_linenumber} : $delta->{old_linenumber};
        if ($delta_linenumber <= $linenumber) {
            $found_delta = $delta;
        } else {
            # Passed the delta of interest, return the previous one found.
            return $found_delta;
        }
    }

    # Return the matching delta found, if any.
    return $found_delta;
}

# Retrieve the ordered list of deltas applied to a specific file. Class factory
# method, returns a list of delta objects.
sub get_deltas($$$) {
    my ($type, $topicid, $filenumber) = @_;
    
    # Obtain a database connection.
    my $dbh = Codestriker::DB::DBI->get_connection();

    # Setup the appropriate statement and execute it.
    my $select_deltas =
        $dbh->prepare_cached('SELECT delta_sequence, filename, revision, ' .
                             'binaryfile, old_linenumber, new_linenumber, ' .
                             'deltatext, description, topicfile.sequence, ' .
                             'repmatch FROM topicfile, delta ' .
                             'WHERE delta.topicid = ? AND ' .
                             'delta.topicid = topicfile.topicid AND ' .
                             'delta.file_sequence = topicfile.sequence ' .
                             (($filenumber != -1) ?
                              'AND topicfile.sequence = ? ' : '') .
                             'ORDER BY delta_sequence ASC');
    
    my $success = defined $select_deltas;
    if ($filenumber != -1) {
        $success &&= $select_deltas->execute($topicid, $filenumber);
    } else {
        $success &&= $select_deltas->execute($topicid);
    }
    
    # Store the results into an array of objects.
    my @results = ();
    if ($success) {
        my @data;
        while (@data = $select_deltas->fetchrow_array()) {
            my $delta = Codestriker::Model::Delta->new( @data );
            push @results, $delta;
        }
    }
    
    # The delta object needs to know if there are only delta objects
    # in this file so it can figure out if the delta is a new file.

    # Set the 'only_delta_in_file' field for each delta.
    if (scalar(@results) > 0) {
        # Assume the first delta is the only delta unless proven otherwise.
        $results[0]->{only_delta_in_file} = 1;
        for (my $i = 1; $i < scalar(@results); $i++) {
            if ($results[$i-1]->{filenumber} == $results[$i]->{filenumber}) {
                # If the previous file has the same filenumber, then
                # we know that neither the current file nor the
                # previous file are the only deltas for the file.
                $results[$i]->{only_delta_in_file} = 0;
                $results[$i-1]->{only_delta_in_file} = 0;
            } else {
                # This is the first delta of a file.  We'll assume it's the
                # only delta for this file.  If there are more deltas, the next
                # loop will correct the assumption.
                $results[$i]->{only_delta_in_file} = 1;
        }
        }
    }

    Codestriker::DB::DBI->release_connection($dbh, $success);
    die $dbh->errstr unless $success;
    
    return @results;
}


# This function looks at the delta, and will return 1 if the delta looks like
# it is a delta for a completly new file. This happens when a new file is added
# to SCM system, or the user updated a plain old text document to be reviewed
# without diffs. We need this function because we want to format this case
# special.
sub is_delta_new_file
{
    my $self = shift;
    
    # All of the following must be true:
    # - one delta for the entire file
    # - delta must start at line 1 (0ld, and new)
    my $is_new_file = 0;
    if ($self->{only_delta_in_file} &&
        $self->{old_linenumber} == 0 &&
        $self->{new_linenumber} == 1) {
        # All of the delta text lines must start with +.
        my @lines = split '\n', $self->{text};
        if ( scalar( grep !/^\+/, @lines ) == 0) {
            $is_new_file = 1;
        }
    }
    
    return $is_new_file;
}

1;