summaryrefslogtreecommitdiff
path: root/XPath/Builder.pm
diff options
context:
space:
mode:
Diffstat (limited to 'XPath/Builder.pm')
-rw-r--r--XPath/Builder.pm198
1 files changed, 198 insertions, 0 deletions
diff --git a/XPath/Builder.pm b/XPath/Builder.pm
new file mode 100644
index 0000000..cc37a9d
--- /dev/null
+++ b/XPath/Builder.pm
@@ -0,0 +1,198 @@
+# $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