File Coverage

blib/lib/File/OM.pm
Criterion Covered Total %
statement 344 551 62.4
branch 149 358 41.6
condition 52 151 34.4
subroutine 47 68 69.1
pod 0 6 0.0
total 592 1134 52.2


line stmt bran cond sub pod time code
1             # xxx need 'raw' format, like plain but no wrap for resolve mode
2             # xxx or need 'granvl' format, like anvl but no wrap for resolve mode?
3             # xxx need 'null' format, to do ...?
4              
5             package File::OM;
6              
7 3     3   62780 use 5.006;
  3         9  
  3         123  
8 3     3   17 use strict;
  3         6  
  3         97  
9 3     3   19 use warnings;
  3         6  
  3         453  
10              
11             our $VERSION;
12             $VERSION = sprintf "%d.%02d", q$Name: Release-1-05 $ =~ /Release-(\d+)-(\d+)/;
13              
14             require Exporter;
15             our @ISA = qw(Exporter);
16              
17             our @EXPORT = qw();
18              
19             our @EXPORT_OK = qw();
20              
21             our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
22              
23 3     3   2832 use Text::Wrap; # which recommends localizing next two settings
  3         9598  
  3         27331  
24             # local $Text::Wrap::columns = $self->{wrap};
25             # local $Text::Wrap::huge = 'overflow';
26              
27             our %outputformats = (
28             anvl => 'ANVL',
29             csv => 'CSV',
30             json => 'JSON',
31             plain => 'Plain',
32             psv => 'PSV',
33             turtle => 'Turtle',
34             xml => 'XML',
35             );
36              
37             sub listformats {
38 0     0 0 0 return map $outputformats{$_}, sort keys %outputformats;
39             }
40              
41             sub om_opt_defaults { return {
42              
43 24     24 0 501 anvl_mode => # which flavor, eg, ANVL, ANVLR, ANVLS
44             'ANVL', # vanilla (unused for now)
45             elemsref => [], # one array to store record elements
46             indent_start => '', # overall starting indent
47             indent_step => # how much to increment/decrement indent
48             ' ', # for XML, JSON
49             outhandle => '', # return string by default
50             turtle_indent => # turtle has one indent width
51             ' ',
52             turtle_predns => # turtle predicate namespaces
53             'http://purl.org/kernel/elements/1.1/',
54             turtle_nosubject => # a default subject
55             'default', # XXX not a URI -- what should this be?
56             turtle_subjelpat => # pattern for matching a subject element
57             '',
58             turtle_stream_prefix => # symbol we use for turtle
59             'erc',
60             xml_stream_name => # for XML output, stream tag
61             'recs',
62             xml_record_name => # for XML output, record tag
63             'rec',
64             wrap => 72, # at which column to wrap elements (0=nowrap)
65             wrap_indent => '', # current indent for wrap, but "\t" for ANVL
66             # xxx is this even used?
67             verbose => 0, # more output (default less)
68              
69             # The following keys are maintained internally.
70             #
71             elemnum => 0, # current element number
72             indent => '', # current ident
73             recnum => 0, # current record number
74             record_is_open => 0, # whether a record is open
75             stream_is_open => 0, # whether a stream is open
76             };
77             }
78              
79             sub new {
80 24   50 24 0 270760 my $class = shift || ''; # XXX undefined depending on how called
81 24         86 my $self = om_opt_defaults();
82 24   100     112 my $format = lc (shift || '');
83 24 100       68 if ($format) {
84 20         49 $format = $outputformats{$format}; # canonical name
85 20 100       62 $format or return undef;
86 19         38 $class = "File::OM::$format";
87             }
88             else { # if no format given, expect
89 4 50       35 $class =~ /^File::OM::\S/ # to be called from subclass
90             or return undef;
91             }
92 23         72 bless $self, $class;
93              
94 23         37 my $options = shift;
95 23         31 my ($key, $value);
96 23         115 $self->{$key} = $value
97             while ($key, $value) = each %$options;
98              
99 23         76 return $self;
100             }
101              
102             # xxxx should refactor subclass methodes to more generic SUPER methods
103             # there's lots of repeated code
104              
105             sub DESTROY {
106 28     28   2327 my $self = shift;
107 28         51 my ($s, $z) = ('', ''); # built string and catchup string
108 28 100       133 $self->{stream_is_open} and # wrap up any loose ends
109             $z = $self->cstream(); # which calls crec()
110 28 50       79 $self->{outhandle} or $s .= $z; # don't retain print status
111 0         0 $self->{outhandle} and
112 28 50 50     626 return (print { $self->{outhandle} } $s)
113             or
114             return $s;
115             }
116              
117             sub elems {
118             # XXX why do 4 bytes (instead of 2) show up in wget??
119             # # %-encode any chars that need it
120             # my $except_re = qr/([\001-\037\177-\377])/; XXX needed any more?
121             # $s =~ s/$except_re/ "%" . join("", unpack("H2", $1)) /ge;
122              
123 17     17 0 956 my $self = shift;
124 17         26 my $sequence = '';
125 17         20 my ($name, $value);
126 17         16 while (1) {
127 44         78 ($name, $value) = (shift, shift); # next arg pair
128 44 100 66     144 last unless $name or $value; # done if null
129 27         58 $sequence .= $self->elem($name, $value);
130             }
131 17         80 return $sequence;
132             }
133              
134             # Shared routine to construct a header ordering based on the record
135             # in the given element array. Used by CSV and PSV formats.
136             #
137 0     0 0 0 sub rec2hdr { my( $r_elems ) = (shift);
138              
139 0         0 my ($n, $nmax) = (0, scalar @$r_elems);
140 0         0 my $r_elem_order = [ ]; # create an array reference
141              
142 0         0 for ($n = 0; $n < $nmax; $n += 3) {
143              
144 0 0       0 $n > 0 and # normal element case
145             push(@$r_elem_order, $$r_elems[$n + 1]),
146             next;
147              
148             # If we get here, $n == 0 (record start). If the record
149             # starts with a label-less value, use '_' as the name.
150             #
151 0 0       0 $$r_elems[$n + 2] and
152             push(@$r_elem_order, '_'),
153             }
154 0         0 return $r_elem_order;
155             }
156              
157             # Called in place of TextWrap::Wrap::wrap, returns string without wrapping.
158             # Second arg is a dummy.
159             #
160 1     1 0 3 sub text_nowrap { my( $line1ind, $line2ind, $val)=(shift, shift, shift);
161 1         91 return $line1ind . $val;
162             }
163              
164             package File::OM::ANVL;
165              
166             our @ISA = ('File::OM');
167              
168             sub elem { # OM::ANVL
169 29     29   43 my $self = shift;
170 29         47 my ($name, $value, $lineno, $elemnum) = (shift, shift, shift, shift);
171 29         43 my ($s, $z) = ('', ''); # built string and catchup string
172              
173 29 100       89 $self->{record_is_open} or # call orec() to open record first
174             ($z = $self->orec(undef, $lineno), # may call ostream()
175             $self->{record_is_open} = 1);
176 29 50       71 $self->{outhandle} or $s .= $z; # don't retain print status
177              
178 29 50 33     79 defined($elemnum) and
179             $self->{elemnum} = $elemnum
180             or
181             $self->{elemnum}++;
182              
183             # Parse $lineno, which is empty or has form LinenumType, where
184             # Type is either ':' (real element) or '#' (comment).
185 29 100       55 defined($lineno) or $lineno = '1:';
186 29         101 my ($num, $type) =
187             $lineno =~ /^(\d*)\s*(.)/;
188              
189 29         46 local ($Text::Wrap::columns, $Text::Wrap::huge, $Text::Wrap::unexapand);
190 29         29 my $wrapper;
191 29 100 66     219 $self->{wrap} and
192             ($wrapper, $Text::Wrap::columns, $Text::Wrap::huge,
193             $Text::Wrap::unexpand) =
194             (\&Text::Wrap::wrap, $self->{wrap}, 'overflow', 0)
195             or
196             $wrapper = \&File::OM::text_nowrap;
197             ;
198              
199 29 100       111 if ($type eq '#') {
    50          
200 1         2 $self->{element_name} = undef; # indicates comment
201 1         2 $self->{elemnum}--; # doesn't count as an element
202             #$s .= Text::Wrap::wrap( # wrap lines with '#' as
203 1         5 $s .= &$wrapper( # wrap lines with '#' as
204             '#', # first line "indent" and
205             '# ', # '# ' for all other indents
206             $self->comment_encode($value) # main part to wrap
207             );
208 1         156 $s .= "\n"; # close comment
209             }
210             # XXX what if ref($value) eq "ARRAY" -> can be used for repeated vals?
211             # XXX does undefined $name mean comment?
212             # XXX document what undef for $name means
213             elsif (defined $name) { # no element if no name
214             # XXX would it look cooler with :\t after the label??
215             # xxx this should be stacked
216 28         64 $self->{element_name} = $self->name_encode($name);
217 28         58 my $enc_val = $self->value_encode($value); # encoded value
218              
219 28 100       163 $s .= $enc_val =~ /^\s*$/ ? # wrap() loses label of
220             "$self->{element_name}:$enc_val" : # blank value
221             &$wrapper( # wrap lines; this 1st
222             $self->{element_name} # "indent" won't break
223             . ':', # label across lines
224             "\t", # tab for other indents
225             $enc_val) # main part to wrap
226             ;
227 28         3483 $s .= "\n";
228             # M_ELEMENT and C_ELEMENT would start here
229             }
230 0         0 $self->{outhandle} and
231 29 50 50     195 return (print { $self->{outhandle} } $s)
232             or
233             return $s;
234             }
235              
236             # XXX need something that will spit out whole input ANVL record
237             sub anvl_rec { # OM::ANVL
238 0     0   0 my $self = shift;
239 0         0 my $rec = shift;
240             # XXX ignore lineno for now
241 0         0 my ($name, $value, $lineno, $elemnum) = (shift, shift, shift, shift);
242 0         0 my ($s, $z) = ('', ''); # built string and catchup string
243              
244 0 0       0 $self->{record_is_open} or # call orec() to open record first
245             ($z = $self->orec(undef, $lineno), # may call ostream()
246             $self->{record_is_open} = 1);
247 0 0       0 $self->{outhandle} or $s .= $z; # don't retain print status
248              
249             #defined($elemnum) and
250             # $self->{elemnum} = $elemnum
251             #or
252             # $self->{elemnum}++;
253              
254             # Parse $lineno, which is empty or has form LinenumType, where
255             # Type is either ':' (real element) or '#' (comment).
256 0 0       0 defined($lineno) or $lineno = '1:';
257 0         0 my ($num, $type) =
258             $lineno =~ /^(\d*)\s*(.)/;
259              
260 0         0 local ($Text::Wrap::columns, $Text::Wrap::huge);
261 0         0 my $wrapper;
262 0 0 0     0 $self->{wrap} and
263             ($wrapper, $Text::Wrap::columns, $Text::Wrap::huge) =
264             (\&Text::Wrap::wrap, $self->{wrap}, 'overflow')
265             or
266             $wrapper = \&File::OM::text_nowrap;
267             ;
268              
269              
270 0 0       0 if ($type eq '#') {
    0          
271 0         0 $self->{element_name} = undef; # indicates comment
272 0         0 $self->{elemnum}--; # doesn't count as an element
273             #$s .= Text::Wrap::wrap( # wrap lines with '#' as
274 0         0 $s .= &$wrapper( # wrap lines with '#' as
275             '#', # first line "indent" and
276             '# ', # '# ' for all other indents
277             $self->comment_encode($value) # main part to wrap
278             );
279 0         0 $s .= "\n"; # close comment
280             }
281             # XXX what if ref($value) eq "ARRAY" -> can be used for repeated vals?
282             # XXX does undefined $name mean comment?
283             # XXX document what undef for $name means
284             elsif (defined $name) { # no element if no name
285             # XXX would it look cooler with :\t after the label??
286             # xxx this should be stacked
287 0         0 $self->{element_name} = $self->name_encode($name);
288 0         0 my $enc_val = $self->value_encode($value); # encoded value
289 0 0       0 $s .= $enc_val =~ /^\s*$/ ? # wrap() loses label of
290             "$self->{element_name}:$enc_val" : # blank value
291             &$wrapper( # wrap lines; this 1st
292             $self->{element_name} # "indent" won't break
293             . ':', # label across lines
294             "\t", # tab for other indents
295             $enc_val) # main part to wrap
296             ;
297 0         0 $s .= "\n";
298             # M_ELEMENT and C_ELEMENT would start here
299             }
300 0         0 $self->{outhandle} and
301 0 0 0     0 return (print { $self->{outhandle} } $s)
302             or
303             return $s;
304             }
305              
306             sub orec { # OM::ANVL
307 6     6   7 my $self = shift;
308 6         12 my ($recnum, $lineno) = (shift, shift);
309 6         12 my ($s, $z) = ('', ''); # built string and catchup string
310              
311 6         11 $self->{elemnum} = 0;
312 6 50       31 $self->{stream_is_open} or # call ostream() to open stream first
313             ($z = $self->ostream(),
314             $self->{stream_is_open} = 1);
315 6         10 $self->{record_is_open} = 1;
316 6 50       16 $self->{outhandle} or $s .= $z; # don't retain print status
317              
318 6 50 33     26 defined($recnum) and
319             $self->{recnum} = $recnum
320             or
321             $self->{recnum}++;
322              
323 6 50       20 defined($lineno) or $lineno = '1:';
324             # xxxx really? will someone pass that in?
325              
326 6 50       18 $self->{verbose} and
327             $s .= "# from record $self->{recnum}, line $lineno\n";
328 0         0 $self->{outhandle} and
329 6 50 50     35 return (print { $self->{outhandle} } $s)
330             or
331             return $s;
332             }
333              
334             sub crec { # OM::ANVL
335 6     6   12 my ($self, $recnum) = (shift, shift);
336 6         8 $self->{record_is_open} = 0;
337 6         10 my $s = "\n";
338 0         0 $self->{outhandle} and
339 6 50 50     32 return (print { $self->{outhandle} } $s)
340             or
341             return $s;
342             }
343              
344             # xxx anvl -m anvln? n=normalized?
345             sub ostream { # OM::ANVL
346 6     6   9 my $self = shift;
347              
348 6         12 $self->{recnum} = 0;
349 6         11 $self->{stream_is_open} = 1;
350 6         10 my $s = '';
351 0         0 $self->{outhandle} and
352 6 50 50     33 return (print { $self->{outhandle} } $s)
353             or
354             return $s;
355             }
356              
357             sub cstream { # OM::ANVL
358 6     6   10 my $self = shift;
359 6         11 my ($s, $z) = ('', ''); # built string and catchup string
360 6 50       27 $self->{record_is_open} and # wrap up any loose ends
361             $z = $self->crec();
362 6 50       20 $self->{outhandle} or $s .= $z; # don't retain print status
363 6         8 $self->{stream_is_open} = 0;
364 0         0 $self->{outhandle} and
365 6 50 50     29 return (print { $self->{outhandle} } $s)
366             or
367             return $s;
368             }
369              
370             sub name_encode { # OM::ANVL
371 28     28   39 my ($self, $s) = (shift, shift);
372 28 50       94 defined($s) or return '';
373             #$s =~ s/^\s+//;
374             #$s =~ s/\s+$//; # trim both ends
375             #$s =~ s/\s+/ /g; # squeeze multiple \s to one space
376             # xxx keep doubling %?
377 28         44 $s =~ s/%/%%/g; # to preserve literal %, double it
378             # xxx what about granvl?
379             # yyy must be decoded by receiver
380             #$s =~ s/:/%3a/g; # URL-encode all colons (%cn)
381              
382 28         53 $s =~ s{ # URL-encode all colons and whitespace
383             ([=:<\s]) # \s matches [ \t\n\f] etc.
384             }{ # = and < anticipate ANVL extensions
385 12         48 sprintf("%%%02x", ord($1)) # replacement hex code
386             }xeg;
387              
388             # This next line takes care of the mainstream case of names that
389             # contain spaces. It makes sure that for every run of one or more
390             # spaces, the first space won't be encoded.
391             #
392 28         50 $s =~ s/%20((?:%20)*)/ $1/g;
393 28         34 $s =~ s/^ /%20/; # but make sure any initial space is encoded
394 28         31 $s =~ s/ $/%20/; # and make sure any final space is encoded
395              
396 28         66 return $s;
397              
398             # XXXX must convert XML namespaces to make safe for ANVL
399             # foo:bar ->? bar.foo (sort friendly, and puts namespace into
400             # proper subordinate position similar to dictionaries)?
401             # or if not namespace, foo:bar ->? foo%xxbar
402             }
403              
404             # Encoding of names and values is done upon output in ANVL.
405             # Default is to wrap long lines.
406              
407             sub value_encode { # OM::ANVL
408 28     28   46 my ($self, $s, $anvl_mode) = (shift, shift, shift);
409 28 100       55 defined($s) or return '';
410 27   50     99 $anvl_mode ||= 'ANVL';
411              
412 27         32 my $value = $s; # save original value
413             #my ($initial_newlines) = # save initial newlines
414             # $s =~ /^(\n*)/; # always defined, often ""
415              
416             ## value after colon starts with either preserved newlines,
417             # a space, or, if value is "" (as opposed to 0), nothing
418             #
419             #my $value_start = $initial_newlines || ($value eq "" ? '' : ' ');
420             #my $value_start = $initial_newlines || ($value eq "" ? '' : ' ');
421 27 100       50 my $value_start = $value eq "" ? '' : ' ';
422              
423             #my $value_start = $initial_newlines || ($value ? ' ' : '');
424             # xxxx is this the right place to enforce the space after ':'?
425              
426             # xxx is there a linear whitespace char class??
427             # problem is that \s includes \n
428             #$s =~ s/^\s+//;
429             #$s =~ s/\s+$//; # trim both ends
430              
431 27         42 $s =~ s/%/%%/g; # to preserve literal %, double it
432             # yyy must be decoded by receiver
433 27         47 $s =~ s{ # URL-encode newlines in portable way
434             (\n) # \n matches all platforms' ends of lines
435             }{ #
436 13         47 sprintf("%%%02x", ord($1)) # replacement hex code
437             }xeg;
438 27 50       60 if ($anvl_mode eq 'ANVLS') {
439 0         0 $s =~ s/\|/%7c/g; # URL-encode all vertical bars (%vb)
440 0         0 $s =~ s/;/%3b/g; # URL-encode all semi-colons (%sc)
441             # XXX what about others, such as (:...) (=...)
442             };
443 27         72 return $value_start . $s;
444             }
445              
446             sub comment_encode { # OM::ANVL
447 1     1   3 my ($self, $s) = (shift, shift);
448 1 50       6 defined($s) or return '';
449 1         4 $s =~ s/\n/\\n/g; # escape \n yyy??
450 1         5 return $s;
451             }
452              
453             package File::OM::CSV;
454              
455             our @ISA = ('File::OM');
456              
457             sub elem { # OM::CSV
458 0     0   0 my $self = shift;
459 0         0 my ($name, $value, $lineno, $elemnum) = (shift, shift, shift, shift);
460 0         0 my ($s, $z) = ('', ''); # built string and catchup string
461              
462 0 0       0 $self->{record_is_open} or # call orec() to open record first
463             ($z = $self->orec(undef, $lineno), # may call ostream()
464             $self->{record_is_open} = 1);
465 0 0       0 $self->{outhandle} or $s .= $z; # don't retain print status
466              
467 0 0 0     0 defined($elemnum) and
468             $self->{elemnum} = $elemnum
469             or
470             $self->{elemnum}++;
471              
472             # Parse $lineno, which is empty or has form LinenumType, where
473             # Type is either ':' (real element) or '#' (comment).
474 0 0       0 defined($lineno) or $lineno = '1:';
475 0         0 my ($num, $type) =
476             $lineno =~ /^(\d*)\s*(.)/;
477              
478 0         0 local ($Text::Wrap::columns, $Text::Wrap::huge);
479 0         0 my $wrapper;
480 0 0 0     0 $self->{wrap} and
481             ($wrapper, $Text::Wrap::columns, $Text::Wrap::huge) =
482             (\&Text::Wrap::wrap, $self->{wrap}, 'overflow')
483             or
484             $wrapper = \&File::OM::text_nowrap;
485             ;
486              
487              
488 0 0       0 $self->{elemnum} > 1 and # we've output an element already,
489             $s .= ","; # so output a separator character
490              
491 0 0       0 if ($type eq '#') {
    0          
492 0         0 $self->{element_name} = undef; # indicates comment
493 0         0 $s .= &$wrapper( # wrap lines with '#' as
494             '#', # first line "indent" and
495             '# ', # '# ' for all other indents
496             $self->comment_encode($value) # main part to wrap
497             );
498             }
499             elsif (defined $name) { # no element if no name
500             # xxx this should be stacked
501 0         0 $self->{element_name} = $self->name_encode($name);
502 0         0 my $enc_val =
503             $s .= &$wrapper('', '',
504             $self->value_encode($value)); # encoded value
505             # M_ELEMENT and C_ELEMENT would start here
506             }
507 0         0 $self->{outhandle} and
508 0 0 0     0 return (print { $self->{outhandle} } $s)
509             or
510             return $s;
511             }
512              
513             sub orec { # OM::CSV
514 0     0   0 my $self = shift;
515 0         0 my ($recnum, $lineno) = (shift, shift);
516 0         0 my ($s, $z) = ('', ''); # built string and catchup string
517              
518 0         0 $self->{elemnum} = 0;
519 0 0       0 $self->{stream_is_open} or # call ostream() to open stream first
520             ($z = $self->ostream(),
521             $self->{stream_is_open} = 1);
522 0         0 $self->{record_is_open} = 1;
523 0 0       0 $self->{outhandle} or $s .= $z; # don't retain print status
524              
525 0 0 0     0 defined($recnum) and
526             $self->{recnum} = $recnum
527             or
528             $self->{recnum}++;
529              
530 0 0       0 defined($lineno) or $lineno = '1:';
531             # xxxx really? will someone pass that in?
532              
533 0 0       0 if ($self->{recnum} == 1) {
534              
535             # We're one of the few orec's that use these args.
536             # We do it only to output and possibly define headers.
537             #
538 0         0 my ($r_elems, $r_elem_order) = (shift, shift);
539              
540             # If the number and order of elements are not defined,
541             # construct them from the ordering implied by record 1.
542             #
543 0 0       0 $r_elem_order or
544             $r_elem_order = File::OM::rec2hdr($r_elems);
545              
546             # We're at record 1 in a CVS file, so output a header.
547             #
548 0         0 $s .= join(",", map(name_encode($self, $_), @$r_elem_order))
549             . "\n";
550             }
551              
552 0 0       0 $self->{verbose} and
553             $s .= "# from record $self->{recnum}, line $lineno\n";
554 0         0 $self->{outhandle} and
555 0 0 0     0 return (print { $self->{outhandle} } $s)
556             or
557             return $s;
558             }
559              
560             sub crec { # OM::CSV
561 0     0   0 my ($self, $recnum) = (shift, shift);
562 0         0 $self->{record_is_open} = 0;
563 0         0 my $s = "\n";
564 0         0 $self->{outhandle} and
565 0 0 0     0 return (print { $self->{outhandle} } $s)
566             or
567             return $s;
568             }
569              
570             sub ostream { # OM::CSV
571 0     0   0 my $self = shift;
572              
573 0         0 $self->{recnum} = 0;
574 0         0 $self->{stream_is_open} = 1;
575 0         0 my $s = '';
576 0         0 $self->{outhandle} and
577 0 0 0     0 return (print { $self->{outhandle} } $s)
578             or
579             return $s;
580             }
581              
582             sub cstream { # OM::CSV
583 0     0   0 my $self = shift;
584 0         0 my ($s, $z) = ('', ''); # built string and catchup string
585 0 0       0 $self->{record_is_open} and # wrap up any loose ends
586             $z = $self->crec();
587 0 0       0 $self->{outhandle} or $s .= $z; # don't retain print status
588 0         0 $self->{stream_is_open} = 0;
589 0         0 $self->{outhandle} and
590 0 0 0     0 return (print { $self->{outhandle} } $s)
591             or
592             return $s;
593             }
594              
595             sub name_encode { # OM::CSV
596             # CSV names used only in header line
597 0     0   0 my ($self, $s) = (shift, shift);
598 0 0       0 defined($s) or return '';
599              
600             # yyy should names be put inside double quotes?
601             #$s =~ s/^\s+//;
602             #$s =~ s/\s+$//; # trim both ends
603             #$s =~ s/\s+/ /g; # squeeze multiple \s to one space
604 0         0 $s =~ s/"/""/g; # double all internal double-quotes
605              
606 0         0 return $s;
607             }
608              
609             sub value_encode { # OM::CSV
610 0     0   0 my ($self, $s) = (shift, shift);
611 0 0       0 defined($s) or return '';
612              
613 0         0 $s =~ s/"/""/g; # double all internal double-quotes
614 0         0 $s =~ s/^/"/;
615 0         0 $s =~ s/$/"/;
616             #$s =~ s/^\s*/"/;
617             #$s =~ s/\s*$/"/; # trim both ends and double-quote
618              
619 0         0 return $s;
620             }
621              
622             sub comment_encode { # OM::CSV
623             # in CSV this would be a pseudo-comment
624 0     0   0 my ($self, $s) = (shift, shift);
625 0 0       0 defined($s) or return '';
626              
627 0         0 $s =~ s/"/""/g; # double all internal double-quotes
628 0         0 $s =~ s/^/"/;
629 0         0 $s =~ s/$/"/;
630             #$s =~ s/^\s*/"/;
631             #$s =~ s/\s*$/"/; # trim both ends and double-quote
632              
633 0         0 return $s;
634             }
635              
636             package File::OM::JSON;
637              
638             our @ISA = ('File::OM');
639              
640             sub elem { # OM::JSON
641 11     11   565 my $self = shift;
642 11         31 my ($name, $value, $lineno, $elemnum) = (shift, shift, shift, shift);
643 11         22 my ($s, $z) = ('', ''); # built string and catchup string
644              
645 11 100       48 $self->{record_is_open} or # call orec() to open record first
646             ($z = $self->orec(undef, $lineno), # may call ostream()
647             $self->{record_is_open} = 1);
648 11 50       37 $self->{outhandle} or $s .= $z; # don't retain print status
649              
650 11 100 66     42 defined($elemnum) and
651             $self->{elemnum} = $elemnum
652             or
653             $self->{elemnum}++;
654              
655             # Parse $lineno, which is empty or has form LinenumType, where
656             # Type is either ':' (real element) or '#' (comment).
657 11 100       23 defined($lineno) or $lineno = '1:';
658 11         50 my ($num, $type) =
659             $lineno =~ /^(\d*)\s*(.)/;
660              
661 11 50       34 $type eq '#' and $name = '#'; # JSON pseudo-comment!
662 11 50       22 $type eq '#' and $self->{elemnum}--; # doesn't count as elem
663 11 50       21 if (defined $name) { # no element if no name
664 11         31 $self->{element_name} = $self->name_encode($name);
665             # either real element or pseudo-comment element was used
666 11 100 66     48 $self->{elemnum} > 1 || $self->{verbose} and
667             $s .= ',';
668 11         23 $s .= "\n" . $self->{indent};
669 11         32 $s .= '"' . $self->{element_name} . '": "'
670             . $self->value_encode($value) . '"';
671             }
672 0         0 $self->{outhandle} and
673 11 50 50     73 return (print { $self->{outhandle} } $s)
674             or
675             return $s;
676             }
677              
678             sub orec { # OM::JSON
679 4     4   10 my $self = shift;
680 4         7 my ($recnum, $lineno) = (shift, shift);
681 4         11 my ($s, $z) = ('', ''); # built string and catchup string
682              
683 4         8 $self->{elemnum} = 0;
684 4 100       27 $self->{stream_is_open} or # call ostream() to open stream first
685             ($z = $self->ostream(),
686             $self->{stream_is_open} = 1);
687 4 50       18 $self->{outhandle} or $s .= $z; # don't retain print status
688 4         11 $self->{record_is_open} = 1;
689              
690 4 50 33     18 defined($recnum) and
691             $self->{recnum} = $recnum
692             or
693             $self->{recnum}++;
694              
695 4 50       13 defined($lineno) or $lineno = '1:';
696             # yyy really? will someone pass that in?
697              
698 4 100       13 $self->{recnum} > 1 and $s .= ',';
699 4         10 $s .= "\n" . $self->{indent} . '{'; # use indent and
700 4 50       54 $self->{verbose} and
701             $s .= qq@ "#": "from record $self->{recnum}, line $lineno"@;
702 4         30 $self->{indent} =~ s/$/$self->{indent_step}/; # increase indent
703 0         0 $self->{outhandle} and
704 4 50 50     25 return (print { $self->{outhandle} } $s)
705             or
706             return $s;
707             }
708              
709             sub crec { # OM::JSON
710 4     4   1375 my ($self, $recnum) = (shift, shift);
711 4         7 $self->{record_is_open} = 0;
712 4         48 $self->{indent} =~ s/$self->{indent_step}$//; # decrease indent
713 4         15 my $s = "\n" . $self->{indent} . '}'; # and use indent
714 0         0 $self->{outhandle} and
715 4 50 50     27 return (print { $self->{outhandle} } $s)
716             or
717             return $s;
718             }
719              
720             sub ostream { # OM::JSON
721 3     3   4 my $self = shift;
722              
723 3         9 $self->{recnum} = 0;
724 3         6 $self->{stream_is_open} = 1;
725 3   50     10 $self->{indent_step} ||= ' '; # standard indent width
726 3         6 $self->{indent} = $self->{indent_step}; # current indent width
727 3         6 my $s = '[';
728 0         0 $self->{outhandle} and
729 3 50 50     19 return (print { $self->{outhandle} } $s)
730             or
731             return $s;
732             }
733              
734             sub cstream { # OM::JSON
735 3     3   7 my $self = shift;
736 3         7 my ($s, $z) = ('', ''); # built string and catchup string
737 3 50       16 $self->{record_is_open} and # wrap up any loose ends
738             $z = $self->crec();
739 3 50       11 $self->{outhandle} or $s .= $z; # don't retain print status
740 3         5 $self->{stream_is_open} = 0;
741 3         23 $self->{indent} =~ s/$self->{indent_step}$//; # decrease indent
742 3         8 $s .= "\n]\n";
743 0         0 $self->{outhandle} and
744 3 50 50     18 return (print { $self->{outhandle} } $s)
745             or
746             return $s;
747             }
748              
749             sub name_encode { # OM::JSON
750 24     24   36 my ($self, $s) = (shift, shift);
751 24 50       48 defined($s) or return '';
752 24         46 $s =~ s/(["\\])/\\$1/g; # excape " and \
753 24         32 $s =~ s{
754             ([\x00-\x1f]) # escape all control chars
755             }{
756 2         12 sprintf("\\u00%02x", ord($1)) # replacement hex code
757             }xeg;
758 24         102 return $s;
759             }
760              
761             sub value_encode { # OM::JSON
762 12     12   14 my $self = shift;
763 12         28 return $self->name_encode(@_);
764             }
765              
766             sub comment_encode { # OM::JSON
767 0     0   0 my $self = shift;
768 0         0 return $self->name_encode(@_);
769             }
770              
771             package File::OM::Plain;
772              
773             our @ISA = ('File::OM');
774              
775             sub elem { # OM::Plain
776 5     5   523 my $self = shift;
777 5         16 my ($name, $value, $lineno, $elemnum) = (shift, shift, shift, shift);
778 5         9 my ($s, $z) = ('', ''); # built string and catchup string
779              
780 5 100       30 $self->{record_is_open} or # call orec() to open record first
781             ($z = $self->orec(undef, $lineno), # may call ostream()
782             $self->{record_is_open} = 1);
783 5 50       13 $self->{outhandle} or $s .= $z; # don't retain print status
784              
785 5 50 33     16 defined($elemnum) and
786             $self->{elemnum} = $elemnum
787             or
788             $self->{elemnum}++;
789              
790             # Parse $lineno, which is empty or has form LinenumType, where
791             # Type is either ':' (real element) or '#' (comment).
792 5 100       12 defined($lineno) or $lineno = '1:';
793 5         21 my ($num, $type) =
794             $lineno =~ /^(\d*)\s*(.)/;
795              
796 5         12 local ($Text::Wrap::columns, $Text::Wrap::huge);
797 5         4 my $wrapper;
798 5 50 33     39 $self->{wrap} and
799             ($wrapper, $Text::Wrap::columns, $Text::Wrap::huge) =
800             (\&Text::Wrap::wrap, $self->{wrap}, 'overflow')
801             or
802             $wrapper = \&File::OM::text_nowrap;
803             ;
804              
805 5 100 33     26 if ($type eq '#') { # Plain pseudo-comment!
    50          
806 1         3 $self->{element_name} = undef; # indicates comment
807 1         3 $self->{elemnum}--; # doesn't count as an element
808 1         5 $s .= &$wrapper( # wrap lines with '#' as
809             '#', # first line "indent" and
810             '# ', # '# ' for all other indents
811             $self->comment_encode($value) # main part to wrap
812             );
813 1         171 $s .= "\n"; # close comment
814             }
815             elsif (defined($value) and defined($name)) { # no element if no name
816             # It is a feature of Plain not to print if value is empty.
817 4         8 $self->{element_name} = $self->name_encode($name);
818 4         9 $s .= &$wrapper( # wrap lines with '' as
819             '', # first line "indent" and
820             '', # '' for all other indents
821             $self->value_encode($value) # main part to wrap
822             );
823 4         478 $s .= "\n";
824             }
825 0         0 $self->{outhandle} and
826 5 50 50     42 return (print { $self->{outhandle} } $s)
827             or
828             return $s;
829             }
830              
831             sub orec { # OM::Plain
832 3     3   5 my $self = shift;
833 3         6 my ($recnum, $lineno) = (shift, shift);
834 3         8 my ($s, $z) = ('', ''); # built string and catchup string
835              
836 3         5 $self->{elemnum} = 0;
837 3 50       16 $self->{stream_is_open} or # call ostream() to open stream first
838             ($z = $self->ostream(),
839             $self->{stream_is_open} = 1);
840 3 50       13 $self->{outhandle} or $s .= $z; # don't retain print status
841 3         4 $self->{record_is_open} = 1;
842              
843 3 50 33     10 defined($recnum) and
844             $self->{recnum} = $recnum
845             or
846             $self->{recnum}++;
847              
848 3 100       27 defined($lineno) or $lineno = '1:';
849              
850 3 50       9 $self->{verbose} and
851             $s .= "# from record $recnum, line $lineno\n";
852 0         0 $self->{outhandle} and
853 3 50 50     21 return (print { $self->{outhandle} } $s)
854             or
855             return $s;
856             }
857              
858             sub crec { # OM::Plain
859 3     3   9 my ($self, $recnum) = (shift, shift);
860 3         4 $self->{record_is_open} = 0;
861 3         7 my $s = "\n";
862 0         0 $self->{outhandle} and
863 3 50 50     20 return (print { $self->{outhandle} } $s)
864             or
865             return $s;
866             }
867              
868             sub ostream { # OM::Plain
869 3     3   6 my $self = shift;
870 3         5 my $s = '';
871              
872 3         3 $self->{recnum} = 0;
873 3         6 $self->{stream_is_open} = 1;
874 0         0 $self->{outhandle} and
875 3 50 50     17 return (print { $self->{outhandle} } $s)
876             or
877             return $s;
878             #$$o{indent_step} ||= ''; # standard indent width
879             #$$o{indent} = $$o{indent_step}; # current indent width
880             }
881              
882             sub cstream { # OM::Plain
883 3     3   5 my $self = shift;
884 3         6 my ($s, $z) = ('', ''); # built string and catchup string
885 3 50       17 $self->{record_is_open} and # wrap up any loose ends
886             $z = $self->crec();
887 3 50       9 $self->{outhandle} or $s .= $z; # don't retain print status
888 3         81 $self->{stream_is_open} = 0;
889 0         0 $self->{outhandle} and
890 3 50 50     19 return (print { $self->{outhandle} } $s)
891             or
892             return $s;
893             }
894              
895             sub name_encode { # OM::Plain
896 4     4   6 my ($self, $s) = (shift, shift);
897 4         10 return $s;
898             }
899              
900             sub value_encode { # OM::Plain
901 4     4   6 my ($self, $s) = (shift, shift);
902 4         14 return $s;
903             }
904              
905             sub comment_encode { # OM::Plain
906 1     1   3 my ($self, $s) = (shift, shift);
907 1         6 return $s;
908             }
909              
910             # XXXXXXXXXX just a copy of CSV for now
911             package File::OM::PSV;
912              
913             our @ISA = ('File::OM');
914              
915             sub elem { # OM::PSV
916 0     0   0 my $self = shift;
917 0         0 my ($name, $value, $lineno, $elemnum) = (shift, shift, shift, shift);
918 0         0 my ($s, $z) = ('', ''); # built string and catchup string
919              
920 0 0       0 $self->{record_is_open} or # call orec() to open record first
921             ($z = $self->orec(undef, $lineno), # may call ostream()
922             $self->{record_is_open} = 1);
923 0 0       0 $self->{outhandle} or $s .= $z; # don't retain print status
924              
925 0 0 0     0 defined($elemnum) and
926             $self->{elemnum} = $elemnum
927             or
928             $self->{elemnum}++;
929              
930             # Parse $lineno, which is empty or has form LinenumType, where
931             # Type is either ':' (real element) or '#' (comment).
932 0 0       0 defined($lineno) or $lineno = '1:';
933 0         0 my ($num, $type) =
934             $lineno =~ /^(\d*)\s*(.)/;
935              
936 0         0 local ($Text::Wrap::columns, $Text::Wrap::huge);
937 0         0 my $wrapper;
938 0 0 0     0 $self->{wrap} and
939             ($wrapper, $Text::Wrap::columns, $Text::Wrap::huge) =
940             (\&Text::Wrap::wrap, $self->{wrap}, 'overflow')
941             or
942             $wrapper = \&File::OM::text_nowrap;
943             ;
944              
945              
946 0 0       0 $self->{elemnum} > 1 and # we've output an element already,
947             $s .= "|"; # so output a separator character
948              
949 0 0       0 if ($type eq '#') {
    0          
950 0         0 $self->{element_name} = undef; # indicates comment
951 0         0 $s .= &$wrapper( # wrap lines with '#' as
952             '#', # first line "indent" and
953             '# ', # '# ' for all other indents
954             $self->comment_encode($value) # main part to wrap
955             );
956             }
957             elsif (defined $name) { # no element if no name
958             # xxx this should be stacked
959 0         0 $self->{element_name} = $self->name_encode($name);
960 0         0 my $enc_val =
961             $s .= &$wrapper('', '',
962             $self->value_encode($value)); # encoded value
963             # M_ELEMENT and C_ELEMENT would start here
964             }
965 0         0 $self->{outhandle} and
966 0 0 0     0 return (print { $self->{outhandle} } $s)
967             or
968             return $s;
969             }
970              
971             sub orec { # OM::PSV
972 0     0   0 my $self = shift;
973 0         0 my ($recnum, $lineno) = (shift, shift);
974 0         0 my ($s, $z) = ('', ''); # built string and catchup string
975              
976 0         0 $self->{elemnum} = 0;
977 0 0       0 $self->{stream_is_open} or # call ostream() to open stream first
978             ($z = $self->ostream(),
979             $self->{stream_is_open} = 1);
980 0         0 $self->{record_is_open} = 1;
981 0 0       0 $self->{outhandle} or $s .= $z; # don't retain print status
982              
983 0 0 0     0 defined($recnum) and
984             $self->{recnum} = $recnum
985             or
986             $self->{recnum}++;
987              
988 0 0       0 defined($lineno) or $lineno = '1:';
989             # xxxx really? will someone pass that in?
990              
991 0 0       0 if ($self->{recnum} == 1) {
992              
993             # We're one of the few orec's that use these args.
994             # We do it only to output and possibly define headers.
995             #
996 0         0 my ($r_elems, $r_elem_order) = (shift, shift);
997              
998             # If the number and order of elements are not defined,
999             # construct them from the ordering implied by record 1.
1000             #
1001 0 0       0 $r_elem_order or
1002             $r_elem_order = File::OM::rec2hdr($r_elems);
1003              
1004             # We're at record 1 in a CVS file, so output a header.
1005             #
1006 0         0 $s .= join("|", map(name_encode($self, $_), @$r_elem_order))
1007             . "\n";
1008             }
1009              
1010 0 0       0 $self->{verbose} and
1011             $s .= "# from record $self->{recnum}, line $lineno\n";
1012 0         0 $self->{outhandle} and
1013 0 0 0     0 return (print { $self->{outhandle} } $s)
1014             or
1015             return $s;
1016             }
1017              
1018             sub crec { # OM::PSV
1019 0     0   0 my ($self, $recnum) = (shift, shift);
1020 0         0 $self->{record_is_open} = 0;
1021 0         0 my $s = "\n";
1022 0         0 $self->{outhandle} and
1023 0 0 0     0 return (print { $self->{outhandle} } $s)
1024             or
1025             return $s;
1026             }
1027              
1028             sub ostream { # OM::PSV
1029 0     0   0 my $self = shift;
1030              
1031 0         0 $self->{recnum} = 0;
1032 0         0 $self->{stream_is_open} = 1;
1033 0         0 my $s = '';
1034 0         0 $self->{outhandle} and
1035 0 0 0     0 return (print { $self->{outhandle} } $s)
1036             or
1037             return $s;
1038             }
1039              
1040             sub cstream { # OM::PSV
1041 0     0   0 my $self = shift;
1042 0         0 my ($s, $z) = ('', ''); # built string and catchup string
1043 0 0       0 $self->{record_is_open} and # wrap up any loose ends
1044             $z = $self->crec();
1045 0 0       0 $self->{outhandle} or $s .= $z; # don't retain print status
1046 0         0 $self->{stream_is_open} = 0;
1047 0         0 $self->{outhandle} and
1048 0 0 0     0 return (print { $self->{outhandle} } $s)
1049             or
1050             return $s;
1051             }
1052              
1053             sub name_encode { # OM::PSV
1054             # PSV names used only in header line
1055 0     0   0 my ($self, $s) = (shift, shift);
1056 0 0       0 defined($s) or return '';
1057              
1058             # xxx document how we don't trim, but encode spaces
1059             # xxxxxxx and then encode!!
1060             #$s =~ s/^\s+//;
1061             #$s =~ s/\s+$//; # trim both ends
1062             #$s =~ s/\s+/ /g; # squeeze multiple \s to one space
1063 0         0 $s =~ s/%/%%/g; # to preserve literal %, double it
1064             # yyy must be decoded by receiver
1065 0         0 $s =~ s/\|/%7c/g; # URL-encode all colons
1066 0         0 $s =~ s/\n/%0a/g; # URL-encode all newlines
1067              
1068 0         0 return $s;
1069             }
1070              
1071             sub value_encode { # OM::PSV
1072 0     0   0 my ($self, $s) = (shift, shift);
1073 0 0       0 defined($s) or return '';
1074              
1075             # xxx document how we don't trim, but encode spaces
1076             # xxxxxxx and then encode!!
1077             #$s =~ s/^\s+//;
1078             #$s =~ s/\s+$//; # trim both ends
1079             #$s =~ s/\s+/ /g; # squeeze multiple \s to one space
1080 0         0 $s =~ s/%/%%/g; # to preserve literal %, double it
1081             # yyy must be decoded by receiver
1082 0         0 $s =~ s/\|/%7c/g; # URL-encode all colons
1083 0         0 $s =~ s/\n/%0a/g; # URL-encode all newlines
1084              
1085 0         0 return $s;
1086             }
1087              
1088             sub comment_encode { # OM::PSV
1089             # in PSV this would be a pseudo-comment
1090 0     0   0 my ($self, $s) = (shift, shift);
1091 0 0       0 defined($s) or return '';
1092              
1093 0         0 $s =~ s/%/%%/g; # to preserve literal %, double it
1094             # yyy must be decoded by receiver
1095 0         0 $s =~ s/\|/%7c/g; # URL-encode all colons
1096 0         0 $s =~ s/\n/%0a/g; # URL-encode all newlines
1097              
1098 0         0 return $s;
1099             }
1100              
1101             package File::OM::Turtle;
1102              
1103             our @ISA = ('File::OM');
1104              
1105             sub elem { # OM::Turtle
1106              
1107 4     4   10 my $self = shift;
1108 4         11 my ($name, $value, $lineno, $elemnum) = (shift, shift, shift, shift);
1109 4         8 my ($s, $z) = ('', ''); # built string and catchup string
1110              
1111 4 100       16 $self->{record_is_open} or # call orec() to open record first
1112             ($z = $self->orec(undef, $lineno), # may call ostream()
1113             $self->{record_is_open} = 1);
1114 4 50       15 $self->{outhandle} or $s .= $z; # don't retain print status
1115              
1116 4 50 33     17 defined($elemnum) and
1117             $self->{elemnum} = $elemnum
1118             or
1119             $self->{elemnum}++;
1120              
1121             # Parse $lineno, which is empty or has form LinenumType, where
1122             # Type is either ':' (real element) or '#' (comment).
1123 4 50       11 defined($lineno) or $lineno = '1:';
1124 4         18 my ($num, $type) =
1125             $lineno =~ /^(\d*)\s*(.)/;
1126              
1127 4 50       18 if ($type eq '#') {
    50          
1128 0         0 $self->{element_name} = undef; # indicates comment
1129 0         0 $self->{elemnum}--; # doesn't count as an element
1130 0         0 $s .= "\n#" . $self->comment_encode($value) . "\n";
1131             #
1132             # To create syntactically correct Turtle, we need
1133             # to end a comment with a newline at the end; this
1134             # can, however, result in ugly Turtle, since the
1135             # ';' or '.' that ends an element will have to
1136             # follow on the next line after that, and the only
1137             # remedy is to peek ahead at the next element.
1138             }
1139             elsif (defined $name) { # no element if no name
1140 4         12 $self->{element_name} = $self->name_encode($name);
1141 4 100       13 $self->{elemnum} > 1 and $s .= ' ;';
1142 4         9 $s .= "\n" . $self->{turtle_indent};
1143 4         26 $s .= $self->{turtle_stream_prefix}
1144             . ":$self->{element_name} "
1145             . '"""'
1146             . $self->value_encode($value)
1147             . '"""';
1148             }
1149 0         0 $self->{outhandle} and
1150 4 50 50     48 return (print { $self->{outhandle} } $s)
1151             or
1152             return $s;
1153             }
1154              
1155             sub orec { # OM::Turtle
1156 2     2   5 my $self = shift;
1157 2         5 my ($recnum, $lineno) = (shift, shift);
1158 2         5 my ($s, $z) = ('', ''); # built string and catchup string
1159              
1160 2         13 $self->{elemnum} = 0;
1161 2 50       20 $self->{stream_is_open} or # call ostream() to open stream first
1162             ($z = $self->ostream(),
1163             $self->{stream_is_open} = 1);
1164 2 50       9 $self->{outhandle} or $s .= $z; # don't retain print status
1165 2         5 $self->{record_is_open} = 1;
1166              
1167 2 100 66     12 defined($recnum) and
1168             $self->{recnum} = $recnum
1169             or
1170             $self->{recnum}++;
1171              
1172 2 50       10 defined($lineno) or $lineno = '1:';
1173              
1174 2 50       8 $self->{verbose} and
1175             $s .= "# from record $recnum, line $lineno\n";
1176 2 50       10 defined($self->{subject}) or
1177             $self->{subject} = $self->{turtle_nosubject};
1178 2         16 $s .= "<$self->{subject}>";
1179              
1180 0         0 $self->{outhandle} and
1181 2 50 50     20 return (print { $self->{outhandle} } $s)
1182             or
1183             return $s;
1184             }
1185              
1186             sub crec { # OM::Turtle
1187 2     2   4 my ($self, $recnum) = (shift, shift);
1188 2         4 $self->{record_is_open} = 0;
1189 2         5 my $s = " .\n\n";
1190 0         0 $self->{outhandle} and
1191 2 50 50     12 return (print { $self->{outhandle} } $s)
1192             or
1193             return $s;
1194             }
1195              
1196             sub ostream { # OM::Turtle
1197 2     2   6 my $self = shift;
1198 2         4 my $s = '';;
1199              
1200 2         5 $self->{recnum} = 0;
1201 2         4 $self->{stream_is_open} = 1;
1202             # add the Turtle preamble
1203 2         10 $s .= "\@prefix $self->{turtle_stream_prefix}: <"
1204             . $self->{turtle_predns} . "> .\n";
1205 0         0 $self->{outhandle} and
1206 2 50 50     16 return (print { $self->{outhandle} } $s)
1207             or
1208             return $s;
1209             }
1210              
1211             sub cstream { # OM::Turtle
1212 2     2   4 my $self = shift;
1213 2         5 my ($s, $z) = ('', ''); # built string and catchup string
1214 2 50       13 $self->{record_is_open} and # wrap up any loose ends
1215             $z = $self->crec();
1216 2 50       8 $self->{outhandle} or $s .= $z; # don't retain print status
1217 2         11 $self->{stream_is_open} = 0;
1218 0         0 $self->{outhandle} and
1219 2 50 50     13 return (print { $self->{outhandle} } $s)
1220             or
1221             return $s;
1222             }
1223              
1224             sub name_encode { # OM::Turtle
1225 4     4   8 my ($self, $s) = (shift, shift);
1226 4 50       11 defined($s) or return '';
1227 4         9 $s =~ s/(["\\])/\\$1/g;
1228 4         11 return $s;
1229             # \" \\
1230             }
1231              
1232             sub value_encode { # OM::Turtle
1233 5     5   10 my ($self, $s) = (shift, shift);
1234 5 50       25 defined($s) or return '';
1235 5         18 $s =~ s/(["\\])/\\$1/g;
1236 5         16 return $s;
1237             }
1238              
1239             sub comment_encode { # OM::Turtle
1240 0     0   0 my ($self, $s) = (shift, shift);
1241 0 0       0 defined($s) or return '';
1242 0         0 $s =~ s/\n/\\n/g; # escape \n
1243 0         0 return $s;
1244             }
1245              
1246             package File::OM::XML;
1247              
1248             our @ISA = ('File::OM');
1249              
1250             sub elem { # OM::XML
1251 10     10   16 my $self = shift;
1252 10         19 my ($name, $value, $lineno, $elemnum) = (shift, shift, shift, shift);
1253 10         21 my ($s, $z) = ('', ''); # built string and catchup string
1254              
1255 10 100       37 $self->{record_is_open} or # call orec() to open record first
1256             ($z = $self->orec(undef, $lineno), # may call ostream()
1257             $self->{record_is_open} = 1);
1258 10 50       31 $self->{outhandle} or $s .= $z; # don't retain print status
1259              
1260 10 50 33     29 defined($elemnum) and
1261             $self->{elemnum} = $elemnum
1262             or
1263             $self->{elemnum}++;
1264              
1265             # Parse $lineno, which is empty or has form LinenumType, where
1266             # Type is either ':' (real element) or '#' (comment).
1267 10 100       22 defined($lineno) or $lineno = '1:';
1268 10         41 my ($num, $type) =
1269             $lineno =~ /^(\d*)\s*(.)/;
1270              
1271 10         17 local ($Text::Wrap::columns, $Text::Wrap::huge);
1272 10         11 my $wrapper;
1273 10 50 33     86 $self->{wrap} and
1274             ($wrapper, $Text::Wrap::columns, $Text::Wrap::huge) =
1275             (\&Text::Wrap::wrap, $self->{wrap}, 'overflow')
1276             or
1277             $wrapper = \&File::OM::text_nowrap;
1278             ;
1279              
1280              
1281 10 100       37 if ($type eq '#') {
    50          
1282             # xxx this should be stacked
1283 1         2 $self->{element_name} = undef; # indicates comment
1284 1         3 $self->{elemnum}--; # doesn't count as an element
1285              
1286 1         5 my $enc_com = $self->comment_encode($value); # encoded value
1287 1 50       10 $s .= $enc_com =~ /^\s*$/ ? # wrap() loses label of
1288             $self->{indent} . # a blank value so put
1289             "\n"; # close comment
1299             }
1300             elsif (defined $name) { # no element if no name
1301             # xxx we're saving this to no end; in full form
1302             # (open and close element) the element name would
1303             # be saved on a stack and the indent increased
1304             # across all outformat types.
1305             #
1306 9         24 $self->{element_name} = $self->name_encode($name);
1307 9         22 my $enc_val = $self->value_encode($value); # encoded value
1308 9 100       67 $s .= $enc_val =~ /^\s*$/ ? # wrap() loses label of
1309             $self->{indent} . # a blank value so put
1310             "<$self->{element_name}>" : # here instead
1311             &$wrapper( # wrap lines; this 1st
1312             $self->{indent} . # "indent" won't break
1313             "<$self->{element_name}>", # label
1314             $self->{indent}, # other line indents
1315             $enc_val) # main part to wrap
1316             ;
1317             #$s .= $self->{indent} . "<$self->{element_name}>"
1318             # . $self->value_encode($value);
1319             # M_ELEMENT and C_ELEMENT would start here
1320 9         1421 $s .= "{element_name}>\n";
1321             }
1322 0         0 $self->{outhandle} and
1323 10 50 50     88 return (print { $self->{outhandle} } $s)
1324             or
1325             return $s;
1326             }
1327              
1328             sub orec { # OM::XML
1329 6     6   10 my $self = shift;
1330 6         10 my ($recnum, $lineno) = (shift, shift);
1331 6         9 my ($s, $z) = ('', ''); # built string and catchup string
1332              
1333 6         10 $self->{elemnum} = 0;
1334 6 100       28 $self->{stream_is_open} or # call ostream() to open stream first
1335             ($z = $self->ostream(),
1336             $self->{stream_is_open} = 1);
1337 6 50       21 $self->{outhandle} or $s .= $z; # don't retain print status
1338 6         8 $self->{record_is_open} = 1;
1339              
1340 6 50 33     22 defined($recnum) and
1341             $self->{recnum} = $recnum
1342             or
1343             $self->{recnum}++;
1344              
1345 6 100       13 defined($lineno) or $lineno = '1:';
1346              
1347 6         21 $s .= $self->{indent} . # use indent and
1348             "<$self->{xml_record_name}>";
1349 6         25 $self->{indent} =~ s/$/$self->{indent_step}/; # increase indent
1350 6 100       23 $self->{verbose} and
1351             $s .= " ";
1352 6         8 $s .= "\n";
1353 0         0 $self->{outhandle} and
1354 6 50 50     33 return (print { $self->{outhandle} } $s)
1355             or
1356             return $s;
1357             }
1358              
1359             sub crec { # OM::XML
1360 5     5   12 my ($self, $recnum) = (shift, shift);
1361 5         9 $self->{record_is_open} = 0;
1362 5         45 $self->{indent} =~ s/$self->{indent_step}$//; # decrease indent
1363 5         18 my $s = $self->{indent} . # and use indent
1364             "{xml_record_name}>\n";
1365 0         0 $self->{outhandle} and
1366 5 50 50     32 return (print { $self->{outhandle} } $s)
1367             or
1368             return $s;
1369             }
1370              
1371             sub ostream { # OM::XML
1372 5     5   7 my $self = shift;
1373              
1374 5         16 $self->{recnum} = 0;
1375 5         7 $self->{stream_is_open} = 1;
1376 5         10 $self->{indent} = $self->{indent_start}; # current indent width
1377 5         29 $self->{indent} =~ s/$/$self->{indent_step}/; # increase indent
1378 5         13 my $s = "<$self->{xml_stream_name}>\n";
1379 0         0 $self->{outhandle} and
1380 5 50 50     26 return (print { $self->{outhandle} } $s)
1381             or
1382             return $s;
1383             }
1384              
1385             sub cstream { # OM::XML
1386 5     5   8 my $self = shift;
1387 5         12 my ($s, $z) = ('', ''); # built string and catchup string
1388 5 50       22 $self->{record_is_open} and # wrap up any loose ends
1389             $z = $self->crec();
1390 5 50       16 $self->{outhandle} or $s .= $z; # don't retain print status
1391 5         10 $self->{stream_is_open} = 0;
1392 5         31 $self->{indent} =~ s/$self->{indent_step}$//; # decrease indent
1393 5         13 $s .= "{xml_stream_name}>\n";
1394 0         0 $self->{outhandle} and
1395 5 50 50     27 return (print { $self->{outhandle} } $s)
1396             or
1397             return $s;
1398             }
1399              
1400             sub name_encode { # OM::XML
1401 20     20   34 my $self = shift;
1402 20         29 local $_ = shift(@_);
1403 20 50       51 defined($_) or $_ = '';
1404              
1405 20         35 s/&/&/g;
1406 20         29 s/'/'/g;
1407 20         26 s/
1408 20         25 s/>/>/g;
1409 20         24 s/\\/\\\\/g;
1410 20         25 s/"/\\"/g;
1411              
1412 20         53 return $_;
1413              
1414             # ' & < > (unparsed \" \\ )
1415             # XXXX CDATA sections begin with the string "
1416             # and end with the string " ]]> "
1417             }
1418              
1419             sub value_encode { # OM::XML
1420 10     10   13 my $self = shift;
1421 10         21 return $self->name_encode(@_);
1422             }
1423              
1424             sub comment_encode { # OM::XML
1425 2     2   6 my ($self, $s) = (shift, shift);
1426 2 50       9 defined($s) or return '';
1427 2         7 $s =~ s/-->/-->/g;
1428 2         8 return $s;
1429             }
1430              
1431             1;
1432              
1433             __END__