1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
package Parse::BBCode::Tag;
$Parse::BBCode::Tag::VERSION = '0.15';
use strict;
use warnings;
use Carp qw(croak carp);
use base 'Class::Accessor::Fast';
__PACKAGE__->follow_best_practice;
__PACKAGE__->mk_accessors(qw/ name attr attr_raw content
finished start end close class single type in_url num level auto_closed /);
sub add_content {
my ($self, $new) = @_;
my $content = $self->get_content;
if (ref $new) {
push @$content, $new;
return;
}
if (@$content and not ref $content->[-1]) {
$content->[-1] .= $new;
}
else {
push @$content, $new;
}
}
sub raw_text {
my ($self, %args) = @_;
%args = (
auto_close => 1,
%args,
);
my $auto_close = $args{auto_close};
my ($start, $end) = ($self->get_start, $self->get_end);
if (not $auto_close and $self->get_auto_closed) {
$end = '';
}
my $text = $start;
$text .= $self->raw_content(%args);
no warnings;
$text .= $end;
return $text;
}
sub _init_info {
my ($self, $num, $level) = @_;
$level ||= 0;
my $name = $self->get_name;
$num->{$name}++;
$self->set_num($num->{$name});
$self->set_level($level);
my $content = $self->get_content || [];
for my $c (@$content) {
next unless ref $c;
$c->_init_info($num, $level + 1);
}
}
sub walk {
my ($self, $type, $sub) = @_;
$type ||= 'bfs';
unless ($type eq 'bfs') {
croak "walk(): $type '$type' not implemented";
}
my $result = $sub->($self);
return if $result;
my $content = $self->get_content || [];
for my $c (@$content) {
next unless ref $c;
$c->walk($type, $sub);
}
}
sub raw_content {
my ($self, %args) = @_;
my $content = $self->get_content;
my $text = '';
#warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$self], ['self']);
for my $c (@$content) {
if (ref $c eq ref $self) {
$text .= $c->raw_text(%args);
}
else {
$text .= $c;
}
}
return $text;
}
sub _reduce {
my ($self) = @_;
if ($self->get_finished) {
return $self;
}
my @text = $self->get_start;
my $content = $self->get_content;
for my $c (@$content) {
if (ref $c eq ref $self) {
push @text, $c->_reduce;
}
else {
push @text, $c;
}
}
push @text, $self->get_end if defined $self->get_end;
return @text;
}
1;
__END__
=pod
=head1 NAME
Parse::BBCode::Tag - Tag Class for Parse::BBCode
=head1 DESCRIPTION
If you parse a bbcode with L<Parse::BBCode> C<Parse::BBCode::parse> returns
a parse tree of Tag objects.
=head1 METHODS
=over 4
=item add_content
$tag->add_content('string');
Adds 'string' to the end of the tag content.
$tag->add_content($another_tag);
Adds C<$another_tag> to the end of the tag content.
=item raw_text
my $bbcode = $tag->raw_text;
Returns the raw text of the parse tree, so all tags are converted
back to bbcode.
=item raw_content
my $bbcode = $tag->raw_content;
Returns the raw content of the tag without the opening and closing tags.
So if you have tag that was parsed from
[i]italic and [bold]test[/b][/i]
it will return
italic and [bold]test[/b]
=item walk
Utility to do a breadth first search ('bfs') over the parsed tree.
$tag->walk('bfs', sub {
# tag is in $_
...
return 0;
});
When the sub returns 1 it stops walking the tree. Useful for
finding a certain tag.
=back
=head1 ACCESSORS
The accessors of a tag are currently
name attr attr_raw content finished start end close class
You can call each accessor with C<get_*> and C<set_*>
=over 4
=item name
The tag name. for C<[i]...[/i]> it is C<i>, the lowercase tag name.
=item attr
TODO
=item attr_raw
The raw text of the attribute
=item content
An arrayref of the content of the tag, each element either a string
or a tag itself.
=item finished
Used during parsing, true if the end of the tag was found.
=item start
The original start string, e.g. 'C<[size=7]>'
=item end
The original end string, e.g. 'C<[/size]>'
=item close
True if the tag needs a closing tag. A tag which doesn't need a closing
tag is C<[*]> for example, inside of C<[list]> tags.
=item class
'block', 'inline' or 'url'
=item single
If this tag does not have a closing tag and also no content, like
[hr], for example, set this to true. Default is 0.
=item num
Absolute number of tag with this name in the tree. Useful if you want
to number code tags and offer download links.
=item level
Level of tag
For the tag [u] in the following bbcode
[b]bold [i]italic [u]underlined[/u][/i][/b]
it returns 3.
=back
=cut
|