File Coverage

blib/lib/Tag/Reader/Perl.pm
Criterion Covered Total %
statement 167 177 94.3
branch 75 90 83.3
condition 81 102 79.4
subroutine 14 14 100.0
pod 4 4 100.0
total 341 387 88.1


line stmt bran cond sub pod time code
1             package Tag::Reader::Perl;
2              
3 14     14   495727 use strict;
  14         138  
  14         415  
4 14     14   76 use warnings;
  14         34  
  14         568  
5              
6 14     14   1598 use Class::Utils qw(set_params);
  14         88798  
  14         609  
7 14     14   306 use Error::Pure qw(err);
  14         27  
  14         578  
8 14     14   82 use Readonly;
  14         23  
  14         29518  
9              
10             # Constants.
11             Readonly::Scalar my $EMPTY_STR => q{};
12              
13             our $VERSION = '0.02';
14              
15             # Constructor.
16             sub new {
17 55     55 1 40045 my ($class, @params) = @_;
18 55         155 my $self = bless {}, $class;
19              
20             # Process params.
21 55         220 set_params($self, @params);
22              
23             # Object.
24 53         1362 return $self;
25             }
26              
27             # Get tag token.
28             sub gettoken {
29 170     170 1 52816 my $self = shift;
30              
31             # Stay.
32 170         313 $self->{'stay'} = 0;
33 170         320 $self->{'spec_stay'} = 0;
34 170         295 $self->{'old_stay'} = 0;
35              
36             # Data.
37 170         468 $self->{'data'} = [];
38              
39             # Tag type.
40 170         296 $self->{'tag_type'} = '!data';
41 170         247 $self->{'tag_length'} = 0;
42              
43             # Braces.
44 170         345 ($self->{'brace'}, $self->{'bracket'}) = (0, 0);
45              
46             # Quote.
47 170         267 $self->{'quote'} = $EMPTY_STR;
48              
49             # Tag line.
50 170         272 $self->{'tagline'} = $self->{'textline'};
51 170         227 $self->{'tagcharpos'} = 0;
52              
53 170 100       505 if (exists $self->{'text'}) {
    50          
54 32   66     174 while (exists $self->{'text'}
      66        
55             && $self->{'stay'} < 98
56             && defined ($self->{'char'}
57             = substr $self->{'text'}, 0, 1)) {
58              
59 705         1217 $self->_gettoken;
60             }
61             } elsif (exists $self->{'filename'}) {
62 138   100     395 while ($self->{'stay'} < 98
      100        
63             && ((defined ($self->{'char'}
64             = shift @{$self->{'old_data'}}))
65             || defined ($self->{'char'}
66             = getc $self->{'filename'}))) {
67              
68 3764         7053 $self->_gettoken;
69             }
70             }
71              
72 170         268 my $data = join $EMPTY_STR, @{$self->{'data'}};
  170         619  
73 170 50       428 if ($data eq $EMPTY_STR) {
74 0         0 return ();
75             }
76             return wantarray ? ($data, $self->{'tag_type'}, $self->{'tagline'},
77 170 50       961 $self->{'tagcharpos'}) : $data;
78             }
79              
80             # Set file.
81             sub set_file {
82 52     52 1 2365 my ($self, $file, $force) = @_;
83 52 50 33     1597 if (! $file || ! -r $file) {
84 0         0 err 'Bad file.';
85             }
86 52 50 33     427 if (! $force && (defined $self->{'text'}
      33        
87             || defined $self->{'filename'})) {
88              
89 0         0 err 'Cannot set new data if exists data.';
90             }
91 52         82 my $inf;
92 52 50       2143 if (! open $inf, '<', $file) {
93 0         0 err "Cannot open file '$file'.";
94             }
95 52         213 $self->{'filename'} = $inf;
96              
97             # Reset values.
98 52         215 $self->_reset;
99              
100 52         166 return;
101             }
102              
103             # Set text.
104             sub set_text {
105 3     3 1 2140 my ($self, $text, $force) = @_;
106 3 50       11 if (! $text) {
107 0         0 err 'Bad text.';
108             }
109 3 0 0     10 if (! $force && (defined $self->{'text'}
      33        
110             || defined $self->{'filename'})) {
111              
112 0         0 err 'Cannot set new data if exists data.';
113             }
114 3         8 $self->{'text'} = $text;
115              
116             # Reset values.
117 3         10 $self->_reset;
118              
119 3         5 return;
120             }
121              
122             # Reset class values.
123             sub _reset {
124 55     55   111 my $self = shift;
125              
126             # Default values.
127 55         101 $self->{'charpos'} = 0;
128 55         115 $self->{'tagcharpos'} = 0;
129 55         97 $self->{'textline'} = 1;
130 55         90 $self->{'tagline'} = 0;
131 55         125 $self->{'old_data'} = [];
132              
133 55         118 return;
134             }
135              
136             # Main get token.
137             sub _gettoken {
138 4469     4469   5739 my $self = shift;
139              
140             # Char position.
141 4469         5680 $self->{'charpos'}++;
142              
143             # Normal tag.
144 4469 100       7187 if ($self->{'spec_stay'} == 0) {
145              
146             # Begin of normal tag.
147 1092 100 100     3223 if ($self->{'stay'} == 0 && $self->{'char'} eq '<') {
    100          
    50          
148              
149             # In tag.
150 166 100       247 if ($#{$self->{'data'}} == -1) {
  166         395  
151             $self->{'tagcharpos'}
152 111         205 = $self->{'charpos'};
153 111         168 $self->{'stay'} = 1;
154 111         168 push @{$self->{'data'}}, $self->{'char'};
  111         251  
155 111         202 $self->{'tag_length'} = 1;
156              
157             # Start of tag, after data.
158             } else {
159 55         94 $self->{'stay'} = 99;
160             }
161              
162             # Text.
163             } elsif ($self->{'stay'} == 0) {
164 190         262 push @{$self->{'data'}}, $self->{'char'};
  190         405  
165 190 100       437 if ($self->{'tagcharpos'} == 0) {
166             $self->{'tagcharpos'}
167 59         103 = $self->{'charpos'};
168             }
169              
170             # In a normal tag.
171             } elsif ($self->{'stay'} == 1) {
172              
173             # End of normal tag.
174 736 100 66     2366 if ($self->{'char'} eq '>') {
    100 100        
    100          
175 47         70 $self->{'stay'} = 98;
176 47         115 $self->_tag_type;
177 47         75 push @{$self->{'data'}}, $self->{'char'};
  47         104  
178 47         75 $self->{'tag_length'} = 0;
179              
180             # First charcter after '<' in normal tag.
181             } elsif ($self->{'tag_length'} == 1
182             && _is_first_char_of_tag($self->{'char'})) {
183              
184 111 100       274 if ($self->{'char'} eq q{!}) {
185 64         123 $self->{'spec_stay'} = 1;
186             }
187 111         146 push @{$self->{'data'}}, $self->{'char'};
  111         303  
188 111         180 $self->{'tag_length'}++;
189              
190             # Next character in normal tag (name).
191             } elsif ($self->{'tag_length'} > 1
192             && _is_in_tag_name($self->{'char'})) {
193              
194 247         322 push @{$self->{'data'}}, $self->{'char'};
  247         599  
195 247         396 $self->{'tag_length'}++;
196              
197             # Other characters.
198             } else {
199 331 50 33     920 if ($self->{'tag_length'} == 1
200             || $self->{'char'} eq '<') {
201              
202 0         0 err 'Bad tag.';
203             }
204 331         659 $self->_tag_type;
205 331         406 push @{$self->{'data'}}, $self->{'char'};
  331         794  
206             }
207             }
208              
209             # Other tags.
210             } else {
211              
212             # End of normal tag.
213 3377 100 100     10016 if ($self->{'char'} eq '>') {
    100          
    100          
    100          
    100          
214 90 100 100     571 if (($self->{'brace'} == 0
      100        
      100        
      100        
      100        
      100        
215             && $self->{'bracket'} == 0
216             && $self->{'spec_stay'} < 3)
217              
218             # Comment.
219             || ($self->{'spec_stay'} == 3
220             && join($EMPTY_STR,
221 6         47 @{$self->{'data'}}[-2 .. -1])
222             eq q{--})
223              
224             # CDATA.
225             || ($self->{'tag_type'} =~ /^!\[cdata\[/ms
226             && join($EMPTY_STR,
227 4         20 @{$self->{'data'}}[-2 .. -1])
228             eq ']]')) {
229              
230 64         108 $self->{'stay'} = 98;
231 64         92 $self->{'spec_stay'} = 0;
232 64         94 $self->{'tag_length'} = 0;
233             }
234 90 100       209 if ($self->{'spec_stay'} != 4) {
235 89         136 $self->{'bracket'}--;
236             }
237 90         127 push @{$self->{'data'}}, $self->{'char'};
  90         198  
238              
239             # Comment.
240             } elsif ($self->{'spec_stay'} == 3) {
241              
242             # '--' is bad.
243 56 50 66     109 if ($self->{'tag_length'} == 0
244 53         143 && join($EMPTY_STR, @{$self->{'data'}}
245             [-2 .. -1]) eq q{--}) {
246              
247 0         0 err 'Bad tag.';
248             }
249 56         115 $self->_tag_type;
250 56         411 push @{$self->{'data'}}, $self->{'char'};
  56         137  
251              
252             # Quote.
253             } elsif ($self->{'spec_stay'} == 4) {
254 812 100       1462 if ($self->{'char'} eq $self->{'quote'}) {
255 34         49 $self->{'spec_stay'} = $self->{'old_stay'};
256 34         59 $self->{'quote'} = $EMPTY_STR;
257             }
258 812         1033 push @{$self->{'data'}}, $self->{'char'};
  812         1714  
259              
260             } elsif ($self->{'char'} eq ']') {
261 13         29 push @{$self->{'data'}}, $self->{'char'};
  13         31  
262 13         23 $self->{'brace'}--;
263              
264             # Next character in normal tag (name).
265             } elsif ($self->{'tag_length'} > 1
266             && _is_in_tag_name($self->{'char'})) {
267              
268             # Comment detect.
269 409 100 100     1453 if (($self->{'tag_length'} == 2
      100        
270             || $self->{'tag_length'} == 3)
271             && $self->{'char'} eq q{-}) {
272              
273 6         10 $self->{'spec_stay'}++;
274             }
275 409 100       755 if ($self->{'char'} eq '[') {
276 7         21 $self->{'brace'}++;
277             }
278 409         526 push @{$self->{'data'}}, $self->{'char'};
  409         921  
279 409         670 $self->{'tag_length'}++;
280              
281             # Other characters.
282             } else {
283 1997 100 66     5528 if ($self->{'quote'} eq $EMPTY_STR
284             && $self->{'char'} eq q{"}) {
285              
286 30         47 $self->{'quote'} = q{"};
287 30         49 $self->{'old_stay'} = $self->{'spec_stay'};
288 30         45 $self->{'spec_stay'} = 4;
289             }
290 1997 100 100     5401 if ($self->{'quote'} eq $EMPTY_STR
291             && $self->{'char'} eq q{'}) {
292              
293 4         9 $self->{'quote'} = q{'};
294 4         16 $self->{'old_stay'} = $self->{'spec_stay'};
295 4         11 $self->{'spec_stay'} = 4;
296             }
297 1997 100       3324 if ($self->{'char'} eq '<') {
298 22         30 $self->{'bracket'}++;
299             }
300 1997 100       3229 if ($self->{'char'} eq '[') {
301 5         7 $self->{'brace'}++;
302             }
303 1997         3907 $self->_tag_type;
304 1997         2355 push @{$self->{'data'}}, $self->{'char'};
  1997         4547  
305             }
306             }
307              
308             # Remove char from buffer.
309 4469 100       7734 if ($self->{'stay'} != 99) {
310 4414 100       7559 if (exists $self->{'text'}) {
311 689 50       1029 if (length $self->{'text'} > 1) {
312 689         1203 $self->{'text'} = substr $self->{'text'}, 1;
313             } else {
314 0         0 delete $self->{'text'};
315             }
316             }
317             } else {
318 55 50 33     192 if (exists $self->{'filename'}
319             && defined $self->{'char'}) {
320              
321 55         73 push @{$self->{'old_data'}}, $self->{'char'};
  55         118  
322             }
323             }
324 4469 100 100     12213 if ($self->{'stay'} == 98 || $self->{'stay'} == 99) {
325 166 100       362 if ($self->{'stay'} == 99) {
326 55         76 $self->{'charpos'}--;
327             }
328             }
329              
330             # Next line.
331 4469 100       7764 if ($self->{'char'} eq "\n") {
332 89         126 $self->{'textline'}++;
333 89         126 $self->{'charpos'} = 0;
334             }
335              
336 4469         10343 return;
337             }
338              
339             # First character in tag.
340             sub _is_first_char_of_tag {
341 111     111   228 my $char = shift;
342 111 50 100     548 if ($char eq q{!} || $char eq q{/} || $char eq q{?}
      100        
      66        
343             || $char =~ /^[\d\w]+$/ms) {
344              
345 111         390 return 1;
346             }
347 0         0 return 0;
348             }
349              
350             # Normal characters in a tag name.
351             sub _is_in_tag_name {
352 732     732   1158 my $char = shift;
353 732 100 100     5756 if ($char eq q{:} || $char eq '[' || $char eq q{-} || $char eq q{%}
      100        
      100        
      100        
354             || $char =~ /^[\d\w]+$/ms) {
355              
356 656         1786 return 1;
357             }
358 76         214 return 0;
359             }
360              
361             # Process tag type.
362             sub _tag_type {
363 2431     2431   3374 my $self = shift;
364 2431 100       4062 if ($self->{'tag_length'} > 0) {
365             $self->{'tag_type'}
366 111         425 = lc join $EMPTY_STR, @{$self->{'data'}}
367 111         362 [1 .. $self->{'tag_length'} - 1];
368 111         220 $self->{'tag_length'} = 0;
369             }
370 2431         3344 return;
371             }
372              
373             1;
374              
375             __END__