Text::Modify::Rule - Modification rule, which can be used to process


Text-Modify documentation Contained in the Text-Modify distribution.

Index


Code Index:

NAME

Top

Text::Modify::Rule - Modification rule, which can be used to process a Text::Buffer object.

SYNOPSIS

Top

  use Text::Modify::Rule;

  my $rule = new Text::Modify::Rule();

DESCRIPTION

Top

Text::Modify::Rule is a specific modification rule, to be applied for a Text::Modify object.

	my $rule = new Text::Modify::Rule();

Text::Modify uses Text::Modify::Rule to process the internal Text::Buffer object, representing the to be modified text.

Methods

Top

new
    $rule = new Text::Modify::Rule(%options);

This creates a new rule object, to be used with Text::Modify and perform the supplied modification tasks on the Text::Buffer object.

# TODO lots of documenation missing for options to new

process
	my $changes = $rule->process($textbuf);

Process the Text::Buffer object with this rule. Returns the number of modifications performed on the text. Each operation (add, replace, delete) is counted as a modification.

getModificationStats
	my ($match, $add, $del, $repl) = $rule->getModificationStats();

Returns to number of matches found, lines added, lines deleted and the number of replacements performed.

isError
getError
	if ($rule->isError()) { print "Error: " . $rule->getError() . "\n"; }

Simple error handling routines. isError returns 1 if an internal error has been raised. getError returns the textual error.

BUGS

Top

There definitly are some, if you find some, please report them.

LICENSE

Top

This software is released under the same terms as perl itself. You may find a copy of the GPL and the Artistic license at

   http://www.fsf.org/copyleft/gpl.html
   http://www.perl.com/pub/a/language/misc/Artistic.html

AUTHOR

Top

Roland Lammel (lammel@cpan.org)


Text-Modify documentation Contained in the Text-Modify distribution.

package Text::Modify::Rule;

# TODO Concept change to support blocks/insert/addIfMissing options
# maybe this has to be moved outside of rule, as a rule has no scope of work only a single line
# the concept has to be extended to working on the whole file/block, with a special concept to
# handle large files (>100KB) with autodetection of file size (slow but working)

use strict;
use vars qw($VERSION);
use Text::Buffer;

BEGIN {
	$VERSION="0.4";
}

#====================================================
# Possible usage and params:
# replace=>'texttoreplace',with=>'anothertext'
# 	optional:
#		ifMissing=>'insert|append|warn|fail'
#		match=>'first'	(last not implemented yet)
#====================================================
sub new {
	my $class = shift;
	my $self = {
				 addcount     => 0,
				 deletecount  => 0,
				 matchcount   => 0,
				 replacecount => 0,
				 ignorecase   => 1,
				 dryrun       => 0,
				 matchfirst   => 65535,
				 _debug       => 0
	};
	bless $self, $class;
	$self->_clearError();
	my %opts = @_;
	if ( $opts{debug} ) { $self->{_debug} = $opts{debug}; }
	$self->{'type'} = undef;
	if ( $opts{replace} ) {

		if ( defined( $opts{with} ) ) {
			$self->{type}  = 'replace';
			# TODO need to distinguish between string, wildcard, regex here
			$self->{replacetype} = $opts{type} || "regex";
			if ($self->{replacetype} eq "wildcard") {
				$self->{regex} = Text::Buffer->convertWildcardToRegex($opts{replace});
			}
			elsif ($self->{replacetype} eq "string") {
				$self->{regex} = Text::Buffer->convertStringToRegex($opts{replace});
			} else {
				$self->{regex} = $opts{replace};
			}
			# Set available options
			foreach (qw(replace string wildcard with dryrun ignorecase matchfirst ifmissing)) {
				$self->{$_} = $opts{$_} if ( defined( $opts{$_} ) );
			}
			$self->{with} =~ s?(^|[^\\])/?$1\\/?g;
			$self->_debug(sprintf("after escape: type=%s regex='%s' with='%s' (orig='%s')", $self->{replacetype}, $self->{regex}, $self->{with}, $opts{replace}));
			

			# Create the regex options from params
			$self->{opts} .= ( $self->{ignorecase} ? "i" : "" );
		}
	}
	elsif ( $opts{insert} ) {
		if ( defined( $opts{at} ) ) {
			$self->{type}  = 'insert';
			$self->{regex} = "";
			$self->{with} = $opts{insert};

			# Set available options
			foreach (qw(insert at dryrun ignorecase ifmissing)) {
				$self->{$_} = $opts{$_} if ( defined( $opts{$_} ) );
			}
		}
	}
	elsif ( $opts{delete} ) {
		$self->{type}  = 'delete';
		$self->{regex} = $opts{delete};

		# Set available options
		foreach (qw(dryrun ignorecase matchfirst)) {
			$self->{$_} = $opts{$_} if ( defined( $opts{$_} ) );
		}
	}
	elsif ( $opts{move} ) {

		# TODO move option not implemented
		if ( defined( $opts{to} ) ) {
			$self->{type}  = 'move';
			$self->{regex} = $opts{move};

			# Set available options
			foreach (qw(move to dryrun ignorecase matchfirst ifmissing)) {
				$self->{$_} = $opts{$_} if ( defined( $opts{$_} ) );
			}
		}
	}
	if ( !$self->{type} ) {
		$self->_debug( "Unknown type" );
		$self->_setError("Unknown Rule type");
		return undef;
	}
	if ( !defined( $self->{opts} ) ) { $self->{opts} = ""; }
	return $self;
}

sub getModificationStats {
	my $self = shift;
	return (($self->{matchcount} || 0), 
			($self->{addcount} || 0), 
			($self->{deletecount} || 0), 
			($self->{replacecount} || 0));
}

#==================================
# Process block of lines
#==================================
sub process {
	my $self = shift;
	my $txt  = shift;
	if ( !( $txt && $txt->isa("Text::Buffer") ) ) { return undef; }
	my @insertblock;
	my @appendblock;

	# Start processing
	$self->_debug( "processing rule of type $self->{type}, regex is " . 
		(defined($self->{regex}) ? $self->{regex} : "undef" ) . 
		", with is " . (defined($self->{with}) ? $self->{with} : "undef" ));
	my $i   = 0;
	my $abs = 0;
	my ( $match, $opts ) = ( $self->{regex}, $self->{opts} );
	my $found = 0;
	my $rc    = 1;    # Return code for this function
	$txt->goto('top');
	my $string = $txt->get();

	if ($self->{type} ne "insert") {
		while ( defined($string) ) {
			$abs++;
			if ( $self->{matchcount} >= $self->{matchfirst} ) {
				$self->_debug( "First matches reached, ignoring rest for this rule" );
				last;
			}
			eval "\$found = (\$string =~ /$match/$opts);";
			$self->_debug( "Eval: \$found = ('$string' =~ /$match/$opts) = $found" );
			if ($found) {
				$self->{matchcount}++;
	
				# TODO complete all functionality here (replace,insert,delete,move)
				$self->_debug(  "Found match on line $abs (rel $i): $string" );
				if ( $self->{type} eq "delete" ) {
					$self->{deletecount}++;
	
					# Should be deleted from array
					$self->_debug(  "deleting line" );
					$txt->delete();
					$string = $txt->get();
					next;
				}
				elsif ( $self->{type} eq "move" ) {
	
					# Should be deleted from array
					$self->{addcount}++;
					$self->{deletecount}++;
					$self->_debug(  "moving line" );
					if ( $self->{to} eq "top" ) {
						$txt->insert($string);
					}
					else {
						$txt->append($string);
					}
					$txt->delete();
					$string = $txt->get();
					next;
				}
				elsif ( $self->{type} eq "replace" ) {
					$self->_debug(  "replacing with $self->{'with'}" );
					my $tmp = $string;
					eval "\$tmp =~ s/$match/$self->{with}/g$opts";
					if ( $tmp ne $string ) {
						$self->{replacecount}++;
					}
					$txt->set($tmp);
				}
				else {
					$self->_setError("not processed by any rule");
					return 0;
				}
			}
			$string = $txt->next();
		}
	}

	if ( $self->{type} eq "insert" ) {

		# Should be deleted from array
		$self->{addcount}++;
		if ( $self->{at} eq "insert" ) {
			$self->_debug( "inserting line:" . $self->{with});
			$txt->insert( $self->{with} );
		}
		else {
			$self->_debug( "appending line" . $self->{with} );
			$txt->append( $self->{with} );
		}
	}

	# process missing elements
	$self->_debug(
				   "Processing ifmissing: ifmissing="
					 . ( $self->{ifmissing} ? $self->{ifmissing} : "unset" )
					 . " matches="
					 . $self->{matchcount}
	);
	if ( $self->{ifmissing} && $self->{matchcount} == 0 ) {

		# Add the missing element now
		$self->{addcount}++;
		if ( $self->{ifmissing} eq "insert" ) {
			$self->_debug( "inserting missing line" );
			$txt->insert( $self->{with} );
		}
		elsif ( $self->{ifmissing} eq "append" ) {
			$self->_debug( "appending missing line" );
			$txt->append( $self->{with} );
		}
		elsif ( $self->{ifmissing} eq "ignore" ) {
			$self->_debug( "ignoring missing line" );
		}
		elsif ( $self->{ifmissing} eq "error" ) {
			$self->_setError("Required line $match not found");
			$rc = 0;
		}
	}

	if ( $self->{_debug} ) {
		$self->_debug( "=== OUT ===\n" . $txt->dumpAsString() . "=== EOF ===" );
	}

	return $rc;
}

sub isError { my $self = shift; return ( $self->{error} ne "" ); }
sub getError    { return shift->{error}; }
sub _clearError { my $self = shift; $self->{error} = ""; }
sub _setError   { my $self = shift; $self->{error} = shift; }

sub _debug {
	my $self = shift;
	if ($#_ == -1) {
		return $self->{_debug};
	}
	elsif ( $self->{_debug} ) {
		print "[DEBUG] @_\n";
	}
}

1;

__END__