blib/lib/Treex/PML/Backend/TEIXML.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 1 | 3 | 33.3 |
branch | n/a | ||
condition | n/a | ||
subroutine | 1 | 1 | 100.0 |
pod | n/a | ||
total | 2 | 4 | 50.0 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | ## This is a simple XML backend for TEI files -*-cperl-*- | ||||||
2 | ## author: Petr Pajas | ||||||
3 | # $Id: TEIXML.pm 2762 2006-07-28 13:57:23Z pajas $ ' | ||||||
4 | ############################################################# | ||||||
5 | |||||||
6 | package Treex::PML::Backend::TEIXML; | ||||||
7 | 1 | 1 | 880 | use Treex::PML; | |||
0 | |||||||
0 | |||||||
8 | use XML::LibXML; | ||||||
9 | use XML::LibXML::SAX::Parser; | ||||||
10 | use Treex::PML::IO qw(close_backend); | ||||||
11 | use strict; | ||||||
12 | |||||||
13 | use vars qw($VERSION); | ||||||
14 | BEGIN { | ||||||
15 | $VERSION='2.21'; # version template | ||||||
16 | } | ||||||
17 | |||||||
18 | sub open_backend { | ||||||
19 | my ($uri,$rw,$encoding)=@_; | ||||||
20 | # discard encoding and pass the rest to the Treex::PML::IO | ||||||
21 | Treex::PML::IO::open_backend($uri,$rw,($rw eq 'w' ? $encoding : undef)); | ||||||
22 | } | ||||||
23 | |||||||
24 | |||||||
25 | sub test { | ||||||
26 | # should be replaced with some better magic-test for TEI XML | ||||||
27 | my ($f)=@_; | ||||||
28 | |||||||
29 | if (ref($f)) { | ||||||
30 | my $line1=$f->getline(); | ||||||
31 | my $line2=$f->getline(); | ||||||
32 | return ($line1 =~ /^\s*<\?xml / and ($line2 =~ /^\s* ]/ |
||||||
33 | or $line2 =~ /^\s* | ||||||
34 | } else { | ||||||
35 | my $fh = Treex::PML::IO::open_backend($f,"r"); | ||||||
36 | my $test = $fh && test($fh); | ||||||
37 | Treex::PML::IO::close_backend($fh); | ||||||
38 | return $test; | ||||||
39 | } | ||||||
40 | } | ||||||
41 | |||||||
42 | sub read { | ||||||
43 | my ($input,$target_doc) = @_; | ||||||
44 | my $handler = Treex::PML::Backend::TEIXML::SAXHandler->new(TargetDocument => $target_doc); | ||||||
45 | my $p = XML::LibXML::SAX::Parser->new(Handler => $handler); | ||||||
46 | if (ref($input)) { | ||||||
47 | $p->parse(Source => { ByteStream => $input }); | ||||||
48 | } else { | ||||||
49 | $p->parse_uri($input); | ||||||
50 | } | ||||||
51 | |||||||
52 | return 1; | ||||||
53 | } | ||||||
54 | |||||||
55 | sub xml_quote { | ||||||
56 | local $_=$_[0]; | ||||||
57 | s/&/&/g; | ||||||
58 | s/\'/'/g; | ||||||
59 | s/\"/"/g; | ||||||
60 | s/>/>/g; | ||||||
61 | s/</g; | ||||||
62 | return $_; | ||||||
63 | } | ||||||
64 | |||||||
65 | sub xml_quote_pcdata { | ||||||
66 | local $_=$_[0]; | ||||||
67 | s/&/&/g; | ||||||
68 | s/>/>/g; | ||||||
69 | s/</g; | ||||||
70 | return $_; | ||||||
71 | } | ||||||
72 | |||||||
73 | |||||||
74 | sub write { | ||||||
75 | my ($output, $src_doc) = @_; | ||||||
76 | |||||||
77 | die "Require GLOB reference\n" unless ref($output); | ||||||
78 | |||||||
79 | my $rootdep=''; | ||||||
80 | if ($src_doc->FS->exists('dep') && | ||||||
81 | $src_doc->FS->isList('dep')) { | ||||||
82 | ($rootdep)=$src_doc->FS->listValues('dep'); | ||||||
83 | } | ||||||
84 | # xml_decl | ||||||
85 | print $output " | ||||||
86 | if ($src_doc->metaData('xmldecl_version') ne "") { | ||||||
87 | print $output " version=\"".$src_doc->metaData('xmldecl_version')."\""; | ||||||
88 | } else { | ||||||
89 | print $output " version=\"1.0\""; | ||||||
90 | } | ||||||
91 | if ($src_doc->encoding() ne "") { | ||||||
92 | print $output " encoding=\"".$src_doc->encoding()."\""; | ||||||
93 | } | ||||||
94 | if ($src_doc->metaData('xmldecl_standalone') ne "") { | ||||||
95 | print $output " standalone=\"".$src_doc->metaData('xmldecl_standalone')."\""; | ||||||
96 | } | ||||||
97 | print $output "?>\n"; | ||||||
98 | |||||||
99 | if ($src_doc->metaData('xml_doctype')) { | ||||||
100 | my $properties=$src_doc->metaData('xml_doctype'); | ||||||
101 | unless ($properties->{'Name'}) { | ||||||
102 | my $output = "DOCTYPE ".$properties->{'Name'}; | ||||||
103 | $output .= ' SYSTEM "'.$properties->{'SystemId'}.'"' if $properties->{'SystemId'}; | ||||||
104 | $output .= ' PUBLIC "'.$properties->{'PublicId'}.'"' if $properties->{'PublicId'}; | ||||||
105 | $output .= ' '.$properties->{'Internal'} if $properties->{'Internal'}; | ||||||
106 | print $output ""; | ||||||
107 | } | ||||||
108 | } | ||||||
109 | |||||||
110 | print $output " |
||||||
111 | # declare all list attributes as fLib. If fLib info exists, use it | ||||||
112 | # to get value identifiers | ||||||
113 | foreach my $attr (grep { $src_doc->FS->isList($_) } $src_doc->FS->attributes) { | ||||||
114 | my %valids; | ||||||
115 | if (ref($src_doc->metaData('fLib'))) { | ||||||
116 | my $flib=$src_doc->metaData('fLib'); | ||||||
117 | if (exists($flib->{$attr})) { | ||||||
118 | foreach (@{$flib->{$attr}}) { | ||||||
119 | $valids{$_->[1]} = $_->[0]; | ||||||
120 | } | ||||||
121 | } | ||||||
122 | } | ||||||
123 | print $output " |
||||||
124 | foreach ($src_doc->FS->listValues($attr)) { | ||||||
125 | print $output " | ||||||
126 | print $output " id=\"$valids{$_}\"" if (exists($valids{$_}) and $valids{$_} ne ""); | ||||||
127 | print $output " name=\"$attr\">", | ||||||
128 | " |
||||||
129 | } | ||||||
130 | print $output "\n"; | ||||||
131 | } | ||||||
132 | print $output "\n"; | ||||||
133 | print $output "
| ||||||
134 | if ($src_doc->tree(0)) { | ||||||
135 | my $tree0=$src_doc->tree(0); | ||||||
136 | foreach ($src_doc->FS->attributes()) { | ||||||
137 | print $output " $1=\"".xml_quote($tree0->{$_})."\"" | ||||||
138 | if (/^p_(.*)$/ and $tree0->{$_} ne ""); | ||||||
139 | } | ||||||
140 | } | ||||||
141 | print $output ">\n"; | ||||||
142 | |||||||
143 | foreach my $tree ($src_doc->trees) { | ||||||
144 | print $output " | ||||||
145 | foreach ($src_doc->FS->attributes()) { | ||||||
146 | print $output " $1=\"".xml_quote($tree->{$_})."\"" | ||||||
147 | if (/^s_(.*)/ and $tree->{$_} ne ""); | ||||||
148 | } | ||||||
149 | print $output ">\n"; | ||||||
150 | |||||||
151 | foreach my $node (sort { $a->{ord} <=> $b->{ord} } $tree->descendants) { | ||||||
152 | my $type=$node->{tei_type} || "w"; | ||||||
153 | print $output "<$type"; | ||||||
154 | foreach (grep { exists($node->{$_}) and | ||||||
155 | defined($node->{$_}) and | ||||||
156 | !/^[sp]_|^(?:form|type|ord|dep)$/ } | ||||||
157 | $src_doc->FS->attributes()) { | ||||||
158 | print $output " $_=\"".xml_quote($node->{$_})."\""; | ||||||
159 | } | ||||||
160 | print $output " dep=\"". | ||||||
161 | xml_quote($node->parent->parent ? ($node->parent->{id} | ||||||
162 | || $node->parent->{AID} #grrrrrrrr! | ||||||
163 | ) : $rootdep )."\""; | ||||||
164 | print $output ">"; | ||||||
165 | print $output xml_quote_pcdata($node->{form}); | ||||||
166 | print $output "$type>\n"; | ||||||
167 | } | ||||||
168 | |||||||
169 | print $output "\n"; | ||||||
170 | } | ||||||
171 | |||||||
172 | print $output "\n"; | ||||||
173 | print $output "\n"; | ||||||
174 | print $output "\n"; | ||||||
175 | } | ||||||
176 | |||||||
177 | |||||||
178 | # SAX TEI-XML to Treex::PML::Document transducer | ||||||
179 | package Treex::PML::Backend::TEIXML::SAXHandler; | ||||||
180 | use strict; | ||||||
181 | |||||||
182 | use vars qw($VERSION); | ||||||
183 | BEGIN { | ||||||
184 | $VERSION='2.21'; # version template | ||||||
185 | } | ||||||
186 | use Treex::PML; | ||||||
187 | |||||||
188 | sub new { | ||||||
189 | my ($class, %args) = @_; | ||||||
190 | bless \%args, $class; | ||||||
191 | } | ||||||
192 | |||||||
193 | sub start_document { | ||||||
194 | my ($self,$hash) = @_; | ||||||
195 | $self->{TargetDocument} ||= Treex::PML::Factory->createDocument(); | ||||||
196 | } | ||||||
197 | |||||||
198 | sub end_document { | ||||||
199 | my ($self) = @_; | ||||||
200 | my @header = ('@V form','@V form','@N ord'); | ||||||
201 | foreach my $attr (keys(%{$self->{FSAttrs}})) { | ||||||
202 | push @header, '@P '.$attr; | ||||||
203 | if (exists($self->{FSAttrSyms}->{$attr}) | ||||||
204 | and ref($self->{FSAttrSyms}->{$attr})) { | ||||||
205 | my ($list); | ||||||
206 | foreach (@{$self->{FSAttrSyms}->{$attr}}) { | ||||||
207 | $list.="|$_->[1]"; | ||||||
208 | } | ||||||
209 | push @header, '@L '.$attr.$list; | ||||||
210 | } | ||||||
211 | } | ||||||
212 | $self->{TargetDocument}->changeFS(Treex::PML::Factory->createFSFormat(\@header)); | ||||||
213 | $self->{TargetDocument}->changeMetaData('fLib' => $self->{FSAttrSyms}); | ||||||
214 | $self->{TargetDocument}; | ||||||
215 | } | ||||||
216 | |||||||
217 | sub xml_decl { | ||||||
218 | my ($self,$data) = @_; | ||||||
219 | $self->{TargetDocument}->changeEncoding($data->{Encoding} || 'iso-8859-2'); | ||||||
220 | $self->{TargetDocument}->changeMetaData('xmldecl_version' => $data->{Version}); | ||||||
221 | $self->{TargetDocument}->changeMetaData('xmldecl_standalone' => $data->{Standalone}); | ||||||
222 | } | ||||||
223 | |||||||
224 | sub characters { | ||||||
225 | my ($self,$hash) = @_; | ||||||
226 | return unless $self->{Node}; | ||||||
227 | if (($self->{Node}{tei_type} eq 'w') or | ||||||
228 | ($self->{Node}{tei_type} eq 'c')) { | ||||||
229 | my $str = $hash->{Data}; | ||||||
230 | if ($]>=5.008) { | ||||||
231 | # leave data in the UTF-8 encoding | ||||||
232 | $self->{Node}->{form}.=$str; | ||||||
233 | } else { | ||||||
234 | $self->{Node}->{form}=$self->{Node}->{form}. | ||||||
235 | XML::LibXML::decodeFromUTF8($self->{TargetDocument}->encoding(),$str); | ||||||
236 | } | ||||||
237 | } | ||||||
238 | } | ||||||
239 | |||||||
240 | sub start_element { | ||||||
241 | my ($self, $hash) = @_; | ||||||
242 | my $elem = $hash->{Name}; | ||||||
243 | my $attr = $hash->{Attributes}; | ||||||
244 | my $target_doc = $self->{TargetDocument}; | ||||||
245 | |||||||
246 | if ($elem eq 'p') { | ||||||
247 | $self->{DocAttributes}=$attr; | ||||||
248 | } elsif ($elem eq 'f') { | ||||||
249 | $self->{CurrentFSAttr}=$attr->{"{}name"}->{Value}; | ||||||
250 | $self->{CurrentFSAttrID}=$attr->{"{}id"}->{Value}; | ||||||
251 | $self->{FSAttrs}->{$attr->{"{}name"}->{Value}}=1; | ||||||
252 | } elsif ($elem eq 'sym') { | ||||||
253 | push @{$self->{FSAttrSyms}->{$self->{CurrentFSAttr}}}, | ||||||
254 | [$self->{CurrentFSAttrID},$attr->{"{}value"}->{Value}]; | ||||||
255 | } elsif ($elem eq 's') { | ||||||
256 | my $node = $self->{Node} = $self->{Tree} = $target_doc->new_tree($target_doc->lastTreeNo+1); | ||||||
257 | $node->{ord} = 0; | ||||||
258 | $self->{LastOrd} = 0; | ||||||
259 | $node->{tei_type}=$elem; | ||||||
260 | $node->{form}='#'.($target_doc->lastTreeNo+1); | ||||||
261 | if (ref($attr)) { | ||||||
262 | foreach (values %$attr) { | ||||||
263 | $node->{'s_'.$_->{Name}} = ($]>=5.008) ? $_->{Value} : | ||||||
264 | XML::LibXML::decodeFromUTF8($target_doc->encoding(),$_->{Value}); | ||||||
265 | $self->{FSAttrs}->{'s_'.$_->{Name}}=1; | ||||||
266 | } | ||||||
267 | $self->{IDs}->{$node->{id}}=$node | ||||||
268 | if ($node->{id} ne ''); | ||||||
269 | $self->{IDs}->{$node->{AID}}=$node | ||||||
270 | if ($node->{AID} ne ''); #grrrrrrrr! | ||||||
271 | } | ||||||
272 | if ($target_doc->lastTreeNo == 0 and ref($self->{DocAttributes})) { | ||||||
273 | foreach (values %{$self->{DocAttributes}}) { | ||||||
274 | # leave data in the UTF-8 encoding in Perl 5.8 | ||||||
275 | $node->{'p_'.$_->{Name}} = ($]>=5.008) ? $_->{Value} : | ||||||
276 | XML::LibXML::decodeFromUTF8($target_doc->encoding(),$_->{Value}); | ||||||
277 | $self->{FSAttrs}->{"p_".$_->{Name}}=1; | ||||||
278 | } | ||||||
279 | } | ||||||
280 | } elsif ($elem eq 'w' or $elem eq 'c') { | ||||||
281 | my $node = $self->{Node} = Treex::PML::Factory->createNode(); | ||||||
282 | $node->{tei_type}=$elem; | ||||||
283 | $node->{ord} = ++($self->{LastOrd}); | ||||||
284 | $node->paste_on($self->{Tree},'ord'); | ||||||
285 | if (ref($attr)) { | ||||||
286 | foreach (values %$attr) { | ||||||
287 | $node->{$_->{Name}} = ($]>=5.008) ? $_->{Value} : | ||||||
288 | XML::LibXML::decodeFromUTF8($target_doc->encoding(),$_->{Value}); | ||||||
289 | $self->{FSAttrs}->{$_->{Name}}=1; | ||||||
290 | } | ||||||
291 | $self->{IDs}->{$node->{id}}=$node | ||||||
292 | if ($node->{id} ne ''); | ||||||
293 | $self->{IDs}->{$node->{AID}}=$node | ||||||
294 | if ($node->{AID} ne ''); #grrrrrrrr! | ||||||
295 | } | ||||||
296 | } | ||||||
297 | } | ||||||
298 | |||||||
299 | sub end_element { | ||||||
300 | my ($self) = @_; | ||||||
301 | if ($self->{Node} and $self->{Node}->{tei_type} eq 's') { | ||||||
302 | # build the tree (no consistency checks at all) | ||||||
303 | my @nodes=$self->{Tree}->descendants; | ||||||
304 | foreach my $node (@nodes) { | ||||||
305 | my $dep=$node->{dep}; | ||||||
306 | if ($dep ne '' and | ||||||
307 | ref($self->{IDs}{$dep})) { | ||||||
308 | $node->cut()->paste_on($self->{IDs}{$dep}, 'ord'); | ||||||
309 | } | ||||||
310 | } | ||||||
311 | } | ||||||
312 | $self->{Node} = $self->{Node}->parent if ($self->{Node}); | ||||||
313 | } | ||||||
314 | |||||||
315 | sub start_entity { | ||||||
316 | # just hoping some parser would support these | ||||||
317 | print "START ENTITY: @{$_[1]}\n"; | ||||||
318 | } | ||||||
319 | |||||||
320 | |||||||
321 | sub end_entity { | ||||||
322 | print "END ENTITY: @{$_[1]}\n"; | ||||||
323 | } | ||||||
324 | |||||||
325 | sub entity_reference { | ||||||
326 | my $self = $_[0]; | ||||||
327 | my $name = $_[1]->{Name}; | ||||||
328 | if ($self->{Node}->{tei_type} eq 'w' or | ||||||
329 | $self->{Node}->{tei_type} eq 'c') { | ||||||
330 | $self->{Node}->{form}.='&'.$name.';'; | ||||||
331 | } | ||||||
332 | } | ||||||
333 | |||||||
334 | sub start_cdata { # not much use for this | ||||||
335 | my $self = shift; | ||||||
336 | $self->{InCDATA} = 1; | ||||||
337 | } | ||||||
338 | |||||||
339 | sub end_cdata { # not much use for this | ||||||
340 | my $self = shift; | ||||||
341 | $self->{InCDATA} = 0; | ||||||
342 | } | ||||||
343 | |||||||
344 | sub comment { | ||||||
345 | my $self = $_[0]; | ||||||
346 | my $data = $_[1]; | ||||||
347 | if ($self->{Node}) { | ||||||
348 | $self->{Node}->{xml_comment}.=''; | ||||||
349 | } | ||||||
350 | } | ||||||
351 | |||||||
352 | sub doctype_decl { # unfortunatelly, not called by the parser, so far | ||||||
353 | my ($self,$hash) = @_; | ||||||
354 | $self->{TargetDocument}->changeMetaData("xml_doctype" => $hash); | ||||||
355 | } | ||||||
356 | |||||||
357 | # hack to fix LibXML | ||||||
358 | sub XML::LibXML::Dtd::type { return $_[0]->nodeType } | ||||||
359 | |||||||
360 | |||||||
361 | 1; | ||||||
362 | __END__ |