###################################################################### package XML::DOM::NamedNodeMap; ###################################################################### use strict; use Carp; use XML::DOM::DOMException; use XML::DOM::NodeList; use vars qw( $Special ); # Constant definition: # Note: a real Name should have at least 1 char, so nobody else should use this $Special = ""; sub new { my ($class, %args) = @_; $args{Values} = new XML::DOM::NodeList; # Store all NamedNodeMap properties in element $Special bless { $Special => \%args}, $class; } sub getNamedItem { # Don't return the $Special item! ($_[1] eq $Special) ? undef : $_[0]->{$_[1]}; } sub setNamedItem { my ($self, $node) = @_; my $prop = $self->{$Special}; my $name = $node->getNodeName; if ($XML::DOM::SafeMode) { croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR) if $self->isReadOnly; croak new XML::DOM::DOMException (WRONG_DOCUMENT_ERR) if $node->[XML::DOM::Node::_Doc] != $prop->{Doc}; croak new XML::DOM::DOMException (INUSE_ATTRIBUTE_ERR) if defined ($node->[XML::DOM::Node::_UsedIn]); croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR, "can't add name with NodeName [$name] to NamedNodeMap") if $name eq $Special; } my $values = $prop->{Values}; my $index = -1; my $prev = $self->{$name}; if (defined $prev) { # decouple previous node $prev->decoupleUsedIn; # find index of $prev $index = 0; for my $val (@{$values}) { last if ($val == $prev); $index++; } } $self->{$name} = $node; $node->[XML::DOM::Node::_UsedIn] = $self; if ($index == -1) { push (@{$values}, $node); } else # replace previous node with new node { splice (@{$values}, $index, 1, $node); } $prev; } sub removeNamedItem { my ($self, $name) = @_; # Be careful that user doesn't delete $Special node! croak new XML::DOM::DOMException (NOT_FOUND_ERR) if $name eq $Special; my $node = $self->{$name}; croak new XML::DOM::DOMException (NOT_FOUND_ERR) unless defined $node; # The DOM Spec doesn't mention this Exception - I think it's an oversight croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR) if $self->isReadOnly; $node->decoupleUsedIn; delete $self->{$name}; # remove node from Values list my $values = $self->getValues; my $index = 0; for my $val (@{$values}) { if ($val == $node) { splice (@{$values}, $index, 1, ()); last; } $index++; } $node; } # The following 2 are really bogus. DOM should use an iterator instead (Clark) sub item { my ($self, $item) = @_; $self->{$Special}->{Values}->[$item]; } sub getLength { my ($self) = @_; my $vals = $self->{$Special}->{Values}; int (@$vals); } #------------------------------------------------------------ # Extra method implementations sub isReadOnly { return 0 if $XML::DOM::IgnoreReadOnly; my $used = $_[0]->{$Special}->{UsedIn}; defined $used ? $used->isReadOnly : 0; } sub cloneNode { my ($self, $deep) = @_; my $prop = $self->{$Special}; my $map = new XML::DOM::NamedNodeMap (Doc => $prop->{Doc}); # Not copying Parent property on purpose! local $XML::DOM::IgnoreReadOnly = 1; # temporarily... for my $val (@{$prop->{Values}}) { my $key = $val->getNodeName; my $newNode = $val->cloneNode ($deep); $newNode->[XML::DOM::Node::_UsedIn] = $map; $map->{$key} = $newNode; push (@{$map->{$Special}->{Values}}, $newNode); } $map; } sub setOwnerDocument { my ($self, $doc) = @_; my $special = $self->{$Special}; $special->{Doc} = $doc; for my $kid (@{$special->{Values}}) { $kid->setOwnerDocument ($doc); } } sub getChildIndex { my ($self, $attr) = @_; my $i = 0; for my $kid (@{$self->{$Special}->{Values}}) { return $i if $kid == $attr; $i++; } -1; # not found } sub getValues { wantarray ? @{ $_[0]->{$Special}->{Values} } : $_[0]->{$Special}->{Values}; } # Remove circular dependencies. The NamedNodeMap and its values should # not be used afterwards. sub dispose { my $self = shift; for my $kid (@{$self->getValues}) { undef $kid->[XML::DOM::Node::_UsedIn]; # was delete $kid->dispose; } delete $self->{$Special}->{Doc}; delete $self->{$Special}->{Parent}; delete $self->{$Special}->{Values}; for my $key (keys %$self) { delete $self->{$key}; } } sub setParentNode { $_[0]->{$Special}->{Parent} = $_[1]; } sub getProperty { $_[0]->{$Special}->{$_[1]}; } #?? remove after debugging sub toString { my ($self) = @_; my $str = "NamedNodeMap["; while (my ($key, $val) = each %$self) { if ($key eq $Special) { $str .= "##Special ("; while (my ($k, $v) = each %$val) { if ($k eq "Values") { $str .= $k . " => ["; for my $a (@$v) { # $str .= $a->getNodeName . "=" . $a . ","; $str .= $a->toString . ","; } $str .= "], "; } else { $str .= $k . " => " . $v . ", "; } } $str .= "), "; } else { $str .= $key . " => " . $val . ", "; } } $str . "]"; } 1; # package return code