File Coverage

blib/lib/Treex/PML/Instance/Writer.pm
Criterion Covered Total %
statement 389 503 77.3
branch 151 276 54.7
condition 69 122 56.5
subroutine 33 37 89.1
pod 0 8 0.0
total 642 946 67.8


line stmt bran cond sub pod time code
1             package Treex::PML::Instance::Writer;
2             {
3 8     8   197 use 5.008;
  8         33  
4 8     8   78 use strict;
  8         23  
  8         272  
5 8     8   45 use warnings;
  8         20  
  8         647  
6 8     8   53 no warnings qw(recursion);
  8         16  
  8         429  
7 8     8   48 use Carp;
  8         17  
  8         726  
8 8     8   55 use Data::Dumper;
  8         68  
  8         532  
9 8     8   71 use UNIVERSAL::DOES;
  8         17  
  8         465  
10              
11             BEGIN {
12 8     8   324 our $VERSION = '2.28'; # version template
13             }
14 8     8   53 use List::Util qw(first);
  8         37  
  8         755  
15 8     8   70 use Treex::PML::Instance::Common qw(:diagnostics :constants);
  8         20  
  8         1488  
16 8     8   59 use Treex::PML::Schema;
  8         17  
  8         1245  
17 8     8   60 use Treex::PML::IO qw(open_backend close_backend rename_uri);
  8         23  
  8         648  
18 8     8   54 use Encode;
  8         18  
  8         72603  
19              
20             my (
21             %handlers,
22             %src,
23             %handler_cache,
24             @handler_cache,
25             );
26              
27             # TODO:
28             # - test inline schemas
29             # - content_pattern and cdata validation on save
30             # - mixed content
31             # - decorate
32              
33             our $CACHE_HANDLERS=1;
34             our $MAX_SCHEMA_CACHE_SIZE=50;
35              
36             our $VALIDATE_CDATA=0;
37             our $SAVE_REFFILES = 1;
38             our $WITH_TREES = 1;
39             our $KEEP_KNIT = 0;
40             our $WRITE_SINGLE_LM = 0;
41             our $WRITE_SINGLE_CHILDREN_LM = 0;
42             our $INDENT = 2;
43              
44             require Treex::PML;
45              
46             sub _get_handlers_cache_key {
47 20     20   55 my ($schema)=@_;
48 20         94 my $key="$schema"; $key=~s/.*=//; # strip class
  20         199  
49             return
50             [
51 20   50     529 $key,
      50        
      50        
      50        
      50        
      50        
      50        
52             join ',',
53             $key,
54             $INDENT || 0,
55             $VALIDATE_CDATA || 0,
56             $SAVE_REFFILES || 0,
57             $WITH_TREES || 0,
58             $WRITE_SINGLE_LM || 0,
59             $KEEP_KNIT || 0,
60             $WRITE_SINGLE_CHILDREN_LM || 0,
61             ];
62             }
63              
64             sub get_cached_handlers {
65 20     20 0 60 my ($key)=@_;
66 20         76 my $subkey = $key->[1];
67 20         99 my $cached = $handler_cache{ $key->[0] }{ $subkey };
68 20 100 66     116 if ($cached and $handler_cache[-1][1] ne $subkey) {
69             # move the last retrieved schema to the end of the queue
70 5         16 @handler_cache = ((grep { $_->[1] ne $subkey } @handler_cache),$key);
  45         148  
71             }
72 20         44 return $cached;
73             }
74              
75             sub cache_handlers {
76 15     15 0 45 my ($key,$handlers)=@_;
77 15         43 my $subkey = $key->[1];
78 15         42 push @handler_cache,$key;
79 15         82 $handler_cache{$key->[0]}{$subkey} = $handlers;
80 15 50       74 if (@handler_cache > $MAX_SCHEMA_CACHE_SIZE) {
81 0         0 my $del = shift @handler_cache;
82 0         0 delete $handler_cache{ $del->[0] }{ $del->[1] };
83             }
84             }
85              
86             sub forget_schema {
87 0     0 0 0 my ($schema)=@_;
88 0         0 delete $handler_cache{ $schema }; # delete also from the handler cache
89 0         0 @handler_cache = grep { $_->[0] ne $schema } @handler_cache;
  0         0  
90             }
91              
92             sub _indent {
93 542 100   542   1068 if ($INDENT>=0) {
94 539         2506 return q{"\n".('}.(' ' x $INDENT).q{' x $indent_level).}
95             } else {
96 3         13 return q()
97             }
98             }
99             sub _indent_inc {
100 119 100   119   299 if ($INDENT>0) {
101 118         437 return q`
102             $indent_level++;`;
103             } else {
104 1         7 return q()
105             }
106             }
107             sub _indent_dec {
108 119 100   119   274 if ($INDENT>0) {
109 118         380 return q`
110             $indent_level--;`;
111             } else {
112 1         5 return q()
113             }
114             }
115              
116             sub save {
117 21     21 0 72 my ($ctxt,$opts)=@_;
118 21         73 my $fh = $opts->{fh};
119             local $VALIDATE_CDATA=$opts->{validate_cdata} if
120 21 50       98 exists $opts->{validate_cdata};
121              
122 21 50       78 $ctxt->set_filename($opts->{filename}) if $opts->{filename};
123 21         64 my $href = $ctxt->{'_filename'};
124              
125 21 50 33     161 $fh=\*STDOUT if ($href eq '-' and !$fh);
126 21         133 my $config = $opts->{config};
127 21 50 66     113 if ($config and ref(my $load_opts = $config->get_data('options/save'))) {
128 0         0 $opts = {%$load_opts, %$opts};
129             }
130              
131 21 50       113 local $KEEP_KNIT = 1 if $opts->{keep_knit};
132 21 50       75 local $WRITE_SINGLE_LM = 1 if $opts->{write_single_LM};
133 21 50       115 local $WRITE_SINGLE_CHILDREN_LM = 1 if $opts->{write_single_children_LM};
134 21 50       91 local $INDENT = $opts->{indent} if defined $opts->{indent};
135 21 100       117 unless ($fh) {
136 1 50 33     4 if (defined($href) and length($href)) {
137 1         5 eval {
138 1 50       2 rename_uri($href,$href."~") unless $href=~/^ntred:/;
139             };
140 1         2 my $ok = 0;
141 1         3 my $res;
142 1         1 eval {
143 1   50     3 $fh = open_backend($href,'w')
144             || die "Cannot open $href for writing: $!";
145 1 50       14 if ($fh) {
146 1         3 binmode $fh;
147 1         8 $res = $ctxt->save({%$opts, fh=> $fh});
148 1         4 close_backend($fh);
149 1         2 $ok = 1;
150             }
151             };
152 1 50       4 unless ($ok) {
153 0         0 my $err = $@;
154 0         0 eval {
155 0 0       0 rename_uri($href."~",$href) unless $href=~/^ntred:/;
156             };
157 0 0       0 die($err."$@\n") if $err;
158             }
159 1         5 return $res;
160             } else {
161 0         0 die("Usage: $ctxt->save({filename=>...,[fh => ...]})");
162             }
163             }
164 20   33     201 $ctxt->{'_refs_save'} ||= $opts->{'refs_save'};
165 20 50       74 binmode $fh if $fh;
166              
167 20         118 my $transform_id = $ctxt->{'_transform_id'};
168 20         48 my ($out_xsl_href,$out_xsl,$orig_fh);
169 20         46 my $xsl_source='';
170 20 50 66     94 if ($config and defined $transform_id and length $transform_id) {
      33        
171 0         0 my $transform = $config->lookup_id( $transform_id );
172 0 0       0 if ($transform) {
173 0         0 ($out_xsl) = $transform->{'out'};
174 0 0       0 if ($out_xsl->{'type'} ne 'xslt') {
175 0         0 die(__PACKAGE__.": unsupported output transformation $transform_id (only type='xslt') transformations are supported)");
176             }
177 0         0 $out_xsl_href = URI->new(Encode::encode_utf8($out_xsl->get_member('href')));
178 0         0 $out_xsl_href = Treex::PML::ResolvePath($config->{_filename}, $out_xsl_href, 1);
179 0 0 0     0 unless (defined $out_xsl_href and length $out_xsl_href) {
180 0         0 die(__PACKAGE__.": no output transformation defined for $transform_id");
181             }
182 0         0 $orig_fh = $fh;
183 0 0       0 open(my $pml_fh, '>', \$xsl_source) or die "Cannot open scalar for writing!";
184 0         0 $fh=$pml_fh;
185             } else {
186 0         0 die(__PACKAGE__.": Couldn't find PML transform with ID $transform_id");
187             }
188             }
189              
190             # dump embedded DOM documents
191 20         51 my $refs_to_save = $ctxt->{'_refs_save'};
192             # save_reffiles must be a id=>href hash reference
193              
194 20 50 0     110 my @refs_to_save = grep { ($_->{readas}||'') eq 'dom' or ($_->{readas}||'') eq 'pml' } $ctxt->get_reffiles();
  12   50     98  
195 20 50       71 if (ref($refs_to_save)) {
196 0         0 @refs_to_save = grep { exists $refs_to_save->{$_->{id}} } @refs_to_save;
  0         0  
197 0         0 for (@refs_to_save) {
198 0 0       0 unless (defined $refs_to_save->{$_->{id}}) {
199 0         0 $refs_to_save->{$_->{id}}=$_->{href};
200             }
201             }
202             } else {
203 20         44 $refs_to_save = {};
204             }
205              
206 20         52 my $references = $ctxt->{'_references'};
207              
208             # update all DOM trees to be saved
209 20   33     171 $ctxt->{'_parser'} ||= $ctxt->_xml_parser();
210 20         57 foreach my $ref (@refs_to_save) {
211 12 50       74 if ($ref->{readas} eq 'dom') {
212 12         61 $ctxt->readas_dom($ref->{id},$ref->{href});
213             }
214             # NOTE:
215             # if ($refs_to_save->{$ref->{id}} ne $ref->{href}),
216             # then the ref-file is going to be renamed.
217             # Although we don't parse it as PML, it can be a PML file.
218             # If it is, we might try to update it's references too,
219             # but the snag here is, that we don't know if the
220             # resources it references aren't moved along with it by
221             # other means (e.g. by user making the copy).
222             }
223              
224 20 50       175 binmode $fh,":utf8" if $fh;
225 20 50       225 local $WITH_TREES = $ctxt->{'_no_read_trees'} ? 0 : 1;
226 20         92 prepare_handlers($ctxt);
227 20 50 33     219 dump_handlers($ctxt) if $opts->{dump_handlers} or $ENV{PML_COMPILE_DUMP};;
228 20         113 $handlers{'#initialize'}->($ctxt,$refs_to_save,$fh);
229 20         45 eval {
230 20         128 $handlers{'#root'}->($ctxt->{_root});
231 20 100       2265 if ($ctxt->{'_pi'}) {
232 10         28 my ($n,$v);
233 10         22 for my $pi (@{$ctxt->{'_pi'}}) {
  10         49  
234             # ($n,$v)=@$pi;
235             # for ($n,$v) { s/&/&/g; s/
236 0         0 print $fh qq(\n);
237             }
238             }
239             };
240 20   33 0   142 ($handlers{'#cleanup'}||sub{})->();
241 20         226 %handlers=();
242             # close_uri($fh);
243 20 50       84 $fh = $orig_fh if defined $orig_fh;
244 20 50       100 die $@ if $@;
245              
246 20 50 33     98 if ($xsl_source and $out_xsl_href) {
247 0 0       0 die "Buggy libxslt version 10127\n" if XSLT_BUG;
248 0         0 my $xslt = XML::LibXSLT->new;
249 0         0 my $params = $out_xsl->content;
250 0         0 my %params;
251 0 0       0 %params = map { $_->{'name'} => $_->value } $params->values
  0         0  
252             if $params;
253 0         0 my $out_xsl_parsed = $xslt->parse_stylesheet_file($out_xsl_href);
254 0         0 my $dom = XML::LibXML->new()->parse_string($xsl_source);
255 0         0 my $result = $out_xsl_parsed->transform($dom,%params);
256 0 0       0 if (UNIVERSAL::can($result,'toFH')) {
257 0         0 $result->toFH($fh,1);
258             } else {
259 0         0 $out_xsl_parsed->output_fh($result,$fh);
260             }
261 0         0 return 1;
262             }
263              
264             # dump DOM trees to save
265 20 100       94 if (ref($ctxt->{'_ref'})) {
266 13         50 foreach my $ref (@refs_to_save) {
267 12 50       66 if ($ref->{readas} eq 'dom') {
    0          
268 12         50 my $dom = $ctxt->{'_ref'}->{$ref->{id}};
269 12         21 my $href;
270 12 50       47 if (defined($refs_to_save->{$ref->{id}})) {
271 0         0 $href = $refs_to_save->{$ref->{id}};
272             } else {
273             $href = $ref->{href}
274 12         30 }
275 12 50       45 if (ref($dom)) {
276 12         22 eval {
277 12 50       80 rename_uri($href,$href."~") unless $href=~/^ntred:/;
278             };
279 12         7132 my $ok = 0;
280 12         25 eval {
281 12         66 my $ref_fh = open_backend($href,"w");
282 12 50       39 if ($ref_fh) {
283 12         50 binmode $ref_fh;
284 12         2164 $dom->toFH($ref_fh,1);
285 12         10011 close_backend($ref_fh);
286 12         61 $ok = 1;
287             }
288             };
289 12 50       1331 unless ($ok) {
290 0         0 my $err = $@;
291 0         0 eval {
292 0 0       0 rename_uri($href."~",$href) unless $href=~/^ntred:/;
293             };
294 0 0       0 _die($err."$@") if $err;
295             }
296             }
297             } elsif ($ref->{readas} eq 'pml') {
298 0         0 my $ref_id = $ref->{id};
299 0         0 my $pml = $ctxt->{'_ref'}->{$ref_id};
300 0 0       0 if ($pml) {
301 0         0 my $href;
302 0 0       0 if (exists($refs_to_save->{$ref_id})) {
303 0         0 $href = $refs_to_save->{$ref_id};
304             } else {
305             $href = $ref->{href}
306 0         0 }
307             $pml->save({ %$opts,
308             refs_save=>{
309 0 0       0 map { my $k=$_; $k=~s%^\Q$ref_id\E/%% ? ($k=>$refs_to_save->{$_}) : () } keys %$refs_to_save
  0         0  
  0         0  
310             },
311             filename => $href, fh=>undef });
312             }
313             }
314             }
315             }
316 20         241 return $ctxt;
317             }
318              
319             ######################################################
320              
321             sub prepare_handlers {
322 20     20 0 57 my ($ctxt)=@_;
323 20         54 %handlers=();
324 20         73 my $schema = $ctxt->{'_schema'};
325 20         77 my $key=_get_handlers_cache_key($schema);
326 20         80 my $cached = get_cached_handlers($key);
327 20 100       63 if ($cached) {
328 5         113 %handlers= @$cached;
329             } else {
330 15         67 compile_schema($schema);
331 15 50       529 cache_handlers($key,[%handlers]) if $CACHE_HANDLERS;
332             }
333             }
334              
335             sub dump_handlers {
336 0     0 0 0 my $dir = '.pml_compile.d';
337 0 0 0     0 (-d $dir) || mkdir($dir) || die "Can't dump to $dir: $!\n";
338             # print "created $dir\n";
339 0         0 for my $f (keys %src) {
340 0         0 my $dump_file = File::Spec->catfile($dir,$f);
341 0 0       0 open (my $fh, '>:utf8', $dump_file)
342             || die "Can't write to $dump_file: $!\n";
343 0         0 my $sub = $src{$f};
344 0         0 $sub=~s/^\s*#line[^\n]*\n//;
345 0         0 print $fh ($sub);
346 0         0 close $fh;
347             }
348             }
349              
350             sub _write_seq {
351 36     36   113 my ($decl,$path,$seq)=@_;
352 36         63 my $sub='';
353 36 100       95 local $INDENT=-1 if $decl->is_mixed;
354 36         80 $sub .= q`
355             for my $el (`.$seq.q`->elements) {
356             ($k,$v)=@$el;
357             if (defined $v and (ref $v or length $v)) {
358             $handlers{ '`.$path.'/'.q`'.$k }->($k,$v);
359             } else {
360             print $out `._indent().q`"<$k/>";
361             }
362             }`;
363 36         163 return $sub;
364             }
365              
366             sub _write_trees_seq {
367 9     9   30 my ($decl)=@_;
368 9         36 my $path = $decl->get_decl_path;
369 9 100       60 $path =~ s/^!// if $path;
370 9         42 return q`
371             my $prolog = $ctxt->{'_pml_prolog'};
372             if ($prolog) {`._write_seq($decl,$path,'$prolog').q`
373             }
374             for $v (@{$ctxt->{'_trees'}}) {
375             if (ref $v) {
376             $k=$v->{'#name'};
377             $handlers{ '`.$path.'/'.q`'.$k }->($k,$v);
378             }
379             }
380             my $epilog = $ctxt->{'_pml_epilog'};
381             if ($epilog) {`._write_seq($decl,$path,'$epilog').q`
382             }`;
383             }
384              
385             sub _write_trees_list {
386 1     1   3 my ($decl)=@_;
387 1         3 my $path = $decl->get_content_decl->get_decl_path;
388 1 50       8 $path =~ s/^!// if $path;
389 1         3 return q`
390             for $v (@{$ctxt->{'_trees'}}) {
391             $handlers{ '`.$path.q`' }->('LM',$v);
392             }`;
393             }
394              
395             sub _write_children_seq {
396 7     7   23 my ($tag,$decl)=@_;
397 7         23 my $path = $decl->get_decl_path;
398 7 50       51 $path =~ s/^!// if $path;
399 7         17 my $sub = q`
400             if ($v = $data->firstson) {`;
401 7 50       25 $sub .= q`
402             print $out `._indent().q`"<`.$tag.q`>";` if defined $tag;
403 7         19 $sub .= _indent_inc().q`
404             my $name;
405             while ($v) {
406             $name = $v->{'#name'};
407             $handlers{ '`.$path.'/'.q`'.$name }->($name,$v);
408             $v = $v->rbrother;
409             }`._indent_dec();
410 7 50       43 $sub .= q`
411             print $out `._indent().q`"";` if defined $tag;
412 7         19 $sub.=q`
413             }`;
414 7         48 return $sub;
415             }
416              
417             sub _write_children_list {
418 5     5   15 my ($tag,$decl)=@_;
419 5         21 $decl = $decl->get_content_decl;
420 5         20 my $path = $decl->get_decl_path;
421 5 50       34 $path =~ s/^!// if $path;
422 5         13 my $sub = q`
423             if ($v = $data->firstson) {`;
424 5 100       15 if (defined $tag) {
425 3 50 33     20 if (!$WRITE_SINGLE_LM and !$WRITE_SINGLE_CHILDREN_LM) {
426 3         11 $sub .= q`
427             if ($v && !$v->rbrother && keys(%$v)) {
428             $handlers{ '`.$path.q`' }->('`.$tag.q`',$v);
429             } else {`;
430             }
431 3         10 $sub .= q`
432             print $out `._indent().q`"<`.$tag.q`>";` ;
433             }
434 5         13 $sub.=_indent_inc().q`
435             while ($v) {
436             $handlers{ '`.$path.q`' }->('LM',$v);
437             $v = $v->rbrother;
438             }`._indent_dec();
439 5 100       53 if (defined $tag) {
440 3         10 $sub .= q`
441             print $out `._indent().q`"";`;
442 3 50 33     20 $sub .= q`
443             }` if !$WRITE_SINGLE_LM and !$WRITE_SINGLE_CHILDREN_LM;
444             }
445 5         16 $sub.=q`
446             }`;
447 5         37 return $sub;
448             }
449              
450              
451             sub _knit_code {
452 14     14   44 my ($knit_decl,$knit_decl_path,$name)=@_;
453 14         65 my $idM = Treex::PML::Instance::Reader::_fix_id_member($knit_decl);
454 14 50       63 if ($idM) {
455 14         52 my $idM_name=$idM->get_name;
456 14         40 return q`
457             my $knit_id = $v->{'`.$idM_name.q`'};
458             my $prefix;
459             unless (defined $knit_id) {
460             warn "Cannot KNIT back: `.$idM_name.q` not defined on object `.$knit_decl_path.q`!";
461             } elsif ($knit_id =~ s/^(.*?)#//) {
462             $prefix=$1;
463             } else {
464             $prefix = $v->{'#knit_prefix'};
465             }
466             print $out `._indent().q`'<`.$name.q`>'.($prefix ? $prefix.'#'.$knit_id : $knit_id).'';
467             if ($prefix and !UNIVERSAL::DOES::does($ctxt->{'_ref'}{$prefix},'Treex::PML::Instance')) {
468             # DOM KNIT
469             my $rf_href = $refs_to_save->{$prefix};
470             if ( $rf_href ) {
471             my $indeces = $ctxt->{'_ref-index'};
472             if ($indeces and $indeces->{$prefix}) {
473             my $knit = $indeces->{$prefix}{$knit_id};
474             if ($knit) {
475             my $save_out = $out;
476             my $xml='';
477             open my $new_out, '>:utf8', \$xml; # perl 5.8.0
478             $out = $new_out;
479             local $INDENT=-1;
480             $handlers{'`.$knit_decl_path.q`' }->($knit->nodeName,$v);
481             close $new_out;
482             $out = $save_out;
483             $xml=''.$xml.'';
484             my $new = $ctxt->{'_parser'}->parse_string($xml)->documentElement->firstChild;
485             $new->setAttribute('`.$idM_name.q`',$knit_id);
486             $knit->ownerDocument->adoptNode( $new );
487             $knit->parentNode->insertAfter($new,$knit);
488             $knit->unbindNode;
489             $indeces->{$prefix}{$knit_id}=$new;
490             } else {
491             _warn("Didn't find ID '$knit_id' in '$rf_href' ('$prefix') - cannot knit back!\n");
492             }
493             } else {
494             _warn("Knit-file '$rf_href' ('$prefix') has no index - cannot knit back!\n");
495             }
496             }
497             }`;
498             } else {
499 0         0 warn("Cannot KNIT ".$knit_decl_path." if there is no member/attribute with role='#ID'!");
500             }
501             }
502              
503             sub simplify {
504 20     20 0 2088 my $filename = shift;
505 20         331 my $up = File::Spec->updir;
506 20         391 my $sep = File::Spec->catfile(q(), q());
507 20         297 while($filename =~ /\Q$sep$up$sep/) {
508 0         0 $filename =~ s/\Q$sep\E?[^$sep]*\Q$sep$up$sep/$sep/;
509             }
510 20         77 return $filename;
511             }
512              
513             sub compile_schema {
514 15     15 0 35 my ($schema)=@_;
515 15         53 my ($ctxt,$refs_to_save,$out,$pml_trees_type,$have_trees,$indent_level);
516 15         97 my $schema_name = $schema->get_root_decl->get_name;
517             $handlers{'#cleanup'}= sub {
518 20     20   102 undef $_ for ($ctxt,$refs_to_save,$out);
519 15         133 };
520             $handlers{'#initialize'}= sub {
521 20     20   64 my ($instance,$refs_save,$fh)=@_;
522 20         46 $ctxt = $instance;
523 20         43 $refs_to_save = $refs_save;
524 20         46 $out = $fh;
525 20         40 $have_trees = 0;
526 20         66 $pml_trees_type = $ctxt->{'_pml_trees_type'};
527 20         49 $indent_level=0;
528 15         171 };
529             $schema->for_each_decl(sub {
530 466     466   852 my ($decl)=@_;
531             # no warnings 'uninitialized';
532 466         1675 my $decl_type=$decl->get_decl_type;
533 466         1346 my $path = $decl->get_decl_path;
534 466 100       1936 $path =~ s/^!// if $path;
535 466 100 100     2933 return if $decl_type == PML_ATTRIBUTE_DECL ||
      100        
      100        
536             $decl_type == PML_MEMBER_DECL ||
537             $decl_type == PML_TYPE_DECL ||
538             $decl_type == PML_ELEMENT_DECL;
539 238 100       1384 if ($decl_type == PML_ROOT_DECL) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    0          
540 15         125 my $name = $decl->get_name;
541 15         65 my $cdecl = $decl->get_content_decl;
542 15         78 my $cdecl_type = $cdecl->get_decl_type;
543 15         44 my $cpath = $cdecl->get_decl_path;
544 15         90 $cpath =~ s/^!//;
545 15         49 my $src = $schema_name.'__generated_write_root';
546 15         46 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
547             sub {
548             my ($data)=@_;
549             my $v;
550             print $out ''."\n";
551             print $out '<`.$decl->get_name.q` xmlns="`.PML_NS.q`"';`;
552             # we need to know attributes now
553 15 100 66     90 if ($cdecl_type == PML_CONSTANT_DECL ||
554             $cdecl_type == PML_STRUCTURE_DECL) {
555 5         25 for my $attr ($cdecl->get_attributes) {
556 0 0       0 if ($attr->is_required) {
557 0         0 $sub.=q`
558             $v = $data->{'`.$attr->get_name.q`'};
559             $v = '' unless defined $v;
560             $v =~ s/&/&/g; $v=~s/
561             print $out ' `.$attr->get_name.q`="'.$v.'"';
562             `;
563             } else {
564 0         0 $sub.=q`
565             $v = $data->{'`.$attr->get_name.q`'};
566             if (defined($v) && length($v)) {
567             $v=~s/&/&/g; $v=~s/
568             print $out ' `.$attr->get_name.q`="'.$v.'"';
569             }
570             `;
571             }
572             }
573             }
574             # NOTE: using _^_ as indentation replacement!
575 15   100     86 my $no_end_indent =
576             ($cdecl_type == PML_SEQUENCE_DECL and
577             $cdecl->is_mixed);
578 15 100       82 my $psub = q`
579             print $out ">\n",
580             "_^_\n";
581             my $inline = $ctxt->{'_schema-inline'};
582              
583             # remove /../ from filename, URI::rel gives strange results for base containing them
584             my $filename = $ctxt->{_filename};
585             $filename = $filename->path if ref $filename and index($filename,'file:/') == 0;
586             $filename = simplify($filename) if -e $filename;
587              
588             if (defined $inline and length $inline) {
589             print $out qq(_^__^_\n),$inline,qq( \n);
590             } else {
591             $v = $ctxt->{'_schema-url'};
592             if (defined $v and length $v) {
593             $v=Treex::PML::IO::make_relative_URI($ctxt->{'_schema-url'},$filename);
594             $v=~s/&/&/g; $v=~s/
595             print $out qq(_^__^_\n);
596             } else {
597             print $out qq(_^__^_\n);
598             $ctxt->{'_schema'}->write({fh=>$out});
599             print $out qq(_^__^_\n);
600             }
601             }
602             my $references = $ctxt->{'_references'};
603             if (ref($references) and keys(%$references)) {
604             my $named = $ctxt->{'_refnames'};
605             my %names = $named ? (map {
606             my $name = $_;
607             map { $_ => $name } (ref($named->{$_}) ? @{$named->{$_}} : $named->{$_})
608             } keys %$named) : ();
609             print $out qq(_^__^_\n);
610             foreach my $id (sort keys %$references) {
611             my $href;
612             if (exists($refs_to_save->{$id})) {
613             # effectively rename the file reference
614             $href = $references->{$id} = $refs_to_save->{$id}
615             } else {
616             $href = $references->{$id};
617             }
618             if (ref $href && $href->isa('Treex::PML::Resource::URI')) {
619             $href = Treex::PML::IO::make_relative_URI($href,$href);
620             } else {
621             $href = Treex::PML::IO::make_relative_URI($href,$filename);
622             }
623             my $name = $names{$id};
624             for ($id,$href, (defined $name ? $name : ())) { s/&/&/g; s/
625             print $out qq(_^__^__^_\n);
626             }
627             print $out qq(_^__^_\n);
628             }
629             print $out "_^_";
630             $handlers{ '`.$cpath.q`' }->(undef,$data);
631             print $out `.($no_end_indent ? '' : _indent()).q`'get_name.q`>'."\n";
632             }`;
633 15 50       75 my $indent = $INDENT>0 ? ' ' x $INDENT : '';
634 15         350 $psub=~s/_\^_/$indent/g;
635 15         124 $sub.=$psub;
636 15         86 $src{$src}=$sub;
637 15 50       16313 $handlers{'#root'}=eval $sub; die _nl($sub)."\n".$@.' ' if $@;
  15         407  
638             } elsif ($decl_type == PML_STRUCTURE_DECL) {
639             # print $path,"\n";
640 38         118 my $src = $schema_name.'__generated_write_structure@'.$path;
641 38         171 $src=~y{/}{@};
642 38         244 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
643             sub {
644             my ($tag,$data)=@_;
645             my ($v,$k);
646             unless (defined $data) {
647             print $out defined $tag ? '/>' : '>' if !$tag;
648             return;
649             }
650             my $close;
651             if (defined $tag) {
652             $close = '/>';
653             print $out `._indent().q`'<'.$tag if length $tag;`;
654 38         188 for my $attr ($decl->get_attributes) {
655 24         127 my $name = $attr->get_name;
656 24 100       80 if ($attr->is_required) {
657 22         87 $sub.=q`
658             $v = $data->{'`.$name.q`'};
659             $v='' unless defined $v;
660             $v=~s/&/&/g; $v=~s/
661             print $out ' `.$name.q`'.'="'.$v.'"';
662             `;
663             } else {
664 2         12 $sub.=q`
665             $v = $data->{'`.$name.q`'};
666             if (defined($v) && length($v)) {
667             $v=~s/&/&/g; $v=~s/
668             print $out ' `.$name.q`'.'="'.$v.'"';
669             }
670             `;
671             }
672             }
673 38         129 $sub .= q`
674             }`._indent_inc();
675 38         77 my $this_trees_type;
676 38         201 for my $m ($decl->get_members) {
677 115 100       311 next if $m->is_attribute;
678 91         225 my $name = $m->get_name;
679 91         243 my $mdecl = $m->get_content_decl;
680 91         352 my $mdecl_type = $mdecl->get_decl_type;
681 91         194 $sub.=q`
682             $v = $data->{'`.$name.q`'};`;
683 91         148 my $close_brace=0;
684 91         147 my $ignore_required=0;
685 91 100 66     454 if ($WITH_TREES and $decl->get_role eq '#NODE' and $m->get_role eq '#CHILDNODES') {
    100 100        
      66        
      33        
686 3         7 $close_brace=1;
687 3         10 $sub.=q`
688             if (UNIVERSAL::DOES::does($data,'Treex::PML::Node')) {
689             if (defined $close) { undef $close; print $out '>'; }`;
690 3 50       13 if ($mdecl_type == PML_SEQUENCE_DECL) {
    50          
691 0         0 $sub .= _write_children_seq($name,$mdecl);
692             } elsif ($mdecl_type == PML_LIST_DECL) {
693 3         11 $sub .= _write_children_list($name,$mdecl);
694             }
695 3         9 $sub.=q`
696             } else { `;
697             } elsif ($WITH_TREES and ($m->get_role eq '#TREES' or $mdecl->get_role eq '#TREES')) {
698 1         2 $close_brace=1;
699 1         2 $this_trees_type = $mdecl;
700 1         2 $ignore_required=1;
701 1         3 $sub.=q`
702             if (!$have_trees and !defined $v and (!defined($pml_trees_type) or $pml_trees_type==$this_trees_type)) {
703             $have_trees=1;`;
704 1 50       3 if ($m->is_required) {
705 1         3 $sub.=q`
706             warn "Member '`.$path.'/'.$name.q`' with role #TREES is required but there are no trees, writing empty tag!\n"
707             if !$ctxt->{_trees} and @{$ctxt->{_trees}};`;
708             }
709 1         4 $sub.=q`
710             if (defined $close) { undef $close; print $out '>'; }
711             print $out `._indent().q`'<`.$name.q`>';`._indent_inc();
712 1 50       5 if ($mdecl_type == PML_SEQUENCE_DECL) {
    50          
713 0         0 $sub .= _write_trees_seq($mdecl);
714             } elsif ($mdecl_type == PML_LIST_DECL) {
715 1         5 $sub .= _write_trees_list($mdecl);
716             }
717 1         4 $sub.=_indent_dec().q`
718             if (defined $close) { undef $close; print $out '>'; }
719             print $out `._indent().q`'';
720             } else { `;
721             }
722 91 50 33     349 if ($mdecl_type == PML_CONSTANT_DECL and !$m->is_required) {
    50 100        
    100          
723             # do not write
724 0         0 $sub.=q`
725             if (defined $v and (ref($v) or length $v and $v ne "`.quotemeta($mdecl->get_value).q`")) {
726             warn "Disregarding invalid constant value in member '`.$name.q`': '$v'!\n";
727             }`;
728             } elsif ($m->get_role eq '#KNIT') {
729 0         0 my $knit_name = $m->get_knit_name;
730 0         0 my $knit_decl = $m->get_knit_content_decl();
731 0         0 my $knit_decl_path = $knit_decl->get_decl_path;
732 0         0 $knit_decl_path=~s/^!//;
733 0         0 $sub.=q`
734             if (defined $v and !ref $v and length $v) {
735             if (defined $close) { undef $close; print $out '>'; }
736             $handlers{'`.$path.'/'.$name.q`' }->('`.$name.q`',$v);
737             } else {`;
738 0 0       0 unless ($name eq $knit_name) {
739 0         0 $sub .= q`
740             $v = $data->{'`.$knit_name.q`'};`;
741             }
742 0         0 $sub .= q`
743             if (defined $close) { undef $close; print $out '>'; }
744             if (ref $v) {`;
745 0 0       0 if ($KEEP_KNIT) {
746 0         0 $sub .= q`
747             $handlers{'`.$knit_decl_path.q`' }->('`.$name.q`',$v);`;
748             } else {
749 0         0 $sub.=_knit_code($knit_decl,$knit_decl_path,$name);
750             }
751 0         0 $sub .= q`
752             }`;
753 0 0       0 if ($m->is_required) {
754 0         0 $sub.=q` else {
755             warn "Required member '`.$path.'/'.$knit_name.q`' missing, writing empty tag!\n";
756             print $out `._indent().q`'<`.$knit_name.q`/>';
757             }`;
758             }
759             $sub.=
760 0         0 q`
761             }`;
762 0 0       0 $sub .= q`
763             }` if $close_brace;
764             } elsif ($mdecl_type == PML_LIST_DECL and $mdecl->get_role eq '#KNIT') {
765 7         37 my $knit_name = $m->get_knit_name;
766 7         39 my $knit_decl = $mdecl->get_knit_content_decl();
767 7         29 my $knit_decl_path = $knit_decl->get_decl_path;
768 7         37 $knit_decl_path=~s/^!//;
769 7 50       26 if ($name ne $knit_name) {
770 7         29 $sub.=q`
771             if (ref $v) {
772             if (defined $close) { undef $close; print $out '>'; }
773             $handlers{'`.$path.'/'.$name.q`' }->('`.$name.q`',$v);
774             } else {
775             $v = $data->{'`.$knit_name.q`'};`;
776             }
777 7 50       58 if ($m->is_required) {
778 0         0 $sub.=q` if (!ref $v) {
779             warn "Required member '`.$path.'/'.$knit_name.q`' missing, writing empty tag!\n";
780             if (defined $close) { undef $close; print $out '>'; }
781             print $out `._indent().q`'<`.$knit_name.q`/>';
782             } else {`;
783             } else {
784 7         19 $sub .= q`
785             if (ref $v) {
786             if (defined $close) { undef $close; print $out '>'; }`;
787             }
788 7 50       31 if ($KEEP_KNIT) {
789 0 0       0 if (!$WRITE_SINGLE_LM) {
790 0         0 $sub .= q`
791             if (@$v==1 and defined($v->[0]) and !(UNIVERSAL::isa($v->[0],'HASH') and keys(%{$v->[0]})==0)) {
792             $handlers{'`.$knit_decl_path.q`' }->('`.$name.q`',$v->[0]);
793             } else {`;
794             }
795 0         0 $sub .= q`
796             print $out `._indent().q`'<`.$name.q`>';`._indent_inc().q`
797             $handlers{'`.$knit_decl_path.q`' }->('LM',$_) for @$v;`._indent_dec().q`
798             print $out `._indent().q`'';`;
799 0 0       0 $sub .= q`
800             }` if !$WRITE_SINGLE_LM;
801             } else {
802 7 50       25 if (!$WRITE_SINGLE_LM) {
803 7         34 $sub .= q`
804             if (@$v==1) {
805             if (defined $close) { undef $close; print $out '>'; }
806             $v=$v->[0];
807             `._knit_code($knit_decl,$knit_decl_path,$name).q`
808             } else {`;
809             }
810 7         24 $sub .= q`
811             if (defined $close) { undef $close; print $out '>'; }
812             print $out `._indent().q`'<`.$name.q`>';`._indent_inc().q`
813             my $l = $v;
814             for $v (@$l) {`._knit_code($knit_decl,$knit_decl_path,'LM').q`
815             }`._indent_dec().q`
816             print $out `._indent().q`'';`;
817 7 50       53 $sub .= q`
818             }` if !$WRITE_SINGLE_LM;
819             }
820 7         20 $sub.=
821             q`
822             }`;
823 7 50       23 if ($name ne $knit_name) {
824 7         25 $sub.=q`
825             }`;
826             }
827 7 50       24 $sub .= q`
828             }` if $close_brace;
829             } else {
830             # if ($mdecl->get_role eq '#TREES') {
831             # $sub.=q`
832             # $handlers{'`.$path.'/'.$name.q`' }->('`.$name.q`',$v);`;
833             # } else {
834 84         208 $sub.=q`
835             if (defined $v and (ref $v or length $v)) {
836             if (defined $close) { undef $close; print $out '>'; }
837             $handlers{'`.$path.'/'.$name.q`' }->('`.$name.q`',$v);
838             }`;
839             # }
840 84 100 100     223 if ($m->is_required and !$ignore_required ) {
841 25         83 $sub.=q` else {
842             warn "Required member '`.$path.'/'.$name.q`' missing, writing empty tag!\n";
843             if (defined $close) { undef $close; print $out '>'; }
844             print $out `._indent().q`'<`.$name.q`/>';
845             }`;
846             }
847             }
848 91 100       293 $sub .= q`
849             }` if $close_brace;
850             }
851 38         171 $sub .= _indent_dec().q`
852             if (defined $tag and length $tag) {
853             print $out (defined($close) ? $close : `._indent().q`"");
854             }
855             }`;
856             # print $sub;
857 38         271 $src{$src}=$sub;
858 38 50       35479 $handlers{$path} = eval($sub); die _nl($sub)."\n".$@.' ' if $@;
  38         1070  
859             } elsif ($decl_type == PML_CONTAINER_DECL) {
860 18         82 my $src = $schema_name.'__generated_write_container@'.$path;
861 18         51 $src=~y{/}{@};
862 18         57 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
863             sub {
864             my ($tag,$data)=@_;
865             my $v;
866             unless (defined $data) {
867             print $out defined $tag ? '/>' : '>' if !$tag;
868             return;
869             }
870             my $close;
871             my $ctag=$tag;`;
872 18         95 my @attributes = $decl->get_attributes;
873 18 100       61 if (@attributes) {
874 12         38 $sub.=q`
875             if (defined $tag) {
876             print $out `._indent().q`'<'.$tag ; $close = '>'; $ctag='';`;
877 12         39 for my $attr (@attributes) {
878 13         95 my $name = $attr->get_name;
879 13 100       58 if ($attr->is_required) {
880 8         564 $sub.=q`
881             $v = $data->{'`.$name.q`'};
882             $v='' unless defined $v;
883             $v=~s/&/&/g; $v=~s/
884             print $out ' `.$name.q`'.'="'.$v.'"';
885             `;
886             } else {
887 5         20 $sub.=q`
888             $v = $data->{'`.$name.q`'};
889             if (defined($v) && length($v)) {
890             $v=~s/&/&/g; $v=~s/
891             print $out ' `.$name.q`'.'="'.$v.'"';
892             }
893             `;
894             }
895             }
896 12         33 $sub .= q`
897             }`;
898             } else {
899 6         21 $sub .= q`undef $tag;`;
900             }
901 18         76 my $cdecl = $decl->get_content_decl;
902             # TODO: #TREES
903 18 50       60 if ($cdecl) {
904 18         85 my $cdecl_type = $cdecl->get_decl_type;
905 18         66 my $cpath = $cdecl->get_decl_path;
906 18         119 $cpath =~ s/^!//;
907 18         48 my $close_brace=0;
908 18 100 66     126 if ($WITH_TREES and $decl->get_role eq '#NODE' and $cdecl->get_role eq '#CHILDNODES') {
      100        
909 9         24 $close_brace=1;
910 9         21 $sub.=q`
911             if (UNIVERSAL::DOES::does($data,'Treex::PML::Node')) {
912             undef $close;
913             if (defined($ctag)) {
914             if (!length($ctag)) {
915             print $out '>';
916             } elsif ($data->firstson) {
917             print $out `._indent().q`qq{<$ctag>};
918             } else {
919             print $out `._indent().q`qq{<$ctag/>};
920             }
921             }`;
922 9 100       43 if ($cdecl_type == PML_SEQUENCE_DECL) {
    50          
923 7         34 $sub .= _write_children_seq(undef,$cdecl);
924             } elsif ($cdecl_type == PML_LIST_DECL) {
925 2         10 $sub .= _write_children_list(undef,$cdecl);
926             }
927 9         29 $sub.=q`
928             if ($data->firstson) {
929             if (defined($ctag) and length($ctag)) {
930             print $out `._indent().q`qq{};
931             } else {
932             print $out `._indent().q`'';
933             }
934             }
935             } else { `;
936             }
937 18         41 $sub.=q`
938             $v = $data->{'#content'};`;
939 18 50       56 $sub.=q`
940             undef $close;
941             if (defined $v and (ref $v or length $v)) {
942             $handlers{'`.$cpath.q`' }->($ctag,$v);
943             my $ref = ref($v);
944             print $out `._indent().q`'' if !$ctag and $ref and !((UNIVERSAL::DOES::does($v,'Treex::PML::Alt')`.($WRITE_SINGLE_LM ? '' : q` or UNIVERSAL::DOES::does($v,'Treex::PML::List')`)
945             .q`) and @$v==1 and defined($v->[0]) and !(UNIVERSAL::isa($v->[0],'HASH') and keys(%{$v->[0]})==0));
946             } else {
947             if (defined($ctag) and length($ctag)) { print $out `._indent().q`qq{<$ctag/>} } else { $close='/>'; }
948             }`;
949 18 100       107 $sub .= q`
950             }` if $close_brace;
951             } else {
952 0         0 $sub .= q`
953             if (defined($ctag) and length($ctag)) { print $out `._indent().q`qq{<$ctag/>} } else {
954             $close='/>'; }`;
955             }
956 18         51 $sub .= q`
957             if (defined $tag and length $tag) {
958             print $out (defined($close) ? $close : "");
959             }
960             }`;
961 18         76 $src{$src}=$sub;
962 18 50       15354 $handlers{$path} = eval($sub); die _nl($sub)."\n".$@.' ' if $@;
  18         476  
963             } elsif ($decl_type == PML_SEQUENCE_DECL) {
964             # print $path,"\n";
965 18         55 my $src = $schema_name.'__generated_write_sequence@'.$path;
966 18         62 $src=~y{/}{@};
967             # TODO: check it's a Seq, warn about on undefined element
968 18 100       72 local $INDENT=-1 if $decl->is_mixed;
969 18         55 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
970             sub {
971             my ($tag,$data)=@_;
972             my ($k,$v);
973             unless (defined $data) {`;
974 18 100 66     95 if ($WITH_TREES and $decl->get_role eq '#TREES') {
975 9         35 $sub .= q`
976             if (!$have_trees and (!defined($pml_trees_type) or $pml_trees_type==$decl)) {
977             print $out (length($tag) ? `._indent().q`"<$tag>" : '>') if defined $tag;
978             $have_trees=1;`._indent_inc()._write_trees_seq($decl)._indent_dec().q`
979             print $out (length($tag) ? `._indent().q`"" : '>') if defined $tag;
980             } else {
981             print $out defined $tag ? '/>' : '>' if !$tag;
982             }`;
983             } else {
984 9         29 $sub .= q`
985             print $out defined $tag ? '/>' : '>' if !$tag;`;
986             }
987 18         56 $sub .= q`
988             return;
989             }
990             print $out (length($tag) ? `._indent().q`"<$tag>" : '>') if defined $tag;`
991             ._indent_inc()._write_seq($decl,$path,'$data')._indent_dec();
992 18         54 $sub.=q`
993             if (defined $tag and length $tag) {
994             print $out `._indent().q`"";
995             }
996             }`;
997 18         86 $src{$src}=$sub;
998 18 50       12916 $handlers{$path} = eval($sub); die _nl($sub)."\n".$@.' ' if $@;
  18         520  
999 18 100       92 $handlers{$path.'/#TEXT'} = eval q`sub { print $out ($_[1]); }` if $decl->is_mixed;
1000             } elsif ($decl_type == PML_LIST_DECL) {
1001 29         133 my $cdecl = $decl->get_content_decl;
1002 29         114 my $cpath = $cdecl->get_decl_path;
1003 29         142 $cpath=~s/^!//;
1004 29         83 my $src = $schema_name.'__generated_write_list@'.$path;
1005 29         85 $src=~y{/}{@};
1006             # TODO: check it's a List
1007 29         86 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
1008             sub {
1009             my ($tag,$data)=@_;
1010             my ($v);
1011             if (!defined $data or !@$data) {`;
1012 29 50 33     138 if ($WITH_TREES and $decl->get_role eq '#TREES') {
1013 0         0 $sub .= q`
1014             if (!$have_trees and (!defined($pml_trees_type) or $pml_trees_type==$decl)) {
1015             print $out (length($tag) ? `._indent().q`"<$tag>" : '>') if defined $tag;
1016             $have_trees=1;`._indent_inc()._write_trees_list($decl)._indent_dec().q`
1017             print $out `._indent().q`"" if defined $tag and length $tag;
1018             return;
1019             } else {
1020             print $out defined $tag ? '/>' : '>' if !$tag;
1021             return;
1022             } `;
1023             } else {
1024 29         80 $sub .= q`
1025             print $out defined $tag ? '/>' : '>' if !$tag;
1026             return;`;
1027             }
1028 29 50       94 if (!$WRITE_SINGLE_LM) {
1029 29         72 $sub .= q`
1030             } elsif (@$data==1 and defined($data->[0]) and !(UNIVERSAL::isa($data->[0],'HASH') and keys(%{$data->[0]})==0)) {
1031             print $out '>' if defined $tag and !length $tag;
1032             $handlers{ '`.$cpath.q`' }->($tag || 'LM',$data->[0]);`;
1033             }
1034 29         107 $sub .= q`
1035             } else {
1036             print $out (length($tag) ? `._indent().q`"<$tag>" : '>') if defined $tag;`._indent_inc().q`
1037             for $v (@$data) {
1038             if (defined $v and (ref $v or length $v)) {
1039             $handlers{ '`.$cpath.q`' }->('LM',$v);
1040             } else {
1041             print $out `._indent().q`"";
1042             }
1043             }`._indent_dec().q`
1044             print $out `._indent().q`"" if defined $tag and length $tag;
1045             }
1046             }`;
1047 29         183 $src{$src}=$sub;
1048 29 50       17445 $handlers{$path} = eval($sub); die _nl($sub)."\n".$@.' ' if $@;
  29         804  
1049             } elsif ($decl_type == PML_ALT_DECL) {
1050 5         20 my $cdecl = $decl->get_content_decl;
1051 5         16 my $cpath = $cdecl->get_decl_path;
1052 5         24 $cpath=~s/^!//;
1053 5         14 my $src = $schema_name.'__generated_write_alt@'.$path;
1054 5         14 $src=~y{/}{@};
1055             # TODO: check it's an Alt
1056 5         32 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
1057             sub {
1058             my ($tag,$data)=@_;
1059             unless (defined $data) {
1060             print $out defined $tag ? '/>' : '>' if !$tag;
1061             return;
1062             }
1063             if (!UNIVERSAL::DOES::does($data, 'Treex::PML::Alt')) {
1064             print $out '>' if defined $tag and !length $tag;
1065             $handlers{ '`.$cpath.q`' }->($tag || 'AM',$data);
1066             } elsif (@$data==1) {
1067             print $out '>' if defined $tag and !length $tag;
1068             $handlers{ '`.$cpath.q`' }->($tag || 'AM',$data->[0]);
1069             } elsif (@$data==0) {
1070             print $out defined $tag ? '/>' : '>' if !$tag;
1071             return;
1072             } else {
1073             my $v;
1074             print $out (length($tag) ? `._indent().q`"<$tag>" : '>') if defined $tag;`._indent_inc().q`
1075             for $v (@$data) {
1076             if (defined $v and (ref $v or length $v)) {
1077             $handlers{ '`.$cpath.q`' }->('AM',$v);
1078             } else {
1079             print $out `._indent().q`"";
1080             }
1081             }`._indent_dec().q`
1082             print $out `._indent().q`"" if defined $tag and length $tag;
1083             }
1084             }`;
1085 5         20 $src{$src}=$sub;
1086 5 50       2219 $handlers{$path} = eval($sub); die _nl($sub)."\n".$@.' ' if $@;
  5         195  
1087             } elsif ($decl_type == PML_CDATA_DECL) {
1088             # TODO: CDATA FORMAT VALIDATION
1089 101         302 my $src = $schema_name.'__generated_write_cdata@'.$path;
1090 101         249 $src=~y{/}{@};
1091 101         275 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
1092             sub {
1093             my ($tag,$data)=@_;
1094             print $out (length($tag) ? `._indent().q`"<$tag>" : '>') if defined $tag;
1095             if (defined $data and length $data) {
1096             $data=~s/&/&/g;$data=~s//]]>/g;
1097             print $out $data;
1098             }
1099             print $out "" if defined $tag and length $tag;
1100             }`;
1101 101         402 $src{$src}=$sub;
1102 101 50       38643 $handlers{$path} = eval($sub); die _nl($sub)."\n".$@.' ' if $@;
  101         2427  
1103             } elsif ($decl_type == PML_CHOICE_DECL) {
1104 14         84 my $value_hash = $decl->{value_hash};
1105 14 50       62 unless ($value_hash) {
1106 0         0 $value_hash={};
1107 0         0 @{$value_hash}{@{$decl->{values}}}=();
  0         0  
  0         0  
1108 0         0 $decl->{value_hash}=$value_hash;
1109             }
1110 14         39 my $src = $schema_name.'__generated_write_choice@'.$path;
1111 14         38 $src=~y{/}{@};
1112 14         46 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
1113             sub {
1114             my ($tag,$data)=@_;
1115             print $out (length($tag) ? `._indent().q`"<$tag>" : '>') if defined $tag;
1116             if (defined $data and length $data) {
1117             warn("Value: '$data' not allowed for choice type '`.$path.q`'; writing anyway!") if !exists $value_hash->{$data};
1118             $data=~s/&/&/g;$data=~s/
1119             print $out $data;
1120             }
1121             print $out "" if defined $tag and length $tag;
1122             }`;
1123 14         76 $src{$src}=$sub;
1124 14 50       5465 $handlers{$path} = eval($sub); die _nl($sub)."\n".$@.' ' if $@;
  14         371  
1125             } elsif ($decl_type == PML_CONSTANT_DECL) {
1126 0         0 my $value = quotemeta($decl->{value});
1127 0         0 my $src = $schema_name.'__generated_write_choice@'.$path;
1128 0         0 $src=~y{/}{@};
1129 0         0 my $sub = q`#line 1 ".pml_compile.d/`.$src.q`"
1130             sub {
1131             my ($tag,$data)=@_;
1132             print $out (length($tag) ? `._indent().q`"<$tag>" : '>') if defined $tag;
1133             if (defined $data and length $data) {
1134             warn("Invalid value '$data' in a constant type '`.$path.q`', should be '`.$value.q`'; writing anyway!") if $data ne "`.$value.q`";
1135             $data=~s/&/&/g;$data=~s/
1136             print $out $data;
1137             }
1138             print $out "" if defined $tag and length $tag;
1139             }`;
1140 0         0 $src{$src}=$sub;
1141 0 0       0 $handlers{$path} = eval($sub); die _nl($sub)."\n".$@.' ' if $@;
  0         0  
1142             }
1143             # print "@_\n";
1144 15         880 });
1145             $schema->for_each_decl(
1146             sub {
1147 466     466   732 my ($decl)=@_;
1148 466         1180 my $decl_type=$decl->get_decl_type;
1149 466 100 100     2064 if ($decl_type == PML_ATTRIBUTE_DECL ||
      100        
1150             $decl_type == PML_MEMBER_DECL ||
1151             $decl_type == PML_ELEMENT_DECL
1152             ) {
1153 159         388 my $parent = $decl->get_parent_decl;
1154 159         385 my $path = $parent->get_decl_path . '/'. $decl->get_name;
1155 159 50       609 $path =~ s/^!// if $path;
1156 159         291 my $mdecl;
1157 159 100       479 if (!exists($handlers{$path})) {
1158 47   33     193 $mdecl ||= $decl->get_content_decl;
1159 47         116 my $mpath = $mdecl->get_decl_path;
1160 47 50       199 $mpath =~ s/^!// if $mpath;
1161             # print "mapping $path -> $mpath ... $handlers{$mpath}\n";
1162 47         214 $handlers{$path} = $handlers{$mpath};
1163             }
1164             }
1165 15         165 });
1166             }
1167              
1168              
1169             }
1170              
1171             sub _nl {
1172 0     0     my ($str)=@_;
1173 0           my $i=0;
1174 0           return join "\n", map sprintf("%4d\t",$i++).$_, split /\n/, $str;
1175             }
1176              
1177             1;
1178             __END__