###############################################################################
#
# LaTeX::TOM::Node
#
# This package defines an object for nodes in the TOM tree, and methods to go
# with them.
#
###############################################################################
package LaTeX::TOM::Node;
use strict;
use warnings;
use constant true => 1;
use constant false => 0;
our $VERSION = '0.05';
# Make a new Node: turn input hash into object.
#
sub _new {
my $class = shift;
my ($node) = @_;
return bless $node || {};
}
# "copy constructor"
#
sub copy {
my $node = shift;
return bless $node;
}
# Split a text node into two text nodes, with the first ending before point a,
# and the second starting after point b. actually returns NEW nodes, does not
# alter the input node.
#
# Note: a and b are relative to the contents of the node, not the original
# document.
#
# Note2: a and b are not jointly constrained. You can split after location x
# without losing any characters by setting a = x + 1 and b = x.
#
sub split {
my $node = shift;
my ($a, $b) = @_;
return (undef) x 2 unless $node->{type} eq 'TEXT';
my $left_text = substr $node->{content}, 0, $a;
my $right_text = substr $node->{content}, $b + 1, length($node->{content}) - $b;
my $left_node = LaTeX::TOM::Node->_new({
type => 'TEXT',
start => $node->{start},
end => $node->{start} + $a - 1,
content => $left_text,
});
my $right_node = LaTeX::TOM::Node->_new({
type => 'TEXT',
start => $node->{start} + $b + 1,
end => $node->{start} + length $node->{content},
content => $right_text,
});
return ($left_node, $right_node);
}
#
# accessor methods
#
sub getNodeType {
my $node = shift;
return $node->{type};
}
sub getNodeText {
my $node = shift;
return $node->{content};
}
sub setNodeText {
my $node = shift;
my ($text) = @_;
$node->{content} = $text;
}
sub getNodeStartingPosition {
my $node = shift;
return $node->{start};
}
sub getNodeEndingPosition {
my $node = shift;
return $node->{end};
}
sub getNodeMathFlag {
my $node = shift;
return $node->{math} ? true : false;
}
sub getNodePlainTextFlag {
my $node = shift;
return $node->{plaintext} ? true : false;
}
sub getNodeOuterStartingPosition {
my $node = shift;
return (defined $node->{ostart} ? $node->{ostart} : $node->{start});
}
sub getNodeOuterEndingPosition {
my $node = shift;
return (defined $node->{oend} ? $node->{oend} : $node->{end});
}
sub getEnvironmentClass {
my $node = shift;
return $node->{class};
}
sub getCommandName {
my $node = shift;
return $node->{command};
}
#
# linked-list accessors
#
sub getChildTree {
my $node = shift;
return $node->{children};
}
sub getFirstChild {
my $node = shift;
if ($node->{children}) {
return $node->{children}->{nodes}[0];
}
return undef;
}
sub getLastChild {
my $node = shift;
if ($node->{children}) {
return $node->{children}->{nodes}[-1];
}
return undef;
}
sub getPreviousSibling {
my $node = shift;
return $node->{prev};
}
sub getNextSibling {
my $node = shift;
return $node->{'next'};
}
sub getParent {
my $node = shift;
return $node->{parent};
}
# This is an interesting function, and kind of a hack because of the way the
# parser makes the current tree. Basically it will give you the next sibling
# that is a GROUP node, until it either hits the end of the tree level, a TEXT
# node which doesn't match /^\s*$/, or a COMMAND node.
#
# This is useful for finding all GROUPed parameters after a COMMAND node. You
# can just have a while loop that calls this method until it gets 'undef'.
#
# Note: this may be bad, but TEXT Nodes matching /^\s*\[[0-9]+\]$/ (optional
# parameter groups) are treated as if they were whitespace.
#
sub getNextGroupNode {
my $node = shift;
my $next = $node;
while ($next = $next->{'next'}) {
# found a GROUP node.
if ($next->{type} eq 'GROUP') {
return $next;
}
# see if we should skip a node
elsif ($next->{type} eq 'COMMENT' ||
($next->{type} eq 'TEXT' &&
$next->{content} =~ /^\s*(?:\[\s*[0-9]+\s*\]\s*)?$/)) {
next;
}
else {
return undef;
}
}
return undef;
}
1;