home *** CD-ROM | disk | FTP | other *** search
- # $Id: Builder.pm,v 1.10 2001/06/12 20:56:56 matt Exp $
-
- package XML::XPath::Builder;
-
- use strict;
-
- # to get array index constants
- use XML::XPath::Node;
- use XML::XPath::Node::Element;
- use XML::XPath::Node::Attribute;
- use XML::XPath::Node::Namespace;
- use XML::XPath::Node::Text;
- use XML::XPath::Node::PI;
- use XML::XPath::Node::Comment;
-
- use vars qw/$xmlns_ns $xml_ns/;
-
- $xmlns_ns = "http://www.w3.org/2000/xmlns/";
- $xml_ns = "http://www.w3.org/XML/1998/namespace";
-
- sub new {
- my $class = shift;
- my $self = ($#_ == 0) ? { %{ (shift) } } : { @_ };
-
- bless $self, $class;
- }
-
- sub start_document {
- my $self = shift;
-
- $self->{IdNames} = {};
- $self->{InScopeNamespaceStack} = [ {
- '_Default' => undef,
- 'xmlns' => $xmlns_ns,
- 'xml' => $xml_ns,
- } ];
-
- $self->{NodeStack} = [ ];
-
- my $document = XML::XPath::Node::Element->new();
- my $newns = XML::XPath::Node::Namespace->new('xml', $xml_ns);
- $document->appendNamespace($newns);
- $self->{current} = $self->{DOC_Node} = $document;
- }
-
- sub end_document {
- my $self = shift;
-
- return $self->{DOC_Node};
- }
-
- sub characters {
- my $self = shift;
- my $sarg = shift;
- my $text = $sarg->{Data};
-
- my $parent = $self->{current};
-
- my $last = $parent->getLastChild;
- if ($last && $last->isTextNode) {
- # append to previous text node
- $last->appendText($text);
- return;
- }
-
- my $node = XML::XPath::Node::Text->new($text);
- $parent->appendChild($node, 1);
- }
-
- sub start_element {
- my $self = shift;
- my $sarg = shift;
- my $tag = $sarg->{'Name'};
- my $attr = $sarg->{'Attributes'};
-
- push @{ $self->{InScopeNamespaceStack} },
- { %{ $self->{InScopeNamespaceStack}[-1] } };
- $self->_scan_namespaces(@_);
-
- my ($prefix, $namespace) = $self->_namespace($tag);
-
- my $node = XML::XPath::Node::Element->new($tag, $prefix);
-
- foreach my $name (keys %$attr) {
- my $value = $attr->{$name};
-
- if ($name =~ /^xmlns(:(.*))?$/) {
- # namespace node
- my $prefix = $2 || '#default';
- # warn "Creating NS node: $prefix = $value\n";
- my $newns = XML::XPath::Node::Namespace->new($prefix, $value);
- $node->appendNamespace($newns);
- }
- else {
- my ($prefix, $namespace) = $self->_namespace($name);
- undef $namespace unless $prefix;
-
- my $newattr = XML::XPath::Node::Attribute->new($name, $value, $prefix);
- $node->appendAttribute($newattr, 1);
- if (exists($self->{IdNames}{$tag}) && ($self->{IdNames}{$tag} eq $name)) {
- # warn "appending Id Element: $val for ", $node->getName, "\n";
- $self->{DOC_Node}->appendIdElement($value, $node);
- }
- }
- }
-
- $self->{current}->appendChild($node, 1);
- $self->{current} = $node;
- }
-
- sub end_element {
- my $self = shift;
- $self->{current} = $self->{current}->getParentNode;
- }
-
- sub processing_instruction {
- my $self = shift;
- my $pi = shift;
- my $node = XML::XPath::Node::PI->new($pi->{Target}, $pi->{Data});
- $self->{current}->appendChild($node, 1);
- }
-
- sub comment {
- my $self = shift;
- my $comment = shift;
- my $node = XML::XPath::Node::Comment->new($comment->{Data});
- $self->{current}->appendChild($node, 1);
- }
-
- sub _scan_namespaces {
- my ($self, %attributes) = @_;
-
- while (my ($attr_name, $value) = each %attributes) {
- if ($attr_name eq 'xmlns') {
- $self->{InScopeNamespaceStack}[-1]{'_Default'} = $value;
- } elsif ($attr_name =~ /^xmlns:(.*)$/) {
- my $prefix = $1;
- $self->{InScopeNamespaceStack}[-1]{$prefix} = $value;
- }
- }
- }
-
- sub _namespace {
- my ($self, $name) = @_;
-
- my ($prefix, $localname) = split(/:/, $name);
- if (!defined($localname)) {
- if ($prefix eq 'xmlns') {
- return '', undef;
- } else {
- return '', $self->{InScopeNamespaceStack}[-1]{'_Default'};
- }
- } else {
- return $prefix, $self->{InScopeNamespaceStack}[-1]{$prefix};
- }
- }
-
- 1;
-
- __END__
-
- =head1 NAME
-
- XML::XPath::Builder - SAX handler for building an XPath tree
-
- =head1 SYNOPSIS
-
- use AnySAXParser;
- use XML::XPath::Builder;
-
- $builder = XML::XPath::Builder->new();
- $parser = AnySAXParser->new( Handler => $builder );
-
- $root_node = $parser->parse( Source => [SOURCE] );
-
- =head1 DESCRIPTION
-
- C<XML::XPath::Builder> is a SAX handler for building an XML::XPath
- tree.
-
- C<XML::XPath::Builder> is used by creating a new instance of
- C<XML::XPath::Builder> and providing it as the Handler for a SAX
- parser. Calling `C<parse()>' on the SAX parser will return the
- root node of the tree built from that parse.
-
- =head1 AUTHOR
-
- Ken MacLeod, <ken@bitsko.slc.ut.us>
-
- =head1 SEE ALSO
-
- perl(1), XML::XPath(3)
-
- PerlSAX.pod in libxml-perl
-
- Extensible Markup Language (XML) <http://www.w3c.org/XML>
-
- =cut
-