| Text-Modify documentation | Contained in the Text-Modify distribution. |
Text::Modify::Rule - Modification rule, which can be used to process a Text::Buffer object.
use Text::Modify::Rule; my $rule = new Text::Modify::Rule();
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.
$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
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.
my ($match, $add, $del, $repl) = $rule->getModificationStats();
Returns to number of matches found, lines added, lines deleted and the number of replacements performed.
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.
There definitly are some, if you find some, please report them.
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
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__