File Coverage

lib/App/PRT/Command/ReplaceToken.pm
Criterion Covered Total %
statement 93 93 100.0
branch 28 32 87.5
condition 8 9 88.8
subroutine 15 15 100.0
pod 0 7 0.0
total 144 156 92.3


line stmt bran cond sub pod time code
1             package App::PRT::Command::ReplaceToken;
2 3     3   3617 use strict;
  3         7  
  3         100  
3 3     3   15 use warnings;
  3         7  
  3         88  
4 3     3   1124 use PPI;
  3         206511  
  3         2806  
5              
6             sub new {
7 33     33 0 71818 my ($class) = @_;
8 33         234 bless {
9             source_tokens => undef,
10             destination_tokens => undef,
11             statement_in => undef,
12             }, $class;
13             }
14              
15             # parse arguments from CLI
16             # arguments:
17             # @arguments
18             # returns:
19             # @rest_arguments
20             sub parse_arguments {
21 9     9 0 131 my ($self, @arguments) = @_;
22              
23 9 100       45 die "source and destination tokens required" unless @arguments >= 2;
24              
25 8         36 $self->register(shift @arguments => shift @arguments);
26              
27 8 100 100     69 if (@arguments >= 2 && $arguments[0] eq '--in-statement') {
28 1         3 shift @arguments;
29 1         5 $self->set_replace_only_statement_which_has_token(shift @arguments);
30             }
31              
32 8         28 @arguments;
33             }
34              
35             # register a replacing rule
36             # arguments:
37             # $source: source token
38             # $destination: destinationination token
39             # discussions:
40             # should consider utf-8 flag ?
41             sub register {
42 30     30 0 7768 my ($self, $source, $destination) = @_;
43              
44 30         49 my $source_tokens = do {
45 30         257 my $document = PPI::Document::Fragment->new(\$source);
46 30         30878 [ map { $_->content } @{ $document->find('PPI::Token') } ];
  64         9896  
  30         129  
47             };
48              
49 30         1631 my $destination_tokens = do {
50 30         109 my $document = PPI::Document::Fragment->new(\$destination);
51 30         23849 [ map { $_->content } @{ $document->find('PPI::Token') } ];
  68         7645  
  30         147  
52             };
53              
54 30         1527 $self->{source_tokens} = $source_tokens;
55 30         109 $self->{destination_tokens} = $destination_tokens;
56             }
57              
58             sub set_replace_only_statement_which_has_token {
59 5     5 0 21 my ($self, $in) = @_;
60              
61 5         16 $self->{statement_in} = $in;
62             }
63              
64             sub replace_only_statement_which_has_token {
65 223     223 0 2516 my ($self) = @_;
66              
67 223         521 $self->{statement_in};
68             }
69              
70             sub source_tokens {
71 922     922 0 11195 my ($self) = @_;
72              
73 922         1465 $self->{source_tokens};
74             }
75              
76             sub destination_tokens {
77 23     23 0 19049 my ($self) = @_;
78              
79 23         115 $self->{destination_tokens};
80             }
81              
82             # refactor a file
83             # argumensts:
84             # $file: filename for refactoring
85             sub execute {
86 21     21   1087 my ($self, $file) = @_;
87              
88 21 100       72 return unless defined $self->source_tokens;
89              
90 20         97 my $document = PPI::Document->new($file);
91              
92             # When parse failed
93 20 100       170670 return unless $document;
94              
95 18         42 my $replaced = 0;
96 18 100       79 if (defined $self->replace_only_statement_which_has_token) {
97 3         10 $replaced += $self->_replace_in_statement($document);
98             } else {
99 15         62 $replaced += $self->_replace_all($document);
100             }
101              
102 18 100       116 $document->save($file) if $replaced;
103             }
104              
105             sub _replace_all {
106 15     15   44 my ($self, $document) = @_;
107              
108 15         63 my $tokens = $document->find('PPI::Token');
109              
110 15 50       30234 return 0 unless $tokens;
111              
112 15         41 my $replaced = 0;
113              
114 15         40 for my $token (@$tokens) {
115 834         1165 $replaced += $self->_try_replace($token);
116             }
117              
118 15         51 $replaced;
119             }
120              
121             sub _try_replace {
122 891     891   1170 my ($self, $token) = @_;
123 891         1221 my @matched = $self->_match($token);
124 891 100       1782 return 0 unless @matched;
125 13         25 my $first = shift @matched;
126 13         27 $first->set_content(join '', @{$self->destination_tokens});
  13         36  
127 13         82 $_->set_content('') for @matched; # removing `(` will delete (...). So set empty content.
128 13         90 1;
129             }
130              
131             sub _match {
132 891     891   1127 my ($self, $token) = @_;
133              
134 891         888 my @matched;
135              
136 891         903 for my $source (@{$self->source_tokens}) {
  891         1149  
137 936 100 66     2986 if ($token->content eq $source) {
    100 100        
138 53         203 push @matched, $token;
139 53         168 $token = $token->next_token;
140             } elsif (@matched && $token->next_token && ref $token eq 'PPI::Token::Whitespace') {
141             # ignore white spaces of source
142 5         259 push @matched, $token;
143 5         10 $token = $token->next_token;
144 5         161 redo;
145             } else {
146 878         4852 return;
147             }
148             }
149 13         682 return @matched;
150             }
151              
152             sub _replace_in_statement {
153 3     3   7 my ($self, $document) = @_;
154              
155 3         11 my $statements = $document->find('PPI::Statement');
156              
157 3 50       8332 return 0 unless $statements;
158              
159 3         8 my $replaced = 0;
160              
161 3         9 for my $statement (@$statements) {
162 42 100       96 next if ref $statement eq 'PPI::Statement::Sub';
163 40 50       74 next if ref $statement eq 'PPI::Statement::Compound';
164 40 50       92 next if $statement->schild(0)->content eq 'do';
165              
166 40         596 my $found = 0;
167 40         89 my $tokens = $statement->find('PPI::Token');
168 40         11503 for my $token (@$tokens) {
169 201 100       336 if ($token->content eq $self->replace_only_statement_which_has_token) {
170 6         9 $found++;
171 6         8 last;
172             }
173             }
174 40 100       99 next unless $found;
175              
176 6         12 for my $token (@$tokens) {
177 57         83 $replaced += $self->_try_replace($token);
178             }
179             }
180              
181 3         9 $replaced;
182             }
183              
184             1;