File Coverage

blib/lib/Treex/PML/Instance/Writer.pm
Criterion Covered Total %
statement 378 506 74.7
branch 146 276 52.9
condition 67 121 55.3
subroutine 34 38 89.4
pod 0 8 0.0
total 625 949 65.8


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