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   10229 use strict;
  3         6  
  3         1092  
3 3     3   21 use warnings;
  3         7  
  3         2065  
4 3     3   31704 use PPI;
  3         386643  
  3         3353  
5              
6             sub new {
7 33     33 0 241627 my ($class) = @_;
8 33         328 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 102 my ($self, @arguments) = @_;
22              
23 9 100       46 die "source and destination tokens required" unless @arguments >= 2;
24              
25 8         41 $self->register(shift @arguments => shift @arguments);
26              
27 8 100 100     69 if (@arguments >= 2 && $arguments[0] eq '--in-statement') {
28 1         2 shift @arguments;
29 1         5 $self->set_replace_only_statement_which_has_token(shift @arguments);
30             }
31              
32 8         145 @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 3475 my ($self, $source, $destination) = @_;
43              
44 30         55 my $source_tokens = do {
45 30         511 my $document = PPI::Document::Fragment->new(\$source);
46 30         49902 [ map { $_->content } @{ $document->find('PPI::Token') } ];
  64         16538  
  30         186  
47             };
48              
49 30         4695 my $destination_tokens = do {
50 30         947 my $document = PPI::Document::Fragment->new(\$destination);
51 30         36281 [ map { $_->content } @{ $document->find('PPI::Token') } ];
  68         10101  
  30         118  
52             };
53              
54 30         1655 $self->{source_tokens} = $source_tokens;
55 30         378 $self->{destination_tokens} = $destination_tokens;
56             }
57              
58             sub set_replace_only_statement_which_has_token {
59 5     5 0 25 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 3095 my ($self) = @_;
66              
67 223         808 $self->{statement_in};
68             }
69              
70             sub source_tokens {
71 922     922 0 48251 my ($self) = @_;
72              
73 922         2291 $self->{source_tokens};
74             }
75              
76             sub destination_tokens {
77 23     23 0 71186 my ($self) = @_;
78              
79 23         351 $self->{destination_tokens};
80             }
81              
82             # refactor a file
83             # argumensts:
84             # $file: filename for refactoring
85             sub execute {
86 21     21   1129 my ($self, $file) = @_;
87              
88 21 100       78 return unless defined $self->source_tokens;
89              
90 20         99 my $document = PPI::Document->new($file);
91              
92             # When parse failed
93 20 100       194331 return unless $document;
94              
95 18         59 my $replaced = 0;
96 18 100       94 if (defined $self->replace_only_statement_which_has_token) {
97 3         18 $replaced += $self->_replace_in_statement($document);
98             } else {
99 15         65 $replaced += $self->_replace_all($document);
100             }
101              
102 18 100       151 $document->save($file) if $replaced;
103             }
104              
105             sub _replace_all {
106 15     15   30 my ($self, $document) = @_;
107              
108 15         67 my $tokens = $document->find('PPI::Token');
109              
110 15 50       38490 return 0 unless $tokens;
111              
112 15         34 my $replaced = 0;
113              
114 15         45 for my $token (@$tokens) {
115 834         1725 $replaced += $self->_try_replace($token);
116             }
117              
118 15         79 $replaced;
119             }
120              
121             sub _try_replace {
122 891     891   1257 my ($self, $token) = @_;
123 891         1464 my @matched = $self->_match($token);
124 891 100       3157 return 0 unless @matched;
125 13         25 my $first = shift @matched;
126 13         22 $first->set_content(join '', @{$self->destination_tokens});
  13         42  
127 13         102 $_->set_content('') for @matched; # removing `(` will delete (...). So set empty content.
128 13         133 1;
129             }
130              
131             sub _match {
132 891     891   1117 my ($self, $token) = @_;
133              
134 891         983 my @matched;
135              
136 891         995 for my $source (@{$self->source_tokens}) {
  891         1507  
137 936 100 66     7425 if ($token->content eq $source) {
    100 100        
138 53         242 push @matched, $token;
139 53         489 $token = $token->next_token;
140             } elsif (@matched && $token->next_token && ref $token eq 'PPI::Token::Whitespace') {
141             # ignore white spaces of source
142 5         508 push @matched, $token;
143 5         15 $token = $token->next_token;
144 5         235 redo;
145             } else {
146 878         6729 return;
147             }
148             }
149 13         1781 return @matched;
150             }
151              
152             sub _replace_in_statement {
153 3     3   7 my ($self, $document) = @_;
154              
155 3         18 my $statements = $document->find('PPI::Statement');
156              
157 3 50       12705 return 0 unless $statements;
158              
159 3         9 my $replaced = 0;
160              
161 3         9 for my $statement (@$statements) {
162 42 100       250 next if ref $statement eq 'PPI::Statement::Sub';
163 40 50       134 next if ref $statement eq 'PPI::Statement::Compound';
164 40 50       144 next if $statement->schild(0)->content eq 'do';
165              
166 40         720 my $found = 0;
167 40         147 my $tokens = $statement->find('PPI::Token');
168 40         16551 for my $token (@$tokens) {
169 201 100       552 if ($token->content eq $self->replace_only_statement_which_has_token) {
170 6         10 $found++;
171 6         13 last;
172             }
173             }
174 40 100       137 next unless $found;
175              
176 6         14 for my $token (@$tokens) {
177 57         158 $replaced += $self->_try_replace($token);
178             }
179             }
180              
181 3         12 $replaced;
182             }
183              
184             1;