XML::DOM2::DOM::Element - A library of DOM (Document Object Model) methods for XML Elements.


XML-DOM2 documentation Contained in the XML-DOM2 distribution.

Index


Code Index:

NAME

Top

XML::DOM2::DOM::Element - A library of DOM (Document Object Model) methods for XML Elements.

DESCRIPTION

Top

Provides all the DOM method for XML Elements

METHODS

Top

$element->getFirstChild()

$element->firstChild()

  Returns the elements first child in it's children list

$element->getLastChild()

$element->lastChild()

  Returns the elements last child in it's children list

$element->getChildIndex( @children )

  Return the array index of this element in the parent or the passed list (if there is one).

$element->getChildAtIndex( $index )

  Return the element at the specified index (the index can be negative).

$element->getNextSibling()

$element->nextSibling()

  Return the next element to this element in the parents child list.

$element->getPreviousSibling()

$element->previousSibling()

  Return the previous element to this element in the parents child list.

$element->getChildren()

$element->getChildElements()

$element->getChildNodes()

  Returns all the elements children.

$element->getChildrenByName( $name )

  Returns all the elements children with that tag name (including namespace prefix).

$element->hasChildren()

$element->hasChildElements()

$element->hasChildNodes()

  Returns 1 if this element has children.

$element->getParent()

$element->getParentElement()

$element->getParentNode()

  Returns the object of the parent element.

$element->setParent( $element )

$element->setParentElement( $element )

$element->setParent($parent);

Sets the parent node, used internaly.

$element->getParents()

$element->getParentElements()

$element->getParentNodes()

$element->getAncestors()

  Return a list of the parents of the current element, starting from the immediate parent. The
  last member of the list should be the document element.

$element->isAncestor( $node )

  Returns true if the current element is an ancestor of the descendant element.

$element->isDescendant( $node )

  Return true if the crrent element is the descendant of the ancestor element.

$element->getSiblings()

  Returns a list of sibling elements.

$element->hasSiblings()

  Returns true if the elements has sibling elements.

$element->getElementName()

$element->getElementType()

$element->getType()

$element->getTagName()

$element->getTagType()

$element->getNodeName()

$element->getNodeType()

  Return a string containing the name (i.e. the type, not the Id) of an element.

$element->getElementId()

  Return a string containing the elements Id (unique identifier string).

$element->getAttribute( $attributeName )

  Returns the specified attribute in the element, will return a
  serialised string instead of posible attribute object if serialise set.

$element->getAttributes( $serialise, $ns )

  Returns a list of attributes in various forms.

$element->getAttributeNames()

  Returns a list of attribute names, used internaly.

$element->getAttributeNamesNS( $namespace )

  Returns a list of attribute names, used internaly.

$element->getAttributeNamespaces()

  Returns a list of attribute names, used internaly.

$element->hasAttribute( $attributeName )

  Returns true if this element as this attribute.

$element->hasAttributeNS( $namespace, $attributeName )

  Returns true if this attribute in this namespace is in this element.

$element->hasAttributes()

  Return true is element has any attributes

$element->setAttribute( $attribute, $value )

  Set an attribute on this element, it will accept serialised strings or objects.

$element->removeAttribute( $name )

  Remove a single attribute from this element.

$element->removeAttributeNS( $namespace, $name )

  Remove a single attribute from this element.

$element->getAttributeNS( $namespace, $name )

  Returns an attributes namespace in this element.

$element->setAttributeNS( $namespace, $name, $value )

  Sets an attributes namespace in this element.

$element->cdata( $text )

  Rerieve and set this elements cdata (non tag cdata form)

$element->hasCDATA()

  Return true if this element has cdata.

$element->document()

  Return this elements document, returns undef if no document available.

$element->insertBefore( $node, $childNode )

$element->insertChildBefore( $node, $childNode )

$element->insertNodeBefore( $node, $childNode )

$element->insertElementBefore( $node, $childNode )

  Inserts a new element just before the referenced child.

$element->insertAfter( $node, $childNode )

$element->insertChildAfter( $node, $childNode )

$element->insertElementAfter( $node, $childNode )

$element->insertNodeAfter( $node, $childNode )

Inserts a new child element just after the referenced child.

$element->insertSiblingAfter( $node )

  Inserts the child just after the current element (effects parent).

$element->insertSiblingBefore( $node )

  Inserts the child just before the current element (effects parent).

$element->replaceChild( $newChild, $oldChild )

  Replace an old child with a new element, returns old element.

$element->replaceElement( $newElement )

$element->replaceNode( $newElement )

  Replace an old element with a new element in the parents context; element becomes orphaned.

$element->removeChild( $child )

  Remove a child from this element, returns the orphaned element.

$element->removeElement()

$element->removeNode()

  Removes this element from it's parent; element becomes orphaned.

$element->appendChild( $node )

$element->appendElement( $node )

$element->appendNode( $node )

  Adds the new child to the end of this elements children list.

$element->cloneNode( $deep )

$element->cloneElement( $deep )

  Clones the current element, deep allows all child elements to be cloned.
  The new element is an orphan with all the same id's and atributes as this element.

$element->findChildIndex( $child )

  Scans through children trying to find this child in the list.

$element->insertAtIndex( $node, $index )

  Adds the new child at the specified index to this element.

$element->removeChildAtIndex( $index )

  Removed the child at index and returns the now orphaned element.

$element->createChildElement( $name, %options )

$element->createElement( $name, %options )

Not DOM2, creates a child element, appending to current element.

The advantage to using this method is the elements created with $document->createElement create basic element objects or base objects (those specified in the XML base class or it's kin) Elements created with this could offer more complex objects back.

Example: an SVG Gradiant will have stop elements under it, creating stop elements with $document->createElement will return an XML::DOM2::Element create a stop element with $element->createChildElement and it will return an SVG2::Element::Gradiant::Stop object (although both would output the same xml) and it would also prevent you from creating invalid child elements such as a group within a text element.

$element->createChildElement($name, %opts);

AUTHOR

Top

Martin Owens, doctormo@postmaster.co.uk

SEE ALSO

Top

perl(1), XML::DOM2, XML::DOM2::Element

http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html DOM at the W3C


XML-DOM2 documentation Contained in the XML-DOM2 distribution.
package XML::DOM2::DOM::Element;

use base "XML::DOM2::DOM::NameSpace";

use strict;
use Carp;

sub getFirstChild ($) {
    my $self=shift;

    if (my @children=$self->getChildren) {
        return $children[0];
    }
    return undef;
}
*firstChild=\&getFirstChild;

sub getLastChild ($) {
	my $self=shift;

	if (my @children=$self->getChildren) {
		return $children[-1];
	}

	return undef;
}
*lastChild=\&getLastChild;

sub getChildIndex ($;@) {
    my ($self,@children)=@_;

    unless (@children) {
        my $parent=$self->getParent();
        @children=$parent->getChildren();
        return undef unless @children;
    }

    for my $index (0..$#children) {
        return $index if $children[$index] == $self;
    }

    return undef;
}

sub getChildAtIndex ($$;@) {
    my ($self,$index,@children)=@_;

    unless (@children) {
        my $parent=$self->getParent();
        @children=$parent->getChildren();
        return undef unless @children;
    }

    return $children[$index];
}

sub getNextSibling ($) {
    my $self=shift;

    if (my $parent=$self->getParent) {
        my @children=$parent->getChildren();
        my $index=$self->getChildIndex(@children);
        if (defined $index and scalar(@children)>$index) {
            return $children[$index+1];
        }
    }
    return undef;
}
*nextSibling=\&getNextSibling;

sub getPreviousSibling ($) {
    my $self=shift;

    if (my $parent=$self->getParent) {
        my @children=$parent->getChildren();
        my $index=$self->getChildIndex(@children);
        if ($index) {
            return $children[$index-1];
        }
    }

    return undef;
}
*previousSibling=\&getPreviousSibling;

sub getChildren ($) {
    my $self=shift;
    if ($self->{'children'}) {
		return @{$self->{'children'}};
	}
    return ();
}
*getChildElements=\&getChildren;
*getChildNodes=\&getChildren;

sub getChildrenByName
{
	my ($self, $name) = @_;
	if(defined($self->{'child'}->{$name})) {
		if(wantarray) {
			return @{$self->{'child'}->{$name}};
		} else {
			return $self->{'child'}->{$name}->[0];
		}
	}
}

sub hasChildren ($) {
    my $self=shift;

    if (exists $self->{'children'}) {
        if (scalar @{$self->{'children'}}) {
            return 1;
        }
    }

    return 0;
}
*hasChildElements=\&hasChildren;
*hasChildNodes=\&hasChildren;

sub getParent ($) {
    my $self=shift;

    if ($self->{'parent'}) {
        return $self->{'parent'};
    }

    return undef;
}
*getParentElement=\&getParent;
*getParentNode=\&getParent;

sub setParent ($$) {
    my ($self,$parent) = @_;

	if(ref($parent) or not defined($parent)) {
		$self->{'parent'} = $parent;
		return 1;
	}

    return undef;
}
*setParentElement=\&setParent;

sub getParents {
    my $self=shift;

    my $parent = $self->getParent;
    return undef unless $parent;

    my @parents;
    while ($parent) {
        push @parents,$parent;
        $parent=$parent->getParent;
    }

    return @parents;
}
*getParentElements=\&getParents;
*getParentNodes=\&getParents;
*getAncestors=\&getParents;

sub isAncestor ($$) {
    my ($self,$descendant)=@_;

    my @parents=$descendant->getParents();
    foreach my $parent (@parents) {
        return 1 if $parent==$self;
    }

    return 0;
}

sub isDescendant ($$) {
    my ($self,$ancestor)=@_;

    my @parents=$self->getParents();
    foreach my $parent (@parents) {
        return 1 if $parent==$ancestor;
    }

    return 0;
}

sub getSiblings ($) {
    my $self=shift;

    if (my $parent=$self->getParent) {
        return $parent->getChildren();
    }

    return wantarray?():undef;
}

sub hasSiblings ($) {
    my $self=shift;

    if (my $parent=$self->getParent) {
        my $siblings=scalar($parent->getChildren);
        return 1 if $siblings>=2;
    }

    return undef;
}

sub getElementName ($) {
    my $self=shift;

	return $self->name;
}
*getType=\&getElementName;
*getElementType=\&getElementName;
*getTagName=\&getElementName;
*getTagType=\&getElementName;
*getNodeName=\&getElementName;
*getNodeType=\&getElementName;

sub getElementId ($) {
    my $self=shift;

    if (exists $self->{id}) {
        return $self->{id};
    }

    return undef;
}

sub getAttribute
{
    my ($self, $name) = @_;
	my $attribute = $self->{'attributes'}->{''}->{$name};
	return $attribute;
}

sub getAttributes
{
    my ($self, $serialise, $ns) = @_;
	my @names = $self->getAttributeNamesNS($ns);
	my %attributes;
	my @attributes;
	foreach my $nsr (@names) {
		my ($sns, $name) = @{$nsr};
		my $attribute;
		if($sns) {
			$attribute = $self->getAttributeNS($sns, $name, $serialise);
		} else {
			$attribute = $self->getAttribute($name, $serialise);
		}
		if(not defined($attribute)) {
			die "Something is very wrong with the attributes";
		}
		if(not ref($attribute)) {
			die "An attribute should always be an object: ($name:$attribute) ".$self->name."\n";
		}
		if($serialise <= 1) {
			$attributes{$attribute->name} = $attribute;
		} else {
			push @attributes, $attribute->serialise_full;
		}
    }
	if($serialise <= 1) {
	    return wantarray ? %attributes : \%attributes;
	} elsif($serialise == 2) {
		return wantarray ? @attributes : \@attributes;
	} else {
		return join(' ', @attributes);
	}
}

sub getAttributeNames
{
    my ($self, $ns) = @_;
	my $prefix = $ns ? $ns->ns_prefix : '';
	warn "The prefix is undefined!" if not defined($prefix);
	my @names;
	foreach my $name (keys(%{$self->{'attributes'}->{$prefix}})) {
		push @names, $name;
	}
    return wantarray ? @names : \@names;
}

sub getAttributeNamesNS
{
	my ($self, $ns) = @_;
	# Default Namespace
	my @names;

	# Get all other name spaces
	my @ns = $ns ? ($ns) : $self->getAttributeNamespaces;

	foreach my $sns (@ns) {
		if(defined($sns)) {
			foreach my $name ($self->getAttributeNames($sns)) {
				push @names, [ $sns, $name ];
			}
		} else {
			warn "One of the name spaces is not defined\n";
		}
	}
	return @names;
}

sub getAttributeNamespaces
{
	my ($self) = @_;
	return map { $_ ne '' ? $self->document->getNamespace($_) : '' } keys(%{$self->{'attributes'}});
}

sub hasAttribute
{
	my ($self, $name) = @_;
	return 1 if exists( $self->{'attributes'}->{''}->{$name} );
}

sub hasAttributeNS
{
    my ($self, $ns, $name) = @_;
	my $prefix = $ns->ns_prefix;
    return 1 if exists( $self->{'attributes'}->{$prefix}->{$name} );
}

sub hasAttributes
{
	my ($self) = @_;
	return 1 if $self->{'attributes'} and keys(%{ $self->{'attributes'} })
}

sub setAttribute
{
    my ($self, $name, $value) = @_;
	confess "Name is not defined" if not $name;
	my $existing = $self->getAttribute($name);
	# This ensures that ids are updated in a sane way.
	if ($name eq "id" and $self->document and defined($value)) {
		# Set the new id
		if($self->document->addId($value, $self)) {
			if($existing) {
				# Remove the old id
				my $oldvalue = $existing->serialise;
				$self->document->removeId($oldvalue);
			}
		} else {
			$self->error('setAttribute', "Id '$value' already exists in document, unable to modify attribute");
			return undef;
		}
	}

	# Some elements can't contain attributes
	$self->{'attributes'}->{''}->{$name} = $self->_get_attribute_object( $name, $value, undef, $existing );
	return 1;
}

sub _get_attribute_object
{
	my ($self, $name, $value, $ns, $existing) = @_;
	if(not $self->_can_contain_attributes) {
		$self->error('setAttribute', "This Element can not contain attributes. (".$self->getElementName.")");
		return undef;
	}
	# undef means delete attribute
	return $self->removeAttribute($name) if not defined($value);
	my $result;
	# This is to handle attributes handled by objects
	if($self->_has_attribute($name)) {
		$result = $existing;
		if(not $result) {
			# Create a new attribute
			$result = $self->_attribute_handle( $name, name => $name, namespace => $ns, owner => $self );
			
		}
		croak "Unable to setAttribute, _attribute_handle does not exist (".ref($self).":$name)" if not ref($result);
		$result->deserialise($value);
	}
	return $result;
}

sub removeAttribute
{
	my ($self, $name) = @_;
	if($self->hasAttribute($name)) {
		my $attribute = delete($self->{'attributes'}->{''}->{$name});
		$attribute->delete;
	}
}

sub removeAttributeNS
{
    my ($self, $ns, $name) = @_;
    if($self->hasAttributeNS($ns, $name)) {
        my $attribute = delete($self->{'attributes'}->{$ns->ns_prefix}->{$name});
		$attribute->delete;
    }
}

sub getAttributeNS
{
	my ($self, $ns, $name) = @_;
	if(not ref($ns)) {
		confess "You must give ns methods the name space object, not just the URI or Prefix (skipped)";
	}
	my $prefix = $ns->ns_prefix;
	$prefix = '' if not $prefix;
	if($self->{'attributes'}->{$prefix}->{$name}) {
		return $self->{'attributes'}->{$prefix}->{$name};
	}
}

sub setAttributeNS
{
	my ($self, $ns, $name, $value) = @_;
	if(not ref($ns)) {
		confess "You must give ns methods the name space object, not just the URI or Prefix (skipped)";
	}
	my $prefix = $ns->ns_prefix;
	$self->{'attributes'}->{$prefix}->{$name} = $self->_get_attribute_object($name, $value, $ns);
	if(not $self->{'attributes'}->{$prefix}->{$name}) {
		warn "setAttributeNS was unable to set the attribute ";
	}
}

sub cdata
{
	my ($self, $text) = @_;
	if($self->hasChildren()) {
		$self->error(value => "Unable to get cdata for element with children, xml error!");
		return;
	}
	if(defined($text)) {
		if(ref($text) =~ /CDATA/) {
			$self->{'cdata'} = $text;
		} else {
			$self->{'cdata'} = XML::DOM2::Element::CDATA->new($text, notag => 1);
		}
	}
	return $self->{'cdata'};
}

sub hasCDATA ($) {
	my $self=shift;
	return exists($self->{'cdata'});
}

sub document
{
	my ($self) = @_;
	return $self->{'document'} if ref($self->{'document'});
	if($self->getParent) {
		return $self->getParent->document;
	} else {
		confess "Where you expecting an orphaned element ".$self->localName."\n";
		return undef;
	}
}

sub insertBefore
{
	my ($self, $newChild, $refChild) = @_;
	return $self->appendElement($newChild) if not $refChild;
	my $index = $self->findChildIndex($refChild);
	return 0 if $index < 0; # NO_FOUND_ERR
	return $self->insertAtIndex($newChild, $index);
}
*insertChildBefore=\&insertBefore;
*insertNodeBefore=\&insertBefore;
*insertElementBefore=\&insertBefore;

sub insertAfter
{
	my ($self, $newChild, $refChild) = @_;
	return $self->appendElement($newChild) if not $refChild;
	my $index = $self->findChildIndex($refChild);
	return 0 if $index < 0; # NO_FOUND_ERR
	return $self->insertAtIndex($newChild, $index+1);
}
*insertChildAfter=\&insertAfter;
*insertNodeAfter=\&insertAfter;
*insertElementAfter=\&insertAfter;

sub insertSiblingAfter
{
	my ($self, $newChild) = @_;
	return $self->getParent->insertAfter($newChild, $self) if $self->getParent;
	return 0;
}

sub insertSiblingBefore
{
    my ($self, $newChild) = @_;
    return $self->getParent->insertBefore($newChild, $self) if $self->getParent;
    return 0;
} 

sub replaceChild
{
	my ($self, $newChild, $oldChild) = @_;
	# Replace newChild if it is in this list of children already
	$self->removeChild($newChild) if $newChild->getParent eq $self;
	# We need the index of the node to replace
	my $index = $self->findChildIndex($oldChild);
	return 0 if($index < 0); # NOT_FOUND_ERR
	# Replace and bind new node with it's family
	$self->removeChildAtIndex($index);
	$self->insertChildAtIndex($index);
	return $oldChild;
}

sub replaceElement
{
	my ($self, $newElement) = @_;
	return $self->getParent->replaceChild($newElement, $self);
}
*replaceNode=\&replaceElement;

sub removeChild
{
	my ($self, $oldChild) = @_;
	my $index = $self->findChildIndex($oldChild);
	return 0 if(not defined $index or $index < 0); # NOT_FOUND_ERR
	return $self->removeChildAtIndex($index);
}

sub removeElement
{
	my ($self) = @_;
	return $self->getParent->removeChild($self);
}
*removeNode=\&removeElement;

sub appendChild
{
	my ($self, $element) = @_;
	return $self->insertAtIndex( $element, scalar($self->getChildren) || 0 );
}
*appendElement=\&appendChild;
*appendNode=\&appendChild;

sub cloneNode
{
	my ($self, $deep) = @_;
	my $clone = XML::DOM2::Element->new($self->localName);
	foreach my $key (keys(%{$self})) {
		if($key ne 'children' and $key ne 'parent') {
			$clone->{$key} = $self->{$key};
		}
	}
	# We need to clone the children if deep is specified.
	if($deep) {
		foreach my $child ($self->getChilden) {
			my $childClone = $child->cloneNode($deep);
			$clone->appendChild($childClone);
		}
	}
	return $clone;
}
*cloneElement=\&cloneNode;

sub findChildIndex
{
	my ($self, $refChild) = @_;
	my $index;
	foreach my $child ($self->getChildren) {
        return $index if $child eq $refChild;
        $index++;
    } 
	return -1;
}

sub insertAtIndex
{
	my ($self, $newChild, $index) = @_;
	confess "Unable to insertAtIndex no index defined" if not defined($index);
	my $id = $newChild->getElementId();
	if($self->document) {
		if($id && not $self->document->addId($id, $newChild)) {
			$self->error($id => "Id already exists in document");
	        return undef;
		}
		$self->document->addElement($newChild);
	} else {
		warn("Unable to insert element ".$self->getElementName." not document defined");
		return 0;
	}
	# Remove the child from other documents and nodes
	$newChild->getParent->removeChild($newChild) if $newChild->getParent;

	# This index supports the getChildrenByName function
	if($self->{'child'}->{$newChild->name}) {
		push @{$self->{'child'}->{$newChild->name}}, $newChild;
	} else {
		$self->{'child'}->{$newChild->name} = [ $newChild ];
	}

	# Set in new parent
	splice @{$self->{'children'}}, $index, 0, $newChild;
    $newChild->setParent($self);
	return 1;
}

sub removeChildAtIndex
{
	my ($self, $index) = @_;
	my $oldChild = splice @{$self->{'children'}}, $index, 1;
	my $id = $oldChild->getElementId();
	$self->document->removeId($id) if($id);
	$self->document->removeElement($oldChild);
	$oldChild->setParent(undef);
	if(not $self->hasChildren) {
		delete $self->{'childen'};
	}
	return $oldChild;
} 

sub createChildElement
{
	my ($self, $name, %opts) = @_;
	my $element = $self->_element_handle($name, %opts, document => $self->document() );
	if(ref($element) =~ /CDATA/) {
		$self->cdata( $element );
	} else {
		$self->appendChild($element);
	}
	return $element;
}
*createElement=\&createChildElement;

return 1;