File Coverage

lib/MKDoc/XML/Token.pm
Criterion Covered Total %
statement 102 102 100.0
branch 36 36 100.0
condition 22 24 91.6
subroutine 15 15 100.0
pod 12 12 100.0
total 187 189 98.9


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             MKDoc::XML::Token - XML Token Object
4              
5             =cut
6             package MKDoc::XML::Token;
7 23     23   225999 use strict;
  23         54  
  23         999  
8 23     23   157 use warnings;
  23         45  
  23         46351  
9              
10              
11             =head1 SYNOPSIS
12              
13             my $tokens = MKDoc::XML::Tokenizer->process_data ($some_xml);
14             foreach my $token (@{$tokens})
15             {
16             print "'" . $token->as_string() . "' is text\n" if (defined $token->text());
17             print "'" . $token->as_string() . "' is a self closing tag\n" if (defined $token->tag_self_close());
18             print "'" . $token->as_string() . "' is an opening tag\n" if (defined $token->tag_open());
19             print "'" . $token->as_string() . "' is a closing tag\n" if (defined $token->tag_close());
20             print "'" . $token->as_string() . "' is a processing instruction\n" if (defined $token->pi());
21             print "'" . $token->as_string() . "' is a declaration\n" if (defined $token->declaration());
22             print "'" . $token->as_string() . "' is a comment\n" if (defined $token->comment());
23             print "'" . $token->as_string() . "' is a tag\n" if (defined $token->tag());
24             print "'" . $token->as_string() . "' is a pseudo-tag (NOT text and NOT tag)\n" if (defined $token->pseudotag());
25             print "'" . $token->as_string() . "' is a leaf token (NOT opening tag)\n" if (defined $token->leaf());
26             }
27              
28              
29             =head1 SUMMARY
30              
31             L is an object representing an XML token produced by L.
32              
33             It has a set of methods to identify the type of token it is, as well as to help building a parsed
34             tree as in L.
35              
36             =head1 API
37              
38             =head2 my $token = new MKDoc::XML::Token ($string_token);
39              
40             Constructs a new MKDoc::XML::Token object.
41              
42             =cut
43             sub new
44             {
45 29     29 1 5729 my $class = shift;
46 29         45 my $token = shift;
47 29         120 return bless \$token, $class;
48             }
49              
50              
51             =head2 my $string_token = $token->as_string();
52              
53             Returns the string representation of this token so that:
54              
55             MKDoc::XML::Token->new ($token)->as_string eq $token
56              
57             is a tautology.
58              
59             =cut
60             sub as_string
61             {
62 19     19 1 51 my $self = shift;
63 19         123 return $$self;
64             }
65              
66              
67             =head2 my $node = $token->leaf();
68              
69             If this token is not an opening tag, this method will return its corresponding
70             node structure as returned by $token->text(), $token->tag_self_close(),
71             etc.
72              
73             Returns undef otherwise.
74              
75             =cut
76             sub leaf
77             {
78 8061     8061 1 13788 my $self = shift;
79 8061         16679 my $res = undef;
80            
81 8061         18030 $res = $self->comment();
82 8061 100       16726 defined $res and return $res;
83            
84 8057         20148 $res = $self->declaration();
85 8057 100       18725 defined $res and return $res;
86            
87 8055         23822 $res = $self->pi();
88 8055 100       15609 defined $res and return $res;
89            
90 8051         16368 $res = $self->tag_self_close();
91 8051 100       16341 defined $res and return $res;
92            
93 8044         28411 $res = $self->text();
94 8044 100       25308 defined $res and return $res;
95              
96 3938         15364 return;
97             }
98              
99              
100             =head2 my $node = $token->pseudotag();
101              
102             If this token is a comment, declaration or processing instruction,
103             this method will return $token->tag_comment(), $token_declaration()
104             or $token->pi() resp.
105              
106             Returns undef otherwise.
107              
108             =cut
109             sub pseudotag
110             {
111 8     8 1 18 my $self = shift;
112 8         14 my $res = undef;
113            
114 8         28 $res = $self->comment();
115 8 100       23 defined $res and return $res;
116            
117 7         18 $res = $self->declaration();
118 7 100       22 defined $res and return $res;
119            
120 6         14 $res = $self->pi();
121 6 100       23 defined $res and return $res;
122            
123 5         24 return;
124             }
125              
126              
127             =head2 my $node = $token->tag();
128              
129             If this token is an opening, closing, or self closing tag, this
130             method will return $token->tag_open(), $token->tag_close()
131             or $token->tag_self_close() resp.
132              
133             Returns undef otherwise.
134              
135             =cut
136             sub tag
137             {
138 27     27 1 40 my $self = shift;
139 27         27 my $res = undef;
140              
141 27         66 $res = $self->tag_open();
142 27 100       80 defined $res and return $res;
143            
144 17         46 $res = $self->tag_close();
145 17 100       47 defined $res and return $res;
146            
147 13         31 $res = $self->tag_self_close();
148 13 100       44 defined $res and return $res;
149            
150 9         28 return $res;
151             }
152              
153              
154             =head2 my $node = $token->comment();
155              
156             If this token object represents a declaration, the following structure
157             is returned:
158              
159             # this is
160             {
161             _tag => '~comment',
162             text => ' I like Pie. Pie is good ',
163             }
164            
165             Returns undef otherwise.
166              
167             =cut
168             sub comment
169             {
170 8077     8077 1 10380 my $self = shift;
171 8077         9651 my $node = undef;
172 8077 100       27817 $$self =~ /^<\!--/ and do {
173 6         25 $node = {
174             _tag => '~comment',
175             text => $$self,
176             };
177 6         27 $node->{text} =~ s/^<\!--//;
178 6         26 $node->{text} =~ s/-->$//;
179             };
180 8077         14716 $node;
181             }
182              
183              
184             =head2 my $node = $token->declaration();
185              
186             If this token object represents a declaration, the following structure
187             is returned:
188              
189             # this is
190             {
191             _tag => '~declaration',
192             text => 'DOCTYPE foo',
193             }
194              
195             Returns undef otherwise.
196              
197             =cut
198             sub declaration
199             {
200 8072     8072 1 11594 my $self = shift;
201 8072         10596 my $node = undef;
202 8072 100 100     46640 $$self !~ /^<\!--/ and $$self =~ /^
203 4         19 $node = {
204             _tag => '~declaration',
205             text => $$self,
206             };
207 4         638 $node->{text} =~ s/^
208 4         19 $node->{text} =~ s/>$//;
209             };
210 8072         18039 $node;
211             }
212              
213              
214             =head2 my $node = $token->pi();
215              
216             If this token object represents a processing instruction, the following structure
217             is returned:
218              
219             # this is
220             {
221             _tag => '~pi',
222             text => 'xml version="1.0" charset="UTF-8"',
223             }
224              
225             Returns undef otherwise.
226              
227             =cut
228             sub pi
229             {
230 8069     8069 1 10530 my $self = shift;
231 8069         10702 my $node = undef;
232 8069 100       17503 $$self =~ /^<\?/ and do {
233 6         23 $node = {
234             _tag => '~pi',
235             text => $$self,
236             };
237 6         25 $node->{text} =~ s/^<\?//;
238 6         22 $node->{text} =~ s/\>$//;
239 6         20 $node->{text} =~ s/\?$//;
240             };
241 8069         12458 $node;
242             }
243              
244              
245             =head2 my $node = $token->tag_open();
246              
247             If this token object represents an opening tag, the following structure
248             is returned:
249              
250             # this is
251             {
252             _tag => 'aTag',
253             _open => 1,
254             _close => 0,
255             foo => 'bar',
256             baz => 'buz',
257             }
258              
259             Returns undef otherwise.
260              
261             =cut
262             sub tag_open
263             {
264 3976     3976 1 7989 my $self = shift;
265 3976         5109 my $node = undef;
266             $$self !~ /^<\!/ and
267             $$self !~ /^<\// and
268             $$self !~ /\/>$/ and
269             $$self !~ /^<\?/ and
270 3976 100 100     38869 $$self =~ /^
      100        
271 1985         4202 my %node = _extract_attributes ($$self);
272 1985         9268 ($node{_tag}) = $$self =~ /.*?([A-Za-z0-9][A-Za-z0-9_:-]*)/;
273 1985         6656 $node{_open} = 1;
274 1985         3274 $node{_close} = 0;
275 1985         4426 $node = \%node;
276             };
277            
278 3976         16294 $node;
279             }
280              
281              
282             =head2 my $node = $token->tag_close();
283              
284             If this token object represents a closing tag, the following structure
285             is returned:
286              
287             # this is
288             {
289             _tag => 'aTag',
290             _open => 0,
291             _close => 1,
292             }
293              
294             Returns undef otherwise.
295              
296             =cut
297             sub tag_close
298             {
299 1994     1994 1 2482 my $self = shift;
300 1994         2486 my $node = undef;
301             $$self !~ /^<\!/ and
302             $$self =~ /^<\// and
303 1994 100 100     25863 $$self !~ /\/>$/ and do {
      66        
304 1974         7595 my %node = ();
305 1974         11094 ($node{_tag}) = $$self =~ /.*?([A-Za-z0-9][A-Za-z0-9_:-]*)/;
306 1974         4260 $node{_open} = 0;
307 1974         2584 $node{_close} = 1;
308 1974         4432 $node = \%node;
309             };
310            
311 1994         4915 $node;
312             }
313              
314              
315             =head2 my $node = $token->tag_self_close();
316              
317             If this token object represents a self-closing tag, the following structure
318             is returned:
319              
320             # this is
321             {
322             _tag => 'aTag',
323             _open => 1,
324             _close => 1,
325             foo => 'bar',
326             baz => 'buz',
327             }
328              
329             Returns undef otherwise.
330              
331             =cut
332             sub tag_self_close
333             {
334 8074     8074 1 20644 my $self = shift;
335 8074         14838 my $node = undef;
336             $$self !~ /^<\!/ and
337             $$self !~ /^<\// and
338             $$self =~ /\/>$/ and
339             # ((?:\w|:|-)+)\s*=\s*\"(.*?)\"/gs;
340 8074 100 100     90532 $$self =~ /^
      100        
      66        
341 14         45 my %node = _extract_attributes ($$self);
342 14         73 ($node{_tag}) = $$self =~ /.*?([A-Za-z0-9][A-Za-z0-9_:-]*)/;
343 14         33 $node{_open} = 1;
344 14         41 $node{_close} = 1;
345 14         27 $node = \%node;
346             };
347            
348 8074         22572 $node;
349             }
350              
351              
352             =head2 my $node = $token->text();
353              
354             If this token object represents a piece of text, then this text is returned.
355             Returns undef otherwise. TRAP! $token->text() returns a false value if this
356             text happens to be '0' or ''. So really you should use:
357              
358             if (defined $token->text()) {
359             ... do stuff...
360             }
361              
362             =cut
363             sub text
364             {
365 8052     8052 1 11296 my $self = shift;
366 8052 100       25599 return ($$self !~ /^
367             }
368              
369             our $S = "[ \\n\\t\\r]+";
370             our $NameStrt = "[A-Za-z_:]|[^\\x00-\\x7F]";
371             our $NameChar = "[A-Za-z0-9_:.-]|[^\\x00-\\x7F]";
372             our $Name = "(?:$NameStrt)(?:$NameChar)*";
373             our $EndTagCE = "$Name(?:$S)?>?";
374             our $AttValSE = "\"[^<\"]*\"|'[^<']*'";
375             our $ElemTagCE = "$Name((?:$S$Name(?:$S)?=(?:$S)?(?:$AttValSE))*)(?:$S)?/?>?";
376             our $ElemTagCE_Mod = "$S($Name)(?:$S)?=(?:$S)?($AttValSE)";
377              
378             our $RE_1 = qr /$ElemTagCE/;
379             our $RE_2 = qr /$ElemTagCE_Mod/;
380              
381              
382             sub _extract_attributes
383             {
384 1999     1999   2638 my $tag = shift;
385 1999         21060 my ($tags) = $tag =~ /$RE_1/g;
386 1999         17280 my %attr = $tag =~ /$RE_2/g;
387 1999         19352 foreach my $key (keys %attr)
388             {
389 1818         2998 my $val = $attr{$key};
390 1818         6400 $val =~ s/^(\"|\')//;
391 1818         5573 $val =~ s/(\"|\')$//;
392 1818         4934 $attr{$key} = $val;
393             }
394            
395 1999         8276 %attr;
396             }
397              
398              
399             1;
400              
401              
402             __END__