File Coverage

blib/lib/Bio/Phylo/NeXML/Entities.pm
Criterion Covered Total %
statement 9 51 17.6
branch 0 24 0.0
condition 0 12 0.0
subroutine 3 5 60.0
pod 2 2 100.0
total 14 94 14.8


line stmt bran cond sub pod time code
1             package Bio::Phylo::NeXML::Entities;
2 51     51   282 use strict;
  51         84  
  51         1186  
3 51     51   252 use warnings;
  51         96  
  51         1060  
4 51     51   213 use base 'Exporter';
  51         87  
  51         66100  
5             our @EXPORT_OK = qw'encode_entities decode_entities';
6              
7             my %entity2char = (
8             # Some normal chars that have special meaning in SGML context
9             '&' => '&', # ampersand
10             '>' => '>', # greater than
11             '&lt;' => '<', # less than
12             '&quot;' => '"', # double quote
13             '&apos;' => "'", # single quote
14            
15             # PUBLIC ISO 8879-1986//ENTITIES Added Latin 1//EN//HTML
16             '&#198;' => chr(198), # capital AE diphthong (ligature)
17             '&#193;' => chr(193), # capital A, acute accent
18             '&#194;' => chr(194), # capital A, circumflex accent
19             '&#192;' => chr(192), # capital A, grave accent
20             '&#197;' => chr(197), # capital A, ring
21             '&#195;' => chr(195), # capital A, tilde
22             '&#196;' => chr(196), # capital A, dieresis or umlaut mark
23             '&#199;' => chr(199), # capital C, cedilla
24             '&#208;' => chr(208), # capital Eth, Icelandic
25             '&#201;' => chr(201), # capital E, acute accent
26             '&#202;' => chr(202), # capital E, circumflex accent
27             '&#200;' => chr(200), # capital E, grave accent
28             '&#203;' => chr(203), # capital E, dieresis or umlaut mark
29             '&#205;' => chr(205), # capital I, acute accent
30             '&#206;' => chr(206), # capital I, circumflex accent
31             '&#204;' => chr(204), # capital I, grave accent
32             '&#207;' => chr(207), # capital I, dieresis or umlaut mark
33             '&#209;' => chr(209), # capital N, tilde
34             '&#211;' => chr(211), # capital O, acute accent
35             '&#212;' => chr(212), # capital O, circumflex accent
36             '&#210;' => chr(210), # capital O, grave accent
37             '&#216;' => chr(216), # capital O, slash
38             '&#213;' => chr(213), # capital O, tilde
39             '&#214;' => chr(214), # capital O, dieresis or umlaut mark
40             '&#222;' => chr(222), # capital THORN, Icelandic
41             '&#218;' => chr(218), # capital U, acute accent
42             '&#219;' => chr(219), # capital U, circumflex accent
43             '&#217;' => chr(217), # capital U, grave accent
44             '&#220;' => chr(220), # capital U, dieresis or umlaut mark
45             '&#221;' => chr(221), # capital Y, acute accent
46             '&#225;' => chr(225), # small a, acute accent
47             '&#226;' => chr(226), # small a, circumflex accent
48             '&#230;' => chr(230), # small ae diphthong (ligature)
49             '&#224;' => chr(224), # small a, grave accent
50             '&#229;' => chr(229), # small a, ring
51             '&#227;' => chr(227), # small a, tilde
52             '&#228;' => chr(228), # small a, dieresis or umlaut mark
53             '&#231;' => chr(231), # small c, cedilla
54             '&#233;' => chr(233), # small e, acute accent
55             '&#234;' => chr(234), # small e, circumflex accent
56             '&#232;' => chr(232), # small e, grave accent
57             '&#240;' => chr(240), # small eth, Icelandic
58             '&#235;' => chr(235), # small e, dieresis or umlaut mark
59             '&#237;' => chr(237), # small i, acute accent
60             '&#238;' => chr(238), # small i, circumflex accent
61             '&#236;' => chr(236), # small i, grave accent
62             '&#239;' => chr(239), # small i, dieresis or umlaut mark
63             '&#241;' => chr(241), # small n, tilde
64             '&#243;' => chr(243), # small o, acute accent
65             '&#244;' => chr(244), # small o, circumflex accent
66             '&#242;' => chr(242), # small o, grave accent
67             '&#248;' => chr(248), # small o, slash
68             '&#245;' => chr(245), # small o, tilde
69             '&#246;' => chr(246), # small o, dieresis or umlaut mark
70             '&#223;' => chr(223), # small sharp s, German (sz ligature)
71             '&#254;' => chr(254), # small thorn, Icelandic
72             '&#250;' => chr(250), # small u, acute accent
73             '&#251;' => chr(251), # small u, circumflex accent
74             '&#249;' => chr(249), # small u, grave accent
75             '&#252;' => chr(252), # small u, dieresis or umlaut mark
76             '&#253;' => chr(253), # small y, acute accent
77             '&#255;' => chr(255), # small y, dieresis or umlaut mark
78            
79             # Some extra Latin 1 chars that are listed in the HTML3.2 draft (21-May-96)
80             '&#169;' => chr(169), # copyright sign
81             '&#174;' => chr(174), # registered sign
82             '&#160;' => chr(160), # non breaking space
83            
84             # Additional ISO-8859/1 entities listed in rfc1866 (section 14)
85             '&#161;' => chr(161),
86             '&#162;' => chr(162),
87             '&#163;' => chr(163),
88             '&#164;' => chr(164),
89             '&#165;' => chr(165),
90             '&#166;' => chr(166),
91             '&#167;' => chr(167),
92             '&#168;' => chr(168),
93             '&#170;' => chr(170),
94             '&#171;' => chr(171),
95             '&#172;' => chr(172),
96             '&#173;' => chr(173),
97             '&#175;' => chr(175),
98             '&#176;' => chr(176),
99             '&#177;' => chr(177),
100             '&#185;' => chr(185),
101             '&#178;' => chr(178),
102             '&#179;' => chr(179),
103             '&#180;' => chr(180),
104             '&#181;' => chr(181),
105             '&#182;' => chr(182),
106             '&#183;' => chr(183),
107             '&#184;' => chr(184),
108             '&#186;' => chr(186),
109             '&#187;' => chr(187),
110             '&#188;' => chr(188),
111             '&#189;' => chr(189),
112             '&#190;' => chr(190),
113             '&#191;' => chr(191),
114             '&#215;' => chr(215),
115             '&#247;' => chr(247),
116             '&#338;' => chr(338),
117             '&#339;' => chr(339),
118             '&#352;' => chr(352),
119             '&#353;' => chr(353),
120             '&#376;' => chr(376),
121             '&#402;' => chr(402),
122             '&#710;' => chr(710),
123             '&#732;' => chr(732),
124             '&#913;' => chr(913),
125             '&#914;' => chr(914),
126             '&#915;' => chr(915),
127             '&#916;' => chr(916),
128             '&#917;' => chr(917),
129             '&#918;' => chr(918),
130             '&#919;' => chr(919),
131             '&#920;' => chr(920),
132             '&#921;' => chr(921),
133             '&#922;' => chr(922),
134             '&#923;' => chr(923),
135             '&#924;' => chr(924),
136             '&#925;' => chr(925),
137             '&#926;' => chr(926),
138             '&#927;' => chr(927),
139             '&#928;' => chr(928),
140             '&#929;' => chr(929),
141             '&#931;' => chr(931),
142             '&#932;' => chr(932),
143             '&#933;' => chr(933),
144             '&#934;' => chr(934),
145             '&#935;' => chr(935),
146             '&#936;' => chr(936),
147             '&#937;' => chr(937),
148             '&#945;' => chr(945),
149             '&#946;' => chr(946),
150             '&#947;' => chr(947),
151             '&#948;' => chr(948),
152             '&#949;' => chr(949),
153             '&#950;' => chr(950),
154             '&#951;' => chr(951),
155             '&#952;' => chr(952),
156             '&#953;' => chr(953),
157             '&#954;' => chr(954),
158             '&#955;' => chr(955),
159             '&#956;' => chr(956),
160             '&#957;' => chr(957),
161             '&#958;' => chr(958),
162             '&#959;' => chr(959),
163             '&#960;' => chr(960),
164             '&#961;' => chr(961),
165             '&#962;' => chr(962),
166             '&#963;' => chr(963),
167             '&#964;' => chr(964),
168             '&#965;' => chr(965),
169             '&#966;' => chr(966),
170             '&#967;' => chr(967),
171             '&#968;' => chr(968),
172             '&#969;' => chr(969),
173             '&#977;' => chr(977),
174             '&#978;' => chr(978),
175             '&#982;' => chr(982),
176             '&#8194;' => chr(8194),
177             '&#8195;' => chr(8195),
178             '&#8201;' => chr(8201),
179             '&#8204;' => chr(8204),
180             '&#8205;' => chr(8205),
181             '&#8206;' => chr(8206),
182             '&#8207;' => chr(8207),
183             '&#8211;' => chr(8211),
184             '&#8212;' => chr(8212),
185             '&#8216;' => chr(8216),
186             '&#8217;' => chr(8217),
187             '&#8218;' => chr(8218),
188             '&#8220;' => chr(8220),
189             '&#8221;' => chr(8221),
190             '&#8222;' => chr(8222),
191             '&#8224;' => chr(8224),
192             '&#8225;' => chr(8225),
193             '&#8226;' => chr(8226),
194             '&#8230;' => chr(8230),
195             '&#8240;' => chr(8240),
196             '&#8242;' => chr(8242),
197             '&#8243;' => chr(8243),
198             '&#8249;' => chr(8249),
199             '&#8250;' => chr(8250),
200             '&#8254;' => chr(8254),
201             '&#8260;' => chr(8260),
202             '&#8364;' => chr(8364),
203             '&#8465;' => chr(8465),
204             '&#8472;' => chr(8472),
205             '&#8476;' => chr(8476),
206             '&#8482;' => chr(8482),
207             '&#8501;' => chr(8501),
208             '&#8592;' => chr(8592),
209             '&#8593;' => chr(8593),
210             '&#8594;' => chr(8594),
211             '&#8595;' => chr(8595),
212             '&#8596;' => chr(8596),
213             '&#8629;' => chr(8629),
214             '&#8656;' => chr(8656),
215             '&#8657;' => chr(8657),
216             '&#8658;' => chr(8658),
217             '&#8659;' => chr(8659),
218             '&#8660;' => chr(8660),
219             '&#8704;' => chr(8704),
220             '&#8706;' => chr(8706),
221             '&#8707;' => chr(8707),
222             '&#8709;' => chr(8709),
223             '&#8711;' => chr(8711),
224             '&#8712;' => chr(8712),
225             '&#8713;' => chr(8713),
226             '&#8715;' => chr(8715),
227             '&#8719;' => chr(8719),
228             '&#8721;' => chr(8721),
229             '&#8722;' => chr(8722),
230             '&#8727;' => chr(8727),
231             '&#8730;' => chr(8730),
232             '&#8733;' => chr(8733),
233             '&#8734;' => chr(8734),
234             '&#8736;' => chr(8736),
235             '&#8743;' => chr(8743),
236             '&#8744;' => chr(8744),
237             '&#8745;' => chr(8745),
238             '&#8746;' => chr(8746),
239             '&#8747;' => chr(8747),
240             '&#8756;' => chr(8756),
241             '&#8764;' => chr(8764),
242             '&#8773;' => chr(8773),
243             '&#8776;' => chr(8776),
244             '&#8800;' => chr(8800),
245             '&#8801;' => chr(8801),
246             '&#8804;' => chr(8804),
247             '&#8805;' => chr(8805),
248             '&#8834;' => chr(8834),
249             '&#8835;' => chr(8835),
250             '&#8836;' => chr(8836),
251             '&#8838;' => chr(8838),
252             '&#8839;' => chr(8839),
253             '&#8853;' => chr(8853),
254             '&#8855;' => chr(8855),
255             '&#8869;' => chr(8869),
256             '&#8901;' => chr(8901),
257             '&#8968;' => chr(8968),
258             '&#8969;' => chr(8969),
259             '&#8970;' => chr(8970),
260             '&#8971;' => chr(8971),
261             '&#9001;' => chr(9001),
262             '&#9002;' => chr(9002),
263             '&#9674;' => chr(9674),
264             '&#9824;' => chr(9824),
265             '&#9827;' => chr(9827),
266             '&#9829;' => chr(9829),
267             '&#9830;' => chr(9830),
268             );
269              
270             # Make the opposite mapping
271             my %char2entity = map { $entity2char{$_} => $_ } keys %entity2char;
272              
273             # Fill in missing entities
274             #for (0 .. 255) {
275             # next if exists $char2entity{chr($_)};
276             # $char2entity{chr($_)} = "&#$_;";
277             #}
278              
279             sub encode_entities {
280 0     0 1   my ( $string, $chars ) = @_;
281 0           my %escape;
282 0 0         if ( $chars ) {
283 0           %escape = map { $_ => 1 } split //, $chars;
  0            
284             }
285             else {
286 0           %escape = map { $_ => 1 } keys %char2entity;
  0            
287             }
288 0           my @string = split //, $string;
289 0           for my $i ( 0 .. $#string ) {
290 0           my $c = $string[$i];
291 0 0 0       if ( $escape{$c} and $c ne '&' and $c ne ';' ) {
    0 0        
    0 0        
      0        
292 0           $string[$i] = $char2entity{$c};
293             }
294             elsif ( $escape{$c} and $c eq '&' ) {
295 0           my $maybe_entity = '';
296 0           FIND_SEMI: for my $j ( $i .. $#string ) {
297 0           $maybe_entity .= $string[$j];
298 0 0         last FIND_SEMI if $string[$j] eq ';';
299             }
300 0 0         if ( not exists $entity2char{$maybe_entity} ) {
301 0           $string[$i] = $char2entity{$c};
302             }
303             }
304             elsif( $escape{$c} and $c eq ';' ) {
305 0           my $maybe_entity = '';
306 0           FIND_AMP: for ( my $j = $i; $j >= 0; $j-- ) {
307 0           $maybe_entity = $string[$j] . $maybe_entity;
308 0 0         last FIND_SEMI if $string[$j] eq '&';
309             }
310 0 0         if ( not exists $entity2char{$maybe_entity} ) {
311 0           $string[$i] = $char2entity{$c};
312             }
313             }
314             }
315 0           return join '', @string;
316             }
317              
318             sub decode_entities {
319 0     0 1   my @results;
320 0           for my $string ( @_ ) {
321 0           my @string = split //, $string;
322 0           for my $i ( 0 .. $#string ) {
323 0           my $c = $string[$i];
324 0 0         if ( $c eq '&' ) {
325 0           my $maybe_entity = '';
326 0           my $length = 0;
327 0           FIND_SEMI: for my $j ( $i .. $#string ) {
328 0           $maybe_entity .= $string[$j];
329 0 0         last FIND_SEMI if $string[$j] eq ';';
330 0           $length++;
331             }
332 0 0         if ( exists $entity2char{$maybe_entity} ) {
333 0           $string[$i] = $entity2char{$maybe_entity};
334 0           splice( @string, $i + 1, $length );
335             }
336             }
337             }
338 0           push @results, join '', @string;
339             }
340 0 0         return wantarray ? @results : $results[0];
341             }
342              
343             1;
344              
345             __END__
346              
347             =head1 NAME
348              
349             Bio::Phylo::NeXML::Entities - Functions for dealing with XML entities
350              
351             =head1 DESCRIPTION
352              
353             This package provides subroutines for dealing with characters that need to be
354             encoded as XML entities, and decoded in other formats. For example: C<&> needs
355             to be encoded as C<&amp;> in XML. The subroutines have the same signatures and
356             the same names as those in the commonly-used module L<HTML::Entities>. They are
357             re-implemented here to avoid introducing dependencies.
358              
359             =head1 SUBROUTINES
360              
361             The following subroutines are utility functions that can be imported using:
362              
363             use Bio::Phylo::NeXML::Entities '/entities/';
364              
365             =over
366              
367             =item encode_entities
368              
369             Encodes problematic characters as XML entities
370              
371             Type : Utility function
372             Title : encode_entities
373             Usage : my $encoded = encode_entities('string with & or >','>&')
374             Function: Encodes entities in first argument string
375             Returns : Modified string
376             Args : Required, first argument: a string to encode
377             Optional, second argument: a string that specifies
378             which characters to encode
379              
380             =item decode_entities
381              
382             Decodes XML entities into the characters they code for
383              
384             Type : Utility function
385             Title : decode_entities
386             Usage : my $decoded = decode_entities('string with &amp; or &gt;')
387             Function: decodes encoded entities in argument string(s)
388             Returns : Array of decoded strings
389             Args : One or more encoded strings
390              
391             =back
392              
393             =head1 SEE ALSO
394              
395             There is a mailing list at L<https://groups.google.com/forum/#!forum/bio-phylo>
396             for any user or developer questions and discussions.
397              
398             =over
399              
400             =item L<Bio::Phylo::Manual>
401              
402             Also see the manual: L<Bio::Phylo::Manual> and L<http://rutgervos.blogspot.com>.
403              
404             =back
405              
406             =head1 CITATION
407              
408             If you use Bio::Phylo in published research, please cite it:
409              
410             B<Rutger A Vos>, B<Jason Caravas>, B<Klaas Hartmann>, B<Mark A Jensen>
411             and B<Chase Miller>, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
412             I<BMC Bioinformatics> B<12>:63.
413             L<http://dx.doi.org/10.1186/1471-2105-12-63>
414              
415              
416              
417             =cut
418