File Coverage

blib/lib/Bio/Phylo/NeXML/Entities.pm
Criterion Covered Total %
statement 6 48 12.5
branch 0 24 0.0
condition 0 12 0.0
subroutine 2 4 50.0
pod 2 2 100.0
total 10 90 11.1


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