File Coverage

blib/lib/XML/TiePYX.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package XML::TiePYX;
2 1     1   772 use strict;
  1         1  
  1         38  
3 1     1   6 use vars qw($VERSION);
  1         2  
  1         48  
4 1     1   5 use Carp;
  1         3  
  1         99  
5 1     1   774 use IO::File;
  1         9832  
  1         124  
6 1     1   1709 use XML::Parser;
  0            
  0            
7            
8             use base 'Tie::Handle';
9            
10             $VERSION = '0.05';
11            
12             sub TIEHANDLE {
13             my $class=shift;
14             my $source=shift;
15             my %args=(Condense=>1,Validating=>0,Latin=>0,Catalog=>0,@_);
16             my $self={output=>[],EOF=>0,offset=>0,in_tag=>0};
17             my $parser;
18             $self->{condense}=delete $args{Condense};
19             $self->{latin}=delete $args{Latin};
20             my $catname=delete $args{Catalog};
21             if ($args{Validating}) {
22             require XML::Checker::Parser;
23             $parser=XML::Checker::Parser->new(%args) or croak "$!";
24             } else {
25             $parser=XML::Parser->new(%args) or croak "$!";
26             }
27             $parser->setHandlers(Start=>\&start,End=>\&end,Char=>\&char,Proc=>\&proc);
28             if ($catname) {
29             require XML::Catalog;
30             my $catalog=XML::Catalog->new($catname) or croak "$!";
31             $parser->setHandlers(ExternEnt=>$catalog->get_handler($parser));
32             }
33             $self->{parser}=$parser->parse_start(TiePYX=>$self) or croak "$!";
34             if (ref($source) eq 'SCALAR') {
35             $self->{src}=$source;
36             $self->{src_offset}=0;
37             }
38             elsif (ref($source)=~/^IO:|^GLOB$/) {
39             $self->{srcfile}=$source;
40             }
41             else {
42             $self->{srcfile}=IO::File->new($source) or return undef;
43             $self->{opened}=1;
44             }
45             bless $self,$class;
46             }
47            
48             sub CLOSE {
49             my $self=shift;
50             $self->{srcfile}->close() if $self->{opened};
51             $self->{parser}=undef;
52             }
53            
54             sub DESTROY {
55             my $self=shift;
56             $self->{parser}=undef;
57             }
58            
59             sub READLINE {
60             my $self=shift;
61             $self->parsechunks(wantarray());
62             if (wantarray()) {
63             if (@{$self->{output}}) {
64             $self->{output}[0]=substr($self->{output}[0],$self->{offset});
65             return @{$self->{output}};
66             }
67             else {
68             return undef;
69             }
70             }
71             my $buf=substr((shift @{$self->{output}}) || '',$self->{offset});
72             $self->{offset}=0;
73             return $buf||undef;
74             }
75            
76             sub READ {
77             my ($self,$data,$length,$offset)=@_;
78             my $buf;
79             while (length($buf)<$length && !$self->{EOF}) {
80             $self->parsechunks(0);
81             my $t=$self->{output}[0] or last;
82             my $t1=$length-length($buf);
83             if ($t1>=length(substr($t,$self->{offset}))) {
84             $buf.=$t;
85             shift @{$self->{output}};
86             $self->{offset}=0;
87             }
88             else {
89             $buf.=substr($t,$self->{offset},$t1);
90             $self->{offset}+=$t1;
91             }
92             }
93             substr($_[1],$offset||0,$length)=$buf;
94             return length($buf);
95             }
96            
97             sub PRINTF {
98             my $self=shift;
99             my $fmt=shift;
100             my $buf=sprintf($fmt,@_);
101             $self->WRITE($buf,length($buf),0);
102             }
103            
104             sub WRITE {
105             my ($self,$data)=@_;
106             if ($self->{latin} && $self->{offset}==0) {
107             $self->output(qq{\n});
108             }
109             ++$self->{offset};
110             foreach my $line (split /\n/,$data) {
111             my $type=substr($line,0,1);
112             $line=substr($line,1);
113             if ($type eq 'A') {
114             my ($attr,$val)=split(/\s+/,$line,2);
115             my $quote='"';
116             $self->output(" $attr=",0);
117             if ($val=~tr/"//) {
118             $quote="'";
119             $val=~s/'/'/g if $val=~tr/'//;
120             }
121             $self->output("${quote}$val$quote",1);
122             next;
123             }
124             if ($self->{in_tag}) {
125             $self->output('>',0);
126             $self->{in_tag}=0;
127             }
128             if ($type eq '(') {
129             $self->output("<$line",0);
130             $self->{in_tag}=1;
131             }
132             elsif ($type eq ')') {
133             $self->output("",0);
134             }
135             elsif ($type eq '-') {
136             $self->output($line,1);
137             }
138             elsif ($type eq '?') {
139             $self->output('
140             $self->output($line,1);
141             $self->output('?>',0);
142             }
143             }
144             }
145            
146             sub output {
147             my ($self,$line,$encode)=@_;
148             my %enc=('&'=>'&','<'=>'<','>'=>'>');
149             my $enc=join '',keys %enc;
150             $line=~s/\\n/\n/g;
151             $line=~s/([$enc])(?!apos)/$enc{$1}/go if $encode;
152             if (exists $self->{src}) {
153             ${$self->{src}}.=$line;
154             }
155             else {
156             $self->{srcfile}->print($line);
157             }
158             }
159            
160             sub parsechunks {
161             my ($self,$goforit)=@_;
162             my $buf;
163             while ((!@{$self->{output}}
164             || (substr($self->{output}[-1],0,1) eq '-' && $self->{condense})
165             || $goforit)
166             && !$self->{EOF}) {
167             if (exists $self->{src}) {
168             $buf=substr(${$self->{src}},$self->{src_offset},4096);
169             $self->{src_offset}+=4096;
170             }
171             else {
172             read($self->{srcfile},$buf,4096);
173             }
174             if (length($buf)==0) {
175             $self->{EOF}=1;
176             $self->{parser}->parse_done();
177             }
178             else {
179             $self->{parser}->parse_more($buf);
180             }
181             }
182             }
183            
184             sub start {
185             my ($parser,$element,@attrs)=@_;
186             my $self=$parser->{TiePYX};
187             if (@{$self->{output}} && substr($self->{output}[-1],0,1) eq '-' && $self->{condense}) {
188             $self->{output}[-1].="\n";
189             }
190             push @{$self->{output}},'('.$self->nsname($element)."\n";
191             while (@attrs) {
192             my ($name,$val)=(shift @attrs,shift @attrs);
193             push @{$self->{output}},'A'.$self->nsname($name).' '.$self->encode($val)."\n";
194             }
195             }
196            
197             sub end {
198             my ($parser,$element)=@_;
199             my $self=$parser->{TiePYX};
200             if (@{$self->{output}} && substr($self->{output}[-1],0,1) eq '-' && $self->{condense}) {
201             $self->{output}[-1].="\n";
202             }
203             push @{$self->{output}},')'.$self->nsname($element)."\n";
204             }
205            
206             sub char {
207             my ($parser,$text)=@_;
208             my $self=$parser->{TiePYX};
209             if (@{$self->{output}} && substr($self->{output}[-1],0,1) eq '-' && $self->{condense}) {
210             $self->{output}[-1].=$self->encode($text);
211             }
212             else {
213             push @{$self->{output}},'-'.$self->encode($text).($self->{condense}?'':"\n");
214             }
215             }
216            
217             sub proc {
218             my ($parser,$target,$value)=@_;
219             my $self=$parser->{TiePYX};
220             if (@{$self->{output}} && substr($self->{output}[-1],0,1) eq '-' && $self->{condense}) {
221             $self->{output}[-1].="\n";
222             }
223             push @{$self->{output}},"?$target ".$self->encode($value)."\n";
224             }
225            
226             sub nsname {
227             my ($self,$name)=@_;
228             my $parser=$self->{parser};
229             if ($parser->{Namespaces}) {
230             my $ns=$parser->namespace($name)||'';
231             $name="{$ns}".$name;
232             }
233             return $self->encode($name);
234             }
235            
236             sub encode {
237             my ($self,$text)=@_;
238             $text=~s/\x0a/\\n/g;
239             if ($self->{latin}) {
240             $text=~s{([\xc0-\xc3])(.)}{
241             my $hi = ord($1);
242             my $lo = ord($2);
243             chr((($hi & 0x03) <<6) | ($lo & 0x3F))
244             }ge;
245             }
246             return $text;
247             }
248            
249             1;
250             __END__