File Coverage

blib/lib/PPI/Normal.pm
Criterion Covered Total %
statement 59 59 100.0
branch 13 18 72.2
condition 5 9 55.5
subroutine 14 14 100.0
pod 3 4 75.0
total 94 104 90.3


line stmt bran cond sub pod time code
1             package PPI::Normal;
2              
3             =pod
4              
5             =head1 NAME
6              
7             PPI::Normal - Normalize Perl Documents
8              
9             =head2 DESCRIPTION
10              
11             Perl Documents, as created by PPI, are typically filled with all sorts of
12             mess such as whitespace and comments and other things that don't effect
13             the actual meaning of the code.
14              
15             In addition, because there is more than one way to do most things, and the
16             syntax of Perl itself is quite flexible, there are many ways in which the
17             "same" code can look quite different.
18              
19             PPI::Normal attempts to resolve this by providing a variety of mechanisms
20             and algorithms to "normalize" Perl Documents, and determine a sort of base
21             form for them (although this base form will be a memory structure, and
22             not something that can be turned back into Perl source code).
23              
24             The process itself is quite complex, and so for convenience and
25             extensibility it has been separated into a number of layers. At a later
26             point, it will be possible to write Plugin classes to insert additional
27             normalization steps into the various different layers.
28              
29             In addition, you can choose to do the normalization only as deep as a
30             particular layer, depending on aggressively you want the normalization
31             process to be.
32              
33             =head1 METHODS
34              
35             =cut
36              
37 66     66   403 use strict;
  66         117  
  66         1812  
38 66     66   266 use Carp ();
  66         93  
  66         1244  
39 66     66   210 use List::Util 1.33 ();
  66         1218  
  66         1314  
40 66     66   224 use PPI::Util '_Document';
  66         110  
  66         3941  
41 66     66   319 use PPI::Document::Normalized ();
  66         150  
  66         1017  
42 66     66   24299 use PPI::Normal::Standard ();
  66         186  
  66         1489  
43 66     66   322 use PPI::Singletons '%LAYER';
  66         107  
  66         9799  
44              
45             our $VERSION = '1.284';
46              
47             # With the registration mechanism in place, load in the main set of
48             # normalization methods to initialize the store.
49             PPI::Normal::Standard->import;
50              
51              
52              
53              
54             #####################################################################
55             # Configuration
56              
57             =pod
58              
59             =head2 register $function => $layer, ...
60              
61             The C<register> method is used by normalization method providers to
62             tell the normalization engines which functions need to be run, and
63             in which layer they apply.
64              
65             Provide a set of key/value pairs, where the key is the full name of the
66             function (in string form), and the value is the layer (see description
67             of the layers above) in which it should be run.
68              
69             Returns true if all functions are registered, or C<undef> on error.
70              
71             =cut
72              
73             sub register {
74 68     68 1 154 my $class = shift;
75 68         248 while ( @_ ) {
76             # Check the function
77 332         393 my $function = shift;
78             SCOPE: {
79 66     66   373 no strict 'refs';
  66         102  
  66         25680  
  332         306  
80 332 50 33     544 defined $function and defined &{"$function"}
  332         973  
81             or Carp::croak("Bad function name provided to PPI::Normal");
82             }
83              
84             # Has it already been added?
85 332 100   671   804 if ( List::Util::any { $_ eq $function } map @{$_}, values %LAYER ) {
  671         748  
  664         1379  
86 1         8 return 1;
87             }
88              
89             # Check the layer to add it to
90 331         650 my $layer = shift;
91 331 50 33     1108 defined $layer and $layer =~ /^(?:1|2)$/
92             or Carp::croak("Bad layer provided to PPI::Normal");
93              
94             # Add to the layer data store
95 331         361 push @{ $LAYER{$layer} }, $function;
  331         748  
96             }
97              
98 67         260 1;
99             }
100              
101              
102              
103              
104              
105             #####################################################################
106             # Constructor and Accessors
107              
108             =pod
109              
110             =head2 new
111              
112             my $level_1 = PPI::Normal->new;
113             my $level_2 = PPI::Normal->new(2);
114              
115             Creates a new normalization object, to which Document objects
116             can be passed to be normalized.
117              
118             Of course, what you probably REALLY want is just to call
119             L<PPI::Document>'s C<normalize> method.
120              
121             Takes an optional single parameter of the normalisation layer
122             to use, which at this time can be either "1" or "2".
123              
124             Returns a new C<PPI::Normal> object, or C<undef> on error.
125              
126             =cut
127              
128             sub new {
129 15     15 1 412036 my $class = shift;
130 15 100 100     128 my $layer = @_ ?
    100          
131             (defined $_[0] and ! ref $_[0] and $_[0] =~ /^[12]$/) ? shift : return undef
132             : 1;
133              
134             # Create the object
135 9         29 my $object = bless {
136             layer => $layer,
137             }, $class;
138              
139 9         23 $object;
140             }
141              
142             =pod
143              
144             =head1 layer
145              
146             The C<layer> accessor returns the normalisation layer of the object.
147              
148             =cut
149              
150 14     14 0 1353 sub layer { $_[0]->{layer} }
151              
152              
153              
154              
155              
156             #####################################################################
157             # Main Methods
158              
159             =pod
160              
161             =head2 process
162              
163             The C<process> method takes anything that can be converted to a
164             L<PPI::Document> (object, SCALAR ref, filename), loads it and
165             applies the normalisation process to the document.
166              
167             Returns a L<PPI::Document::Normalized> object, or C<undef> on error.
168              
169             =cut
170              
171             sub process {
172 11 100   11 1 396 my $self = ref $_[0] ? shift : shift->new;
173              
174             # PPI::Normal objects are reusable, but not re-entrant
175 11 50       27 return undef if $self->{Document};
176              
177             # Get or create the document
178 11 50       27 $self->{Document} = _Document(shift) or return undef;
179              
180             # Work out what functions we need to call
181 11         28 my @functions = map { @{ $LAYER{$_} } } ( 1 .. $self->layer );
  15         16  
  15         52  
182              
183             # Execute each function
184 11         36 foreach my $function ( @functions ) {
185 66     66   419 no strict 'refs';
  66         130  
  66         9500  
186 34         40 &{"$function"}( $self->{Document} );
  34         111  
187             }
188              
189             # Create the normalized Document object
190             my $Normalized = PPI::Document::Normalized->new(
191             Document => $self->{Document},
192 11 50       147 version => __PACKAGE__->VERSION,
193             functions => \@functions,
194             ) or return undef;
195              
196             # Done, clean up
197 11         29 delete $self->{Document};
198 11         31 return $Normalized;
199             }
200              
201             1;
202              
203             =pod
204              
205             =head1 NOTES
206              
207             The following normalisation layers are implemented. When writing
208             plugins, you should register each transformation function with the
209             appropriate layer.
210              
211             =head2 Layer 1 - Insignificant Data Removal
212              
213             The basic step common to all normalization, layer 1 scans through the
214             Document and removes all whitespace, comments, POD, and anything else
215             that returns false for its C<significant> method.
216              
217             It also checks each Element and removes known-useless sub-element
218             metadata such as the Element's physical position in the file.
219              
220             =head2 Layer 2 - Significant Element Removal
221              
222             After the removal of the insignificant data, Layer 2 removed larger, more
223             complex, and superficially "significant" elements, that can be removed
224             for the purposes of normalisation.
225              
226             Examples from this layer include pragmas, now-useless statement
227             separators (since the PDOM tree is holding statement elements), and
228             several other minor bits and pieces.
229              
230             =head2 Layer 3 - TO BE COMPLETED
231              
232             This version of the forward-port of the Perl::Compare functionality
233             to the 0.900+ API of PPI only implements Layer 1 and 2 at this time.
234              
235             =head1 TO DO
236              
237             - Write the other 4-5 layers :)
238              
239             =head1 SUPPORT
240              
241             See the L<support section|PPI/SUPPORT> in the main module.
242              
243             =head1 AUTHOR
244              
245             Adam Kennedy E<lt>adamk@cpan.orgE<gt>
246              
247             =head1 COPYRIGHT
248              
249             Copyright 2005 - 2011 Adam Kennedy.
250              
251             This program is free software; you can redistribute
252             it and/or modify it under the same terms as Perl itself.
253              
254             The full text of the license can be found in the
255             LICENSE file included with this module.
256              
257             =cut