File Coverage

NewsML/Node.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             # $Id: Node.pm,v 0.1 2002/02/13 14:11:43 brendan Exp brendan $
2             # Syndication::NewsML::Node.pm
3              
4             $VERSION = sprintf("%d.%02d", q$Revision: 0.1 $ =~ /(\d+)\.(\d+)/);
5             $VERSION_DATE= sprintf("%s", q$Date: 2002/02/13 14:11:43 $ =~ m# (.*) $# );
6              
7             $DEBUG = 1;
8              
9             #
10             # Syndication::NewsML::Node -- superclass defining a few functions all these will need
11             #
12             package Syndication::NewsML::Node;
13 5     5   27 use Carp;
  5         9  
  5         385  
14 5     5   18202 use XML::DOM;
  0            
  0            
15             @ISA = qw( XML::DOM::Node );
16              
17             sub new {
18             my ($class, $node) = @_;
19             my $self = bless {}, $class;
20              
21             use constant REQUIRED => 1;
22             use constant IMPLIED => 2;
23             use constant OPTIONAL => 3;
24             use constant ZEROORMORE => 4;
25             use constant ONEORMORE => 5;
26              
27             $self->{node} = $node;
28             $self->{text} = undef;
29             $self->{_tagname} = undef;
30              
31             # child elements we may want to access
32             $self->{_singleElements} = {};
33             $self->{_multiElements} = {};
34             $self->{_attributes} = {};
35             $self->{_hasText} = 0;
36              
37             $self->_init($node); # init will vary for different subclasses
38              
39             # call _init of ALL parent classes as well
40             # thanks to Duncan Cameron for suggesting how to get this to work!
41             $_->($self, $node) for ( map {$_->can("_init")||()} @{"${class}::ISA"} );
42              
43             return $self;
44             }
45              
46             sub _init { } # undef init, subclasses may want to use it
47              
48             # get the contents of an element as as XML string (wrapper around XML::DOM::Node::toString)
49             # this *includes* the container tag of the current element.
50             sub getXML {
51             my ($self) = @_;
52             $self->{xml} = $self->{node}->toString;
53             }
54              
55             # getChildXML is the same as the above but doesn't include the container tag.
56             sub getChildXML {
57             my ($self) = @_;
58             my $xmlstring = "";
59             for my $child ($self->{node}->getChildNodes()) {
60             $xmlstring .= $child->toString();
61             }
62             $self->{xml} = $xmlstring;
63             }
64              
65             # get the text of the element, if any
66             # now includes get text of all children, including elements, recursively!
67             sub getText {
68             my ($self, $stripwhitespace) = @_;
69             croak "Can't use getText on this element" unless $self->{_hasText};
70             $self->{text} = "";
71             $self->{text} = getTextRecursive($self->{node}, $stripwhitespace);
72             }
73              
74             # special "cheat" method to get ALL text in ALL child elements, ignoring any markup tags.
75             # can use on any element, anywhere (if there's no text, it will just return an empty string
76             # or all whitespace)
77             sub getAllText {
78             my ($self, $stripwhitespace) = @_;
79             $self->{text} = "";
80             $self->{text} = getTextRecursive($self->{node}, $stripwhitespace);
81             }
82              
83             sub getTextRecursive {
84             my ($node, $stripwhitespace) = @_;
85             my $textstring = "";
86             for my $child ($node->getChildNodes()) {
87             if ( $child->getNodeType == XML::DOM::ELEMENT_NODE ) {
88             $textstring .= getTextRecursive($child, $stripwhitespace);
89             } else {
90             my $tmpstring = $child->getData();
91             if ($stripwhitespace && ($stripwhitespace eq "strip")) {
92             $tmpstring =~ s/^\s+/ /; #replace with single space -- is this ok?
93             $tmpstring =~ s/\s+$/ /; #replace with single space -- is this ok?
94             }
95             $textstring .= $tmpstring;
96             }
97             }
98             $textstring =~ s/\s+/ /g if $stripwhitespace; #replace with single space -- is this ok?
99             return $textstring;
100             }
101              
102             # get the tag name of this element
103             sub getTagName {
104             my ($self) = @_;
105             $self->{_tagname} = $self->{node}->getTagName;
106             }
107              
108             # get the path up to and including this element
109             sub getPath {
110             my ($self) = @_;
111             $self->getParentPath($self->{node});
112             }
113              
114             # get the path of this node including all parent nodes (called by getPath)
115             sub getParentPath {
116             my ($self, $parent) = @_;
117             # have to look two levels up because XML::DOM treats "#document" as a level in the tree
118             return $parent->getNodeName if !defined($parent->getParentNode->getParentNode);
119             return $self->getParentPath($parent->getParentNode) . "->" . $parent->getNodeName;
120             }
121              
122             use vars '$AUTOLOAD';
123              
124             # Generic routine to extract child elements from node.
125             # handles "getParamaterName", "getParameterNameList" and "getParameterNameCount"
126             sub AUTOLOAD {
127             my ($self) = @_;
128              
129             if ($AUTOLOAD =~ /DESTROY$/) {
130             return;
131             }
132              
133             # extract attribute name
134             $AUTOLOAD =~ /.*::get(\w+)/
135             or croak "No such method: $AUTOLOAD";
136              
137             print "AUTOLOAD: method is $AUTOLOAD\n" if $DEBUG;
138             my $call = $1;
139             if ($call =~ /(\w+)Count$/) {
140              
141             # handle getXXXCount method
142             $var = $1;
143             if (!$self->{_multiElements}->{$var}) {
144             croak "Can't use getCount on $var";
145             }
146             my $method = "get".$var."List";
147             $self->$method unless defined($self->{$var."Count"});
148             return $self->{$var."Count"};
149             } elsif ($call =~ /(\w+)List$/) {
150              
151             # handle getXXXList method for multi-element tags
152             my $elem = $1;
153              
154             if (!$self->{_multiElements}->{$elem}) {
155             croak "No such method: $AUTOLOAD";
156             }
157              
158             # return undef if self->node doesn't exist
159             return undef unless defined($self->{node});
160              
161             my $list = $self->{node}->getElementsByTagName($elem, 0);
162             if (!$list && $self->{_multiElements}->{$elem} eq ONEORMORE) {
163             croak "Error: required element $elem is missing";
164             }
165             # set elemCount while we know what it is
166             $self->{$elem."Count"} = $list->getLength;
167             my @elementObjects;
168             my $elementObject;
169             for (my $i = 0; $i < $self->{$elem."Count"}; $i++) {
170             $elementObject = "Syndication::NewsML::$elem"->new($list->item($i))
171             if defined($list->item($i)); # if item is undef, push an undef to the array
172             push(@elementObjects, $elementObject);
173             }
174             $self->{$elem} = \@elementObjects;
175             return wantarray ? @elementObjects : $self->{$elem};
176             } elsif ($self->{_singleElements}->{$call}) {
177             # return undef if self->node doesn't exist
178             return undef unless defined($self->{node});
179              
180             # handle getXXX method for single-element tags
181             my $element = $self->{node}->getElementsByTagName($call, 0);
182             if (!$element) {
183             if ($self->{_singleElements}->{$call} eq REQUIRED) {
184             croak "Error: required element $call is missing";
185             } else {
186             return undef;
187             }
188             }
189             $self->{$call} = "Syndication::NewsML::$call"->new($element->item(0))
190             if defined($element->item(0));
191             return $self->{$call};
192             } elsif ($self->{_attributes}->{$call}) {
193             # return undef if self->node doesn't exist
194             return undef unless defined($self->{node});
195             my $attr = $self->{node}->getAttributeNode($call);
196             $self->{$call} = $attr ? $attr->getValue : '';
197             if (!$attr && $self->{_attributes}->{$call} eq REQUIRED) {
198             croak "Error: $call attribute is required";
199             }
200             return $self->{$call};
201             } elsif ($self->{_multiElements}->{$call}) {
202             # flag error because multiElement needs to be called with "getBlahList"
203             croak "$call can be a multi-element field: must call get".$call."List";
204             } else {
205             croak "No such method: $AUTOLOAD";
206             }
207             }
208              
209             1;