File Coverage

blib/lib/PPI/Dumper.pm
Criterion Covered Total %
statement 55 70 78.5
branch 30 54 55.5
condition 6 8 75.0
subroutine 6 8 75.0
pod 4 4 100.0
total 101 144 70.1


line stmt bran cond sub pod time code
1             package PPI::Dumper;
2              
3             =pod
4              
5             =head1 NAME
6              
7             PPI::Dumper - Dumping of PDOM trees
8              
9             =head1 SYNOPSIS
10              
11             # Load a document
12             my $Module = PPI::Document->new( 'MyModule.pm' );
13            
14             # Create the dumper
15             my $Dumper = PPI::Dumper->new( $Module );
16            
17             # Dump the document
18             $Dumper->print;
19              
20             =head1 DESCRIPTION
21              
22             The PDOM trees in PPI are quite complex, and getting a dump of their
23             structure for development and debugging purposes is important.
24              
25             This module provides that functionality.
26              
27             The process is relatively simple. Create a dumper object with a
28             particular set of options, and then call one of the dump methods to
29             generate the dump content itself.
30              
31             =head1 METHODS
32              
33             =cut
34              
35 9     9   9886 use strict;
  9         18  
  9         312  
36 9     9   35 use Params::Util qw{_INSTANCE};
  9         11  
  9         7221  
37              
38             our $VERSION = '1.284';
39              
40              
41              
42              
43              
44             #####################################################################
45             # Constructor
46              
47             =pod
48              
49             =head2 new $Element, param => value, ...
50              
51             The C<new> constructor creates a dumper, and takes as argument a single
52             L<PPI::Element> object of any type to serve as the root of the tree to
53             be dumped, and a number of key-E<gt>value parameters to control the output
54             format of the Dumper. Details of the parameters are listed below.
55              
56             Returns a new C<PPI::Dumper> object, or C<undef> if the constructor
57             is not passed a correct L<PPI::Element> root object.
58              
59             =over
60              
61             =item memaddr
62              
63             Should the dumper print the memory addresses of each PDOM element.
64             True/false value, off by default.
65              
66             =item indent
67              
68             Should the structures being dumped be indented. This value is numeric,
69             with the number representing the number of spaces to use when indenting
70             the dumper output. Set to '2' by default.
71              
72             =item class
73              
74             Should the dumper print the full class for each element.
75             True/false value, on by default.
76              
77             =item content
78              
79             Should the dumper show the content of each element. True/false value,
80             on by default.
81              
82             =item whitespace
83              
84             Should the dumper show whitespace tokens. By not showing the copious
85             numbers of whitespace tokens the structure of the code can often be
86             made much clearer. True/false value, on by default.
87              
88             =item comments
89              
90             Should the dumper show comment tokens. In situations where you have
91             a lot of comments, the code can often be made clearer by ignoring
92             comment tokens. True/false value, on by default.
93              
94             =item locations
95              
96             Should the dumper show the location of each token. The values shown are
97             [ line, rowchar, column ]. See L<PPI::Element/"location"> for a description of
98             what these values really are. True/false value, off by default.
99              
100             =back
101              
102             =cut
103              
104             sub new {
105 80     80 1 187 my $class = shift;
106 80 50       854 my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
107              
108             # Create the object
109 80         830 my $self = bless {
110             root => $Element,
111             display => {
112             memaddr => '', # Show the refaddr of the item
113             indent => 2, # Indent the structures
114             class => 1, # Show the object class
115             content => 1, # Show the object contents
116             whitespace => 1, # Show whitespace tokens
117             comments => 1, # Show comment tokens
118             locations => 0, # Show token locations
119             },
120             }, $class;
121              
122             # Handle the options
123 80         251 my @options = map { lc $_ } @_; # strict hashpairs # https://github.com/Perl-Critic/PPI/issues/201
  0         0  
124 80         169 my %options = @options;
125 80         122 foreach ( keys %{$self->{display}} ) {
  80         366  
126 560 50       925 if ( exists $options{$_} ) {
127 0 0       0 if ( $_ eq 'indent' ) {
128 0         0 $self->{display}->{indent} = $options{$_};
129             } else {
130 0         0 $self->{display}->{$_} = !! $options{$_};
131             }
132             }
133             }
134              
135 80         429 $self->{indent_string} = join '', (' ' x $self->{display}->{indent});
136              
137 80         229 $self;
138             }
139              
140              
141              
142              
143              
144             #####################################################################
145             # Main Interface Methods
146              
147             =pod
148              
149             =head2 print
150              
151             The C<print> method generates the dump and prints it to STDOUT.
152              
153             Returns as for the internal print function.
154              
155             =cut
156              
157             sub print {
158 0     0 1 0 CORE::print(shift->string);
159             }
160              
161             =pod
162              
163             =head2 string
164              
165             The C<string> method generates the dump and provides it as a
166             single string.
167              
168             Returns a string or undef if there is an error while generating the dump.
169              
170             =cut
171              
172             sub string {
173 0 0   0 1 0 my $array_ref = shift->_dump or return undef;
174 0         0 join '', map { "$_\n" } @$array_ref;
  0         0  
175             }
176              
177             =pod
178              
179             =head2 list
180              
181             The C<list> method generates the dump and provides it as a raw
182             list, without trailing newlines.
183              
184             Returns a list or the null list if there is an error while generating
185             the dump.
186              
187             =cut
188              
189             sub list {
190 80 50   80 1 26790 my $array_ref = shift->_dump or return ();
191 80         569 @$array_ref;
192             }
193              
194              
195              
196              
197              
198             #####################################################################
199             # Generation Support Methods
200              
201             sub _dump {
202 1813 50   1813   2491 my $self = ref $_[0] ? shift : shift->new(shift);
203 1813 100       5938 my $Element = _INSTANCE($_[0], 'PPI::Element') ? shift : $self->{root};
204 1813   100     2939 my $indent = shift || '';
205 1813   100     2441 my $output = shift || [];
206              
207             # Print the element if needed
208 1813         1737 my $show = 1;
209 1813 100       4998 if ( $Element->isa('PPI::Token::Whitespace') ) {
    100          
210 574 50       967 $show = 0 unless $self->{display}->{whitespace};
211             } elsif ( $Element->isa('PPI::Token::Comment') ) {
212 14 50       36 $show = 0 unless $self->{display}->{comments};
213             }
214 1813 50       3111 push @$output, $self->_element_string( $Element, $indent ) if $show;
215              
216             # Recurse into our children
217 1813 100       3699 if ( $Element->isa('PPI::Node') ) {
218 482         662 my $child_indent = $indent . $self->{indent_string};
219 482         1041 foreach my $child ( @{$Element->{children}} ) {
  482         841  
220 1733         2398 $self->_dump( $child, $child_indent, $output );
221             }
222             }
223              
224 1813         2312 $output;
225             }
226              
227             sub _element_string {
228 1813 50   1813   2086 my $self = ref $_[0] ? shift : shift->new(shift);
229 1813 50       5533 my $Element = _INSTANCE($_[0], 'PPI::Element') ? shift : $self->{root};
230 1813   100     2686 my $indent = shift || '';
231 1813         1740 my $string = '';
232              
233             # Add the memory location
234 1813 50       2699 if ( $self->{display}->{memaddr} ) {
235 0         0 $string .= $Element->refaddr . ' ';
236             }
237            
238             # Add the location if such exists
239 1813 50       3048 if ( $self->{display}->{locations} ) {
240 0         0 my $loc_string;
241 0 0       0 if ( $Element->isa('PPI::Token') ) {
242 0         0 my $location = $Element->location;
243 0 0       0 if ($location) {
244 0         0 $loc_string = sprintf("[ % 4d, % 3d, % 3d ] ", @$location);
245             }
246             }
247             # Output location or pad with 20 spaces
248 0   0     0 $string .= $loc_string || " " x 20;
249             }
250            
251             # Add the indent
252 1813 50       2457 if ( $self->{display}->{indent} ) {
253 1813         2227 $string .= $indent;
254             }
255              
256             # Add the class name
257 1813 50       2423 if ( $self->{display}->{class} ) {
258 1813         3550 $string .= ref $Element;
259             }
260              
261 1813 100       3938 if ( $Element->isa('PPI::Token') ) {
    100          
262             # Add the content
263 1331 50       2008 if ( $self->{display}->{content} ) {
264 1331         2136 my $content = $Element->content;
265 1331         1978 $content =~ s/\n/\\n/g;
266 1331         1476 $content =~ s/\t/\\t/g;
267 1331         1400 $content =~ s/\f/\\f/g;
268 1331         1745 $string .= " \t'$content'";
269             }
270              
271             } elsif ( $Element->isa('PPI::Structure') ) {
272             # Add the content
273 136 50       250 if ( $self->{display}->{content} ) {
274 136 50       279 my $start = $Element->start
275             ? $Element->start->content
276             : '???';
277 136 100       257 my $finish = $Element->finish
278             ? $Element->finish->content
279             : '???';
280 136         226 $string .= " \t$start ... $finish";
281             }
282             }
283            
284 1813         2959 $string;
285             }
286              
287             1;
288              
289             =pod
290              
291             =head1 SUPPORT
292              
293             See the L<support section|PPI/SUPPORT> in the main module.
294              
295             =head1 AUTHOR
296              
297             Adam Kennedy E<lt>adamk@cpan.orgE<gt>
298              
299             =head1 COPYRIGHT
300              
301             Copyright 2001 - 2011 Adam Kennedy.
302              
303             This program is free software; you can redistribute
304             it and/or modify it under the same terms as Perl itself.
305              
306             The full text of the license can be found in the
307             LICENSE file included with this module.
308              
309             =cut