File Coverage

lib/Kite/XML/Node.pm
Criterion Covered Total %
statement 103 133 77.4
branch 33 64 51.5
condition 12 18 66.6
subroutine 16 17 94.1
pod 1 5 20.0
total 165 237 69.6


line stmt bran cond sub pod time code
1             #============================================================= -*-perl-*-
2             #
3             # Kite::XML::Node
4             #
5             # DESCRIPTION
6             # Base class for XML node modules which are constructed automatically
7             # by the Kite::XML::Parser. These represent the XML elements.
8             #
9             # AUTHOR
10             # Andy Wardley
11             #
12             # COPYRIGHT
13             # Copyright (C) 2000 Andy Wardley. All Rights Reserved.
14             #
15             # This module is free software; you can redistribute it and/or
16             # modify it under the same terms as Perl itself.
17             #
18             # VERSION
19             # $Id: Node.pm,v 1.1 2000/10/17 11:58:16 abw Exp $
20             #
21             #========================================================================
22            
23             package Kite::XML::Node;
24              
25             require 5.004;
26              
27 3     3   2106 use strict;
  3         4  
  3         86  
28 3     3   1221 use Kite::Base;
  3         8  
  3         97  
29 3     3   15 use base qw( Kite::Base );
  3         6  
  3         388  
30 3     3   15 use vars qw( $VERSION $AUTOLOAD );
  3         6  
  3         529  
31              
32             $VERSION = sprintf("%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/);
33              
34             # create some aliases for method names
35             *attribute = \&attr;
36             *element = \&elem;
37             *content = \&char;
38              
39              
40             #------------------------------------------------------------------------
41             # init(\%config)
42             #
43             # Initialisation method called by the base class constructor, new().
44             # Copies attributes pass in the $config hash reference into the $self
45             # object, checking that all mandatory attributes are specified.
46             #------------------------------------------------------------------------
47              
48             sub init {
49 15     15 1 25 my ($self, $config) = @_;
50 15         26 my $class = ref $self;
51 15         24 my ($attribs, $elems, $default, $key, $val, $mult);
52              
53             {
54 3     3   29 no strict qw( refs );
  3         8  
  3         1329  
  15         61  
55 15   50     18 $attribs = ${"$class\::ATTRIBUTES"} || { };
56 15   100     20 $elems = ${"$class\::ELEMENTS"} || { };
57             }
58              
59             # ugy hack: we must call keys() to reset the iterators on the hashes
60 15         66 my @dud = (keys(%$attribs), keys(%$elems));
61              
62             # set attributes from the $config hash, where specified in $ATTRIBUTES
63 15         57 while (($key, $val) = each %$attribs) {
64 28 50       73 if ($key =~ /^_/) {
65             # just set default for private keys with leading _UNDERSCORE
66 0 0       0 $self->{ $key } = ref $val eq 'CODE' ? &$val : $val;
67             }
68             else {
69 28 100       79 if (defined $config->{ $key }) {
    100          
70 17         56 $self->{ $key } = $config->{ $key };
71             }
72             elsif (defined $val) {
73 8         36 $self->{ $key } = $val;
74             }
75             else {
76 3         30 return $self->error("$key not defined");
77             }
78             }
79 25         81 delete $config->{ $key };
80             }
81              
82             # set elements from the $config hash, or initialise
83 12         40 while (($key, $val) = each %$elems) {
84             # value can be an array ref containing [ $pkg, $module ]
85 15 100       41 $val = $val->[0]
86             if ref $val eq 'ARRAY';
87              
88             # look for, and strip trailing '+' on package name, then create
89             # array reference for elements with multiplicity
90 15         48 $mult = ($val =~ s/\+$//);
91 15 100       39 $self->{ $key } = []
92             if $mult;
93              
94             # copy any config value(s) into the elements
95 15 50       37 if (defined ($val = $config->{ $key })) {
96 0 0       0 if ($mult) {
97 0 0       0 push(@{ $self->{ $key } }, ref $val eq 'ARRAY' ? @$val : $val);
  0         0  
98             }
99             else {
100 0         0 $self->{ $key } = $val;
101             }
102             }
103 15         45 delete $config->{ $key };
104             }
105              
106             # any items remaining in $config are invalid
107 12         36 foreach $key (keys %$config) {
108 1         11 return $self->error("invalid attribute '$key'");
109             }
110              
111 11         76 return $self;
112             }
113              
114              
115             #------------------------------------------------------------------------
116             # attr($name)
117             # attr($name, $value)
118             #
119             # Accessor method to retrieve or update element attributes.
120             #------------------------------------------------------------------------
121              
122             sub attr {
123 19     19 0 34 my $self = shift;
124 19         24 my $attr = shift;
125 19         30 my $class = ref $self;
126              
127 3     3   16 no strict qw( refs );
  3         4  
  3         438  
128 19   50     22 my $attribs = ${"$class\::ATTRIBUTES"} || { };
129              
130             # set new or return existing value for valid PARAMS
131 19 50       39 if (exists $attribs->{ $attr }) {
132 19 100       34 if (@_) {
133 1         6 return ($self->{ $attr } = shift);
134             }
135             else {
136 18         96 return $self->{ $attr };
137             }
138             }
139             else {
140 0         0 return $self->error("no such attribute '$attr'");
141             }
142             }
143              
144              
145             #------------------------------------------------------------------------
146             # char()
147             # char($text)
148             #
149             # Returns the internal CDATA member when called without any arguments,
150             # which contains the current character content for the node. When called
151             # with an argument, the passed value will be appended to the CDATA member.
152             # The CDATA item must be defined in the $ELEMENTS hash reference in the
153             # subclass's package for character content to be accepted. A call to
154             # char() for a node that don't accept CDATA will be considered an error
155             # and will set the internal ERROR variable and return undef. There is
156             # one caveat to this rule: any node that *doesn't* define CDATA will
157             # accept and silently ignore any $text that contains only whitespace.
158             # This is required to prevent XML nodes that shouldn't define content,
159             # but do contain whitespace, from raising errors.
160             #------------------------------------------------------------------------
161              
162             sub char {
163 3     3 0 25 my ($self, $text) = @_;
164 3         4 my $class = ref $self;
165              
166 3     3   15 no strict qw( refs );
  3         5  
  3         634  
167 3   50     4 my $elems = ${"$class\::ELEMENTS"} || { };
168              
169 3 50       9 if ($elems->{ CDATA }) {
    0          
170 3 100       9 $self->{ CDATA } = '' unless defined $self->{ CDATA };
171 3 100       9 $self->{ CDATA } .= $text if defined $text;
172 3         12 return $self->{ CDATA };
173             }
174             elsif(defined $text) {
175             # complain about character data unless it's just white noise
176 0 0       0 return $self->error("invalid character data")
177             unless $text =~ /^\s*$/;
178 0         0 return 1;
179             }
180             else {
181 0         0 return $self->error("no character data");
182             }
183             }
184              
185              
186             #------------------------------------------------------------------------
187             # elem($name)
188             # elem($name, @args)
189             #
190             # Accessor method to retrieve the element specified by parameter. If
191             # additional arguments are provided then the call is assumed to be a
192             # construction request and is delegated to child($name, @args).
193             #------------------------------------------------------------------------
194              
195             sub elem {
196 10     10 0 13 my $self = shift;
197 10         13 my $elem = shift;
198 10         20 my $class = ref $self;
199              
200             # delegate to child() if additional arguments specified!
201 10 100       69 return $self->child($elem, @_)
202             if @_;
203              
204 3     3   15 no strict qw( refs );
  3         5  
  3         496  
205 6   50     10 my $elems = ${"$class\::ELEMENTS"} || { };
206              
207             # set new or return existing value for valid PARAMS
208 6 50       16 if (exists $elems->{ $elem }) {
209 6         40 return $self->{ $elem };
210             }
211             else {
212 0         0 return $self->error("no such element '$elem'");
213             }
214             }
215            
216              
217             #------------------------------------------------------------------------
218             # child($element, @args)
219             #
220             # Creates a new child element of type denoted by the first parameter.
221             # Examines the $ELEMENTS hash reference in the object's package for
222             # a key matching $element and uses the relevant value as a package
223             # name against which the new() constructor can be called, passing
224             # any additional arguments specified. The package name may be suffixed
225             # by a '+' to indicate that multiple child elements are permitted.
226             #------------------------------------------------------------------------
227              
228             sub child {
229 9     9 0 10 my $self = shift;
230 9         13 my $class = ref $self;
231 9         15 my $elem = shift;
232 9         19 my ($pkg, $mod, $mult) = (0) x 3;
233              
234 3     3   13 no strict qw( refs );
  3         12  
  3         1173  
235 9   50     10 my $elems = ${"$class\::ELEMENTS"} || { };
236              
237 9 50       26 if (defined($pkg = $elems->{ $elem })) {
238             # value can be an array ref containing [ $pkg, $module ]
239 9 50       22 ($pkg, $mod) = @$pkg
240             if ref $pkg eq 'ARRAY';
241              
242             # look for, and strip trailing '+' on package name
243 9         36 $mult = ($pkg =~ s/\+$//);
244              
245             # use package name to define module name if $mod set to 1
246 9 50       23 if ($mod eq '1') {
247 0         0 $mod = $pkg;
248 0         0 $mod =~ s/::/\//g;
249 0         0 $mod .= '.pm';
250             }
251              
252 9 50       19 require $mod if $mod;
253              
254 9   100     59 my $node = $pkg->new(@_)
255             || return $self->error($pkg->error());
256              
257 7 100       17 if ($mult) {
258 6         8 push(@{ $self->{ $elem } }, $node);
  6         15  
259             }
260             else {
261 1 50       4 return $self->error("$elem already defined")
262             if defined $self->{ $elem };
263 1         2 $self->{ $elem } = $node;
264             }
265 7         62 return $node;
266             }
267             else {
268 0         0 return $self->error("invalid element '$elem'");
269             }
270             }
271              
272              
273             #------------------------------------------------------------------------
274             # AUTOLOAD
275             #
276             # Autoload method.
277             #------------------------------------------------------------------------
278              
279             sub AUTOLOAD {
280 24     24   188 my $self = shift;
281 24         44 my $class = ref $self;
282 24         32 my $method = $AUTOLOAD;
283            
284 24         96 $method =~ s/.*:://;
285 24 50       60 return if $method eq 'DESTROY';
286              
287 3     3   15 no strict qw( refs );
  3         4  
  3         2471  
288 24   50     26 my $attribs = ${"$class\::ATTRIBUTES"} || { };
289 24   100     32 my $elems = ${"$class\::ELEMENTS"} || { };
290            
291 24 50       66 if ($method =~ /^_/) {
292 0         0 my ($pkg, $file, $line) = caller();
293 0         0 die "attempt to access private member $method at $file line $line\n";
294             }
295              
296 24 100       67 if (exists $attribs->{ $method }) {
    50          
297 17         59 return $self->attr($method, @_);
298             }
299             elsif (exists $elems->{ $method }) {
300 7         28 return $self->elem($method, @_);
301             }
302             else {
303 0           return $self->error("no such attribute '$method'");
304             }
305             }
306              
307              
308             #------------------------------------------------------------------------
309             # _dump()
310             #
311             # Debug method to return a formatted string containing the object data.
312             #------------------------------------------------------------------------
313              
314             sub _dump {
315 0     0     my $self = shift;
316 0           my $text = "$self:\n";
317 0           local $" = ', ';
318 0           while (my ($key, $value) = each %$self) {
319 0           my $v;
320 0 0         $value = '' unless defined $value;
321 0 0         $value = [ map { $v = $value->{ $_ };
  0            
322 0 0         $v = '' unless defined $v;
323 0           "$_ => $v" } keys %$value ]
324             if ref $value eq 'HASH';
325 0 0         $value = "[ @$value ]"
326             if ref $value eq 'ARRAY';
327 0           $text .= sprintf(" %-12s => $value\n", $key);
328             }
329 0           return $text;
330             }
331              
332              
333             1;
334              
335             __END__