package XML::DOM::Lite::Node; use Scalar::Util qw(weaken); use XML::DOM::Lite::NodeList; use XML::DOM::Lite::Constants qw(:all); sub new { my ($class, $proto) = @_; unless (UNIVERSAL::isa($proto->{childNodes}, 'XML::DOM::Lite::NodeList')) { $proto->{childNodes} = XML::DOM::Lite::NodeList->new( $proto->{childNodes} || [ ] ); } $proto->{attributes} = XML::DOM::Lite::NodeList->new([ ]) unless defined $proto->{attributes}; my $self = bless $proto, $class; return $self; } sub childNodes { my $self = shift; $self->{childNodes} = shift if @_; return $self->{childNodes}; } sub parentNode { my $self = shift; if (@_) { weaken($self->{parentNode} = shift()); } else { return $self->{parentNode}; } } sub documentElement { weaken($_[0]->{documentElement} = $_[1]) if $_[1]; $_[0]->{documentElement}; } sub nodeType { my $self = shift; $self->{nodeType} = shift if @_; $self->{nodeType}; } sub nodeName { my $self = shift; $self->{nodeName} = shift if @_; $self->{nodeName}; } sub tagName { my $self = shift; $self->{tagName} = shift if @_; $self->{tagName}; } sub appendChild { my ($self, $node) = @_; if ($node->{parentNode}) { $node->{parentNode}->removeChild($node); } unless ($node->nodeType == DOCUMENT_FRAGMENT_NODE) { $node->{parentNode} = $self; $self->{childNodes}->insertNode($node); } else { while ($node->childNodes->length) { $self->appendChild($node->firstChild); } } return $node; } sub previousSibling { my $self = shift; if ($self->parentNode) { my $index = $self->parentNode->childNodes->nodeIndex($self); return undef if $index == 0; return $self->parentNode->childNodes->[$index - 1]; } } sub nextSibling { my $self = shift; if ($self->parentNode) { my $index = $self->parentNode->childNodes->nodeIndex($self); return undef if $index == @{$self->childNodes->length} - 1; return $self->parentNode->childNodes->[$index + 1]; } } sub removeChild { my ($self, $node) = @_; if ($node->parentNode == $self) { undef($node->{parentNode}); return $self->childNodes->removeNode($node); } else { die "$node is not a child of $self"; } } sub insertBefore { my ($self, $node, $refNode) = @_; die "usage error" unless (scalar(@_) == 3); if ($node->parentNode) { $node->parentNode->removeChild($node); } if ($node->nodeType == DOCUMENT_FRAGMENT_NODE) { foreach my $c (@{$node->childNodes}) { $self->insertBefore($c, $refNode); } return; } $node->parentNode($self); my $index = $self->childNodes->nodeIndex($refNode); if (defined $index) { if ($index <= 0) { $self->childNodes->insertNode($node, 0); } else { $self->childNodes->insertNode($node, $index); } } else { die "$refNode is not a child of $self"; } } sub replaceChild { my ($self, $node, $refNode) = @_; die "usage error" unless (scalar(@_) == 3); $self->insertBefore($refNode, $node); $self->removeChild($refNode); } sub nodeValue { my $self = shift; $self->{nodeValue} = shift if @_; $self->{nodeValue}; } sub attributes { my $self = shift; $self->{attributes} = shift if @_; return $self->{attributes}; } sub getAttribute { my ($self, $attname) = @_; for (my $x = 0; $x < $self->{attributes}->length; $x++) { return $self->{attributes}->[$x]->{nodeValue} if ($self->{attributes}->[$x]->{nodeName} eq $attname); } return undef; } sub setAttribute { my ($self, $attname, $value) = @_; for (my $x = 0; $x < $self->{attributes}->length; $x++) { if ($self->{attributes}->[$x]->{nodeName} eq $attname) { $self->{attributes}->[$x]->{nodeValue} = $value; return $value; } } push @{$self->{attributes}}, XML::DOM::Lite::Node->new({ nodeType => ATTRIBUTE_NODE, nodeName => $attname, nodeValue => $value }); return $value; } sub firstChild { my ($self) = @_; return $self->childNodes->item(0); } sub lastChild { my ($self) = @_; return $self->childNodes->[$#{$self->childNodes}]; } sub ownerDocument { my $self = shift; weaken($self->{ownerDocument} = shift) if @_; $self->{ownerDocument}; } sub getElementsByTagName { my ($self, $tag_name) = @_; my $nlist = XML::DOM::Lite::NodeList->new([ ]); my @stack = @{ $self->childNodes }; while (my $n = shift(@stack)) { if ($n->nodeType == ELEMENT_NODE) { if ($n->tagName eq $tag_name) { $nlist->insertNode($n); } push @stack, @{ $n->childNodes }; } } return $nlist; } sub cloneNode { my ($self, $deep) = @_; my $copy = { }; @copy{keys %$self} = values %$self; $copy->{childNodes} = XML::DOM::Lite::NodeList->new([ ]); $copy->{attributes} = XML::DOM::Lite::NodeList->new([@{$self->attributes}]); $copy->{tagName} = $self->tagName; $copy->{nodeName} = $self->nodeName; $copy->{nodeType} = $self->nodeType; $copy->{ownerDocument} = $self->ownerDocument; bless $copy, ref($self); if ($deep) { foreach (@{$self->childNodes}) { $copy->childNodes->insertNode($_->cloneNode($deep)); } } return $copy; } sub xml { my $self = shift; require XML::DOM::Lite::Serializer; my $serializer = XML::DOM::Lite::Serializer->new(); return $serializer->serializeToString( $self ); } 1;