File Coverage

blib/lib/Parse/Man/DOM.pm
Criterion Covered Total %
statement 157 174 90.2
branch 5 6 83.3
condition 4 5 80.0
subroutine 62 73 84.9
pod 7 11 63.6
total 235 269 87.3


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2011-2012 -- leonerd@leonerd.org.uk
5              
6             package Parse::Man::DOM;
7              
8 7     7   223828 use strict;
  7         18  
  7         970  
9 7     7   41 use warnings;
  7         608  
  7         290  
10              
11 7     7   52 use base qw( Parse::Man );
  7         17  
  7         10955  
12              
13             our $VERSION = '0.02';
14              
15             =head1 NAME
16              
17             C - parse nroff-formatted manpages and return a DOM tree
18              
19             =head1 SYNOPSIS
20              
21             use Parse::Man::DOM;
22              
23             my $parser = Parse::Man::DOM->new;
24              
25             my $document = $parser->from_file( "my_manpage.1" );
26              
27             print "The manpage name is", $document->meta( "name" ), "\n";
28              
29             =head1 DESCRIPTION
30              
31             This subclass of L returns an object tree representing the parsed
32             content of the input file. The returned result will be an object of the
33             C class, which itself will contain other objects
34             nested within it.
35              
36             =cut
37              
38             sub parse
39             {
40 17     17 0 898 my $self = shift;
41              
42 17         59 local $self->{current_document} = $self->_make( document => );
43              
44 17         105 $self->SUPER::parse( @_ );
45              
46 17         2062 return $self->{current_document};
47             }
48              
49             sub para_TH
50             {
51 2     2 1 186 my $self = shift;
52 2         5 my ( $name, $section ) = @_;
53              
54 2         10 $self->{current_document}->add_meta( $self->_make( metadata => name => $name ) );
55 2         8 $self->{current_document}->add_meta( $self->_make( metadata => section => $section ) );
56             }
57              
58             sub para_SH
59             {
60 2     2 1 141 my $self = shift;
61 2         3 my ( $text ) = @_;
62 2         6 $self->{current_document}->append_para( $self->_make( heading => 1 => $text ) );
63             }
64              
65             sub para_SS
66             {
67 0     0 1 0 my $self = shift;
68 0         0 my ( $text ) = @_;
69 0         0 $self->{current_document}->append_para( $self->_make( heading => 2 => $text ) );
70             }
71              
72             sub para_P
73             {
74 16     16 1 23 my $self = shift;
75 16         24 my ( $opts ) = @_;
76 16         46 $self->{current_document}->append_para( $self->_make( para_plain => $opts, $self->_make( chunklist => ) ) );
77             }
78              
79             sub para_TP
80             {
81 1     1 1 2 my $self = shift;
82 1         3 my ( $opts ) = @_;
83 1         5 $self->{current_document}->append_para( $self->_make( para_term => $opts, $self->_make( chunklist => ), $self->_make( chunklist => ) ) );
84             }
85              
86             sub para_IP
87             {
88 1     1 1 4 my $self = shift;
89 1         2 my ( $opts ) = @_;
90 1         5 $self->{current_document}->append_para( $self->_make( para_indent => $opts, $self->_make( chunklist => ) ) );
91             }
92              
93             sub chunk
94             {
95 25     25 1 1031 my $self = shift;
96 25         76 my ( $text, %opts ) = @_;
97 25         76 $self->{current_document}->append_chunk( $self->_make( chunk => $text => $opts{font}, $opts{size} ) );
98             }
99              
100             sub join_para
101             {
102 4     4 0 54 my $self = shift;
103 4         12 $self->{current_document}->append_chunk( $self->_make( linebreak => ) );
104             }
105              
106             sub entity_br
107             {
108 0     0 0 0 my $self = shift;
109 0         0 $self->{current_document}->append_chunk( $self->_make( break => ) );
110             }
111              
112             sub entity_sp
113             {
114 0     0 0 0 my $self = shift;
115 0         0 $self->{current_document}->append_chunk( $self->_make( space => ) );
116             }
117              
118             sub _make
119             {
120 89     89   111 my $self = shift;
121 89         104 my $type = shift;
122 89 50       551 my $code = $self->can( "${type}_class" ) or die "Unable to make a ${type}";
123 89         471 return $code->()->new( @_ );
124             }
125              
126 7     7   53 use constant document_class => "Parse::Man::DOM::Document";
  7         13  
  7         665  
127 7     7   34 use constant metadata_class => "Parse::Man::DOM::Metadata";
  7         14  
  7         329  
128 7     7   33 use constant heading_class => "Parse::Man::DOM::Heading";
  7         10  
  7         295  
129 7     7   32 use constant para_plain_class => "Parse::Man::DOM::Para::Plain";
  7         11  
  7         482  
130 7     7   34 use constant para_term_class => "Parse::Man::DOM::Para::Term";
  7         22  
  7         347  
131 7     7   32 use constant para_indent_class => "Parse::Man::DOM::Para::Indent";
  7         12  
  7         300  
132 7     7   32 use constant chunklist_class => "Parse::Man::DOM::Chunklist";
  7         10  
  7         310  
133 7     7   35 use constant chunk_class => "Parse::Man::DOM::Chunk";
  7         11  
  7         285  
134 7     7   31 use constant space_class => "Parse::Man::DOM::Space";
  7         11  
  7         310  
135 7     7   43 use constant break_class => "Parse::Man::DOM::Break";
  7         25  
  7         310  
136 7     7   29 use constant linebreak_class => "Parse::Man::DOM::Linebreak";
  7         12  
  7         3200  
137              
138             package Parse::Man::DOM::Document;
139              
140             =head1 Parse::Man::DOM::Document
141              
142             Represents the document as a whole.
143              
144             =cut
145              
146             sub new
147             {
148 17     17   26 my $class = shift;
149 17         114 return bless { meta => {}, paras => [] }, $class;
150             }
151              
152             =head2 $meta = $document->meta( $key )
153              
154             Returns a C object for the named item of metadata.
155              
156             =over 4
157              
158             =item * name
159              
160             The page name given to the C<.TH> directive.
161              
162             =item * section
163              
164             The section number given to the C<.TH> directive.
165              
166             =back
167              
168             =cut
169              
170             sub meta
171             {
172 3     3   1007 my $self = shift;
173 3         6 my ( $name ) = @_;
174 3   50     23 return $self->{meta}{$name} || die "No meta defined for $name";
175             }
176              
177             sub add_meta
178             {
179 4     4   7 my $self = shift;
180 4         8 my ( $meta ) = @_;
181 4         33 $self->{meta}{ $meta->name } = $meta;
182             }
183              
184             =head2 @paras = $document->paras
185              
186             Returns a list of C or C or
187             subclass objects, containing the actual page content.
188              
189             =cut
190              
191             sub paras
192             {
193 16     16   350 my $self = shift;
194 16         20 return @{ $self->{paras} };
  16         97  
195             }
196              
197             sub append_para
198             {
199 20     20   34 my $self = shift;
200 20         25 push @{ $self->{paras} }, @_;
  20         84  
201             }
202              
203 29     29   91 sub last_para { shift->{paras}[-1] }
204              
205 29     29   62 sub append_chunk { shift->last_para->append_chunk( @_ ) }
206              
207             package Parse::Man::DOM::Metadata;
208              
209             =head1 Parse::Man::DOM::Metadata
210              
211             Represents a single item of metadata about the page.
212              
213             =cut
214              
215             sub new
216             {
217 4     4   7 my $class = shift;
218 4         29 return bless [ $_[0] => $_[1] ], $class;
219             }
220              
221             =head2 $name = $metadata->name
222              
223             The string name of the metadata
224              
225             =head2 $value = $metadata->value
226              
227             The string value of the metadata
228              
229             =cut
230              
231 4     4   23 sub name { shift->[0] }
232 2     2   10 sub value { shift->[1] }
233              
234             package Parse::Man::DOM::Heading;
235 7     7   42 use constant type => "heading";
  7         11  
  7         3875  
236              
237             =head1 Parse::Man::DOM::Heading
238              
239             Represents the contents of a C<.SH> or C<.SS> heading
240              
241             =cut
242              
243             sub new
244             {
245 2     2   3 my $class = shift;
246 2         9 return bless [ $_[0] => $_[1] ], $class;
247             }
248              
249             =head2 $level = $heading->level
250              
251             The heading level number; 1 for C<.SH>, 2 for C<.SS>
252              
253             =head2 $text = $heading->text
254              
255             The plain text string of the heading title
256              
257             =cut
258              
259 1     1   1398 sub level { shift->[0] }
260 1     1   7 sub text { shift->[1] }
261              
262             package Parse::Man::DOM::Para;
263              
264             =head1 Parse::Man::DOM::Para
265              
266             Represents a paragraph of formatted text content. Will be one of the following
267             subclasses.
268              
269             =cut
270              
271             =head2 $filling = $para->filling
272              
273             Returns true if filling (C<.fi>) is in effect, or false if no-filling (C<.nf>)
274             is in effect.
275              
276             =head2 $chunklist = $para->body
277              
278             Returns a C to represent the actual content of the
279             paragraph.
280              
281             =cut
282              
283 4     4   44 sub filling { shift->{filling} }
284 39     39   2431 sub body { shift->{body} }
285              
286             package Parse::Man::DOM::Para::Plain;
287 7     7   51 use base qw( Parse::Man::DOM::Para );
  7         11  
  7         6250  
288 7     7   45 use constant type => "plain";
  7         12  
  7         1246  
289              
290             =head1 Parse::Man::DOM::Para::Plain
291              
292             Represent a plain (C<.P> or C<.PP>) paragraph.
293              
294             =head2 $type = $para->type
295              
296             Returns C<"plain">.
297              
298             =cut
299              
300             sub new
301             {
302 17     17   32 my $class = shift;
303 17         27 my ( $opts, $body ) = @_;
304 17         34 return bless { (map { $_ => $opts->{$_} } qw( filling indent )), body => $body }, $class;
  34         215  
305             }
306              
307 26     26   86 sub append_chunk { shift->body->append_chunk( @_ ) }
308              
309             package Parse::Man::DOM::Para::Term;
310 7     7   60 use base qw( Parse::Man::DOM::Para );
  7         14  
  7         4007  
311 7     7   44 use constant type => "term";
  7         15  
  7         2226  
312              
313             =head1 Parse::Man::DOM::Para::Term
314              
315             Represents a term paragraph (C<.TP>).
316              
317             =head2 $type = $para->type
318              
319             Returns C<"term">.
320              
321             =cut
322              
323             sub new
324             {
325 1     1   3 my $class = shift;
326 1         2 my ( $opts, $term, $definition ) = @_;
327              
328 1         8 return bless { indent => $opts->{indent}, term => $term, definition => $definition }, $class;
329             }
330              
331             =head2 $chunklist = $para->term
332              
333             Returns a C for the defined term name.
334              
335             =head2 $chunklist = $para->definition
336              
337             Returns a C for the defined term definition.
338              
339             =cut
340              
341 2     2   7 sub term { shift->{term} }
342 2     2   9 sub definition { shift->{definition} }
343              
344             sub append_chunk
345             {
346 3     3   4 my $self = shift;
347 3         8 my ( $chunk ) = @_;
348 3 100 100     40 if( !$self->{term_done} and $chunk->isa( "Parse::Man::DOM::Linebreak" ) ) {
    100          
349 1         5 $self->{term_done} = 1;
350             }
351             elsif( !$self->{term_done} ) {
352 1         4 $self->term->append_chunk( $chunk );
353             }
354             else {
355 1         3 $self->definition->append_chunk( $chunk );
356             }
357             }
358              
359             package Parse::Man::DOM::Para::Indent;
360 7     7   42 use base qw( Parse::Man::DOM::Para::Plain );
  7         11  
  7         4220  
361 7     7   46 use constant type => "indent";
  7         13  
  7         9253  
362              
363             =head1 Parse::Man::DOM::Para::Indent
364              
365             Represents an indented paragraph (C<.IP>).
366              
367             =head2 $type = $para->type
368              
369             Returns C<"indent">.
370              
371             =cut
372              
373             package Parse::Man::DOM::Chunklist;
374              
375             =head1 Parse::Man::DOM::Chunklist
376              
377             Contains a list of C objects to represent paragraph
378             content.
379              
380             =cut
381              
382             sub new
383             {
384 19     19   30 my $class = shift;
385 19         112 return bless { chunks => [ @_ ] }, $class;
386             }
387              
388             =head2 @chunks = $chunklist->chunks
389              
390             Returns a list of C objects.
391              
392             =cut
393              
394 15     15   18 sub chunks { @{ shift->{chunks} } }
  15         126  
395              
396             sub append_chunk
397             {
398 28     28   67 my $self = shift;
399 28         31 push @{ $self->{chunks} }, @_;
  28         146  
400             }
401              
402             package Parse::Man::DOM::Chunk;
403              
404             =head1 Parse::Man::DOM::Chunk
405              
406             Represents a chunk of text with a particular format applied.
407              
408             =cut
409              
410             sub new
411             {
412 25     25   35 my $class = shift;
413 25         131 return bless [ @_ ], $class;
414             }
415              
416 0     0   0 sub is_linebreak { 0 }
417 0     0   0 sub is_space { 0 }
418 0     0   0 sub is_break { 0 }
419              
420             =head2 $text = $chunk->text
421              
422             The plain string value of the text for this chunk.
423              
424             =head2 $font = $chunk->font
425              
426             The font name in effect for this chunk. One of C<"R">, C<"B">, C<"I"> or
427             C<"SM">.
428              
429             =head2 $size = $chunk->size
430              
431             The size of this chunk, relative to the paragraph base of 0.
432              
433             =cut
434              
435 21     21   126 sub text { shift->[0] }
436 12     12   97 sub font { shift->[1] }
437 0     0   0 sub size { shift->[2] }
438              
439             package Parse::Man::DOM::Linebreak;
440 7     7   50 use base qw( Parse::Man::DOM::Chunk );
  7         14  
  7         5030  
441              
442             sub new
443             {
444 4     4   6 my $class = shift;
445 4         16 return bless [], $class;
446             }
447              
448 3     3   13 sub is_linebreak { 1 }
449              
450             package Parse::Man::DOM::Space;
451 7     7   61 use base qw( Parse::Man::DOM::Chunk );
  7         37  
  7         11664  
452              
453             sub new
454             {
455 0     0     my $class = shift;
456 0           return bless [], $class;
457             }
458              
459 0     0     sub is_space { 1 }
460              
461             package Parse::Man::DOM::Break;
462 7     7   51 use base qw( Parse::Man::DOM::Chunk );
  7         109  
  7         4444  
463              
464             sub new
465             {
466 0     0     my $class = shift;
467 0           return bless [], $class;
468             }
469              
470 0     0     sub is_break { 1 }
471              
472             =head1 AUTHOR
473              
474             Paul Evans
475              
476             =cut
477              
478             0x55AA;