# $Id: /mirror/perl/XML-RSS-LibXML/trunk/lib/XML/RSS/LibXML/ImplBase.pm 2259 2007-05-07T14:41:59.593216Z daisuke $ # # Copyright (c) 2005-2007 Daisuke Maki # All rights reserved. package XML::RSS::LibXML::ImplBase; use strict; use warnings; use base qw(Class::Accessor::Fast); use Carp qw(croak); use XML::RSS::LibXML::MagicElement; use XML::RSS::LibXML::Namespaces; sub rss_accessor { my $self = shift; my $name = shift; my $c = shift; if (! exists $c->{$name}) { croak "Unregistered entity: Can't access $name field in object of class " . ref($self); } my $ret; if (@_ == 1) { if (ref $_[0]) { # eval { $_[0]->isa('XML::RSS::LibXML::MagicElement') }) { $ret = $c->{$name}; $c->{$name} = $_[0]; } else { $ret = $c->{$name}->{$_[0]}; if (ref $ret && eval { $ret->isa('XML::RSS::LibXML::ElementSpec') }) { $ret = undef; } } } elsif (@_ > 1) { my %hash = @_; my $definition = $self->accessor_definition; foreach my $key (keys %hash) { $self->validate_accessor($definition, $name, $key, $hash{$key}) if $definition; if ($key =~ /^(?:rdf|dc|syn|taxo|admin|content|cc)$/) { if (! exists $c->namespaces->{$key}) { $c->add_module(prefix => $key, uri => XML::RSS::LibXML::Namespaces::lookup_uri($key)); } } # $self->store_element($c, $c->{$name}, $key, $hash{$key}); $self->set_value($c, $name, $key, $hash{$key}); if (my $uri = $c->namespaces->{$key}) { $self->set_value($c, $name, $uri, $hash{$key}); # $self->store_element($c, $c->{$name}, $uri, $hash{$key}); } } $ret = $c->{$name}; } else { $ret = $c->{$name}; if (ref $ret && eval { $ret->isa('XML::RSS::LibXML::ElementSpec') }) { $ret = undef; } } return $ret; } sub definition {} sub accessor_definition { } sub validate_accessor { my ($self, $definition, $prefix, $key, $value) = @_; if (! defined $value) { croak "Undefined value in XML::RSS::LibXML::validate_accessor"; } my $spec = $definition->{$prefix}{$key}; croak "$key cannot exceed " . $spec->[1] . " characters in length" if defined $spec->[1] && length($value) > $spec->[1]; } sub set_value { my ($self, $c, $prefix, $key, $value) = @_; if (eval { $c->{$prefix}->isa('XML::RSS::LibXML::ElementSpec') }) { $c->{$prefix} = +{ %{ $c->{$prefix} } }; } $c->{$prefix}{$key} = $value; } sub validate_item { } sub channel { shift->rss_accessor('channel', @_) } sub image { shift->rss_accessor('image', @_) } sub textinput { shift->rss_accessor('textinput', @_) } sub skipDays { shift->rss_accessor('skipDays', @_) } sub skipHours { shift->rss_accessor('skipHours', @_) } sub reset { my ($self, $c) = @_; # internal hash $c->_internal({}); # init num of items to 0 $c->num_items(0); # initialize items $c->{items} = []; my $definition = $self->definition; while (my ($k, $v) = each(%$definition)) { $c->{$k} = +{%{$v}}; bless($c->{$k}, 'XML::RSS::LibXML::ElementSpec') if (ref($v) eq 'XML::RSS::LibXML::ElementSpec'); } return; } sub store_element { my ($self, $container, $name, $value) = @_; if (exists $container->{$name} && ! eval { $container->{$name}->isa('XML::RSS::LibXML::ElementSpec') }) { if (ref($container->{$name}) eq 'ARRAY') { push @{ $container->{$name} }, $value; } else { $container->{$name} = [ $container->{$name}, $value ]; } } else { $container->{$name} = $value; } } sub parse_dom { } sub parse_namespaces { my ($self, $c, $dom) = @_; my %namespaces = $self->parse_namespaces_recurse($c, $dom->documentElement()); while (my ($prefix, $uri) = each %namespaces) { $c->add_module(prefix => $prefix, uri => $uri); } } sub parse_namespaces_recurse { my ($self, $c, $parent) = @_; my %namespaces; foreach my $node ($parent->findnodes('./*')) { my %h = $self->parse_namespaces_recurse($c, $node); %namespaces = (%namespaces, %h); } return (%namespaces, $c->get_namespaces($parent)); } sub parse_taxo { my ($self, $c, $dom, $container, $parent) = @_; my $xc = $c->create_xpath_context(scalar $c->namespaces); my @nodes = $xc->findnodes('taxo:topics/rdf:Bag/rdf:li', $parent); return unless @nodes; my $uri = XML::RSS::LibXML::Namespaces::lookup_uri('taxo'); if (! exists $c->namespaces->{taxo}) { $c->add_module(prefix => 'taxo', uri => $uri); } $container->{taxo} ||= []; foreach my $p (@nodes) { push @{ $container->{taxo} }, $p->findvalue('@resource'); } $container->{$uri} = $container->{taxo}; } sub parse_misc_simple { } sub parse_children { my ($self, $c, $node, $xpath) = @_; my %h; $xpath ||= './*'; my $xc = $c->create_xpath_context(scalar $c->namespaces); foreach my $child ($xc->findnodes($xpath, $node)) { my $prefix = $child->getPrefix(); my $name = $child->localname(); # XXX - this is probably the only case where we need to explicitly # normalize a name $name = 'textinput' if ($name eq 'textInput'); my $val = undef; if ($child->findnodes('./*')) { $val = $self->parse_children($c, $child); } else { my $text = $child->textContent(); $text = '' if $text !~ /\S/ ; # argh. it has attributes. we do our little hack... if ($child->hasAttributes) { $val = XML::RSS::LibXML::MagicElement->new( content => $text, attributes => [ $child->attributes ] ); } else { $val = $text; } } # XXX - XML::RSS now can store multiple elements in a slot. # This we detect and change the underlying structure from a # scalar to an array if ($prefix) { $h{$prefix} ||= {}; $self->store_element($h{$prefix}, $name, $val); # XML::RSS requires us to allow access to elements both from # the prefix and the namespace $h{$c->{namespaces}{$prefix}} ||= {}; $self->store_element($h{$c->{namespaces}{$prefix}}, $name, $val); } else { $self->store_element(\%h, $name, $val); } } return wantarray ? %h : \%h; } sub as_string { my ($self, $c, $format) = @_; my $dom = $self->create_dom($c); return $dom->toString($format); } sub create_dom { my ($self, $c) = @_; my $dom = $self->create_document($c); $self->create_dtd($c, $dom); $self->create_pi($c, $dom); $self->create_rootelement($c, $dom); $self->create_namespaces($c, $dom); $self->create_channel($c, $dom); $self->create_items($c, $dom); return $dom; } sub create_pi { my ($self, $c, $dom) = @_; my $styles = $c->stylesheets; foreach my $style (@$styles) { my $pi = $dom->createProcessingInstruction('xml-stylesheet'); $pi->setData(type => 'text/xsl', href => $style); $dom->appendChild($pi); } } sub create_document { my $self = shift; my $c = shift; return XML::LibXML::Document->new('1.0', $c->encoding); } sub create_rootelement {} sub create_dtd {} sub create_channel {} sub create_items {} sub create_misc_simple { my ($self, $c, $dom, $parent) = @_; my $definition = $self->definition; while (my($p, $children) = each %$definition) { next if ! $c->{$p}; my @nodes; while (my($e, $value) = each %$children) { if (defined $value) { my $node = $dom->createElement($e); $node->appendText($value); push @nodes, $node; } } if (@nodes) { my $local_parent = $dom->createElement($p); $local_parent->appendChild($_) for @nodes; $parent->appendChild($local_parent); } } } sub create_taxo { my ($self, $c, $dom, $parent) = @_; my $list = $c->{taxo}; if (! $list || @$list <= 0) { return; } my $topic = $dom->createElement('taxo:topics'); my $bag = $dom->createElement('rdf:Bag'); foreach my $taxo (@$list) { my $node = $dom->createElement('rdf:li'); $node->setAttribute(resource => $taxo); $bag->appendChild($node); } $topic->appendChild($bag); $parent->appendChild($topic); } sub create_extra_modules { my ($self, $c, $dom, $parent, $namespaces) = @_; while (my ($prefix, $uri) = each %$namespaces) { next if $prefix =~ /^(?:dc|syn|taxo|rss\d\d)$/; next if ! defined $c->{$prefix}; while (my($e, $value) = each %{ $c->{$prefix} }) { my $node = $dom->createElement("$prefix:$e"); $node->appendText($value); $parent->appendChild($node); } } } sub create_namespaces { my $self = shift; my $c = shift; my $dom = shift; my $root = $dom->getDocumentElement() or croak "No document element found?!"; my $namespaces = $c->namespaces; while (my($prefix, $url) = each %$namespaces) { next if $prefix =~ /^rss\d\d$/; next if $prefix =~ /^#default$/; $root->setNamespace($url, $prefix, 0); } } sub create_element_from_spec { my ($self, $c, $dom, $parent, $specs) = @_; my $root = $dom->getDocumentElement(); my $node; while (my ($e, $spec) = each %$specs) { my( $callback, $list ); if (ref $spec eq 'HASH') { $callback = $spec->{callback}; $list = $spec->{candidates}; } elsif (ref $spec eq 'ARRAY') { $list = $spec; } foreach my $p (@$list) { my ($prefix, $value); if (ref $p && ref $p eq 'HASH') { if ($c->{$p->{module}}) { $prefix = $p->{module}; $value = $c->{$p->{module}}{$p->{element}}; } } else { $value = $c->{$p}; } if (defined $value) { if ($prefix) { $root->setNamespace( XML::RSS::LibXML::Namespaces::lookup_uri($prefix), $prefix, 0 ); } $node = $dom->createElement($e); if (ref $value && eval { $value->isa('XML::RSS::LibXML::MagicElement') }) { foreach my $attr ($value->attributes) { $node->setAttribute($attr, $value->{$attr}); } } elsif ($callback) { $callback->($value); } $node->appendText($value); $parent->appendChild($node); last; } } } } sub add_item { my $self = shift; my $c = shift; my $h = ref($_[0]) eq 'HASH' ? $_[0] : {@_}; $self->validate_item($c, $h); my $guid = $h->{guid}; if (defined $guid) { # guid should *only* be MagicElement if (! eval { $guid->isa('XML::RSS::LibXML::MagicElement') }) { $h->{permaLink} = $guid; } else { if (my $is_permalink = $guid->{isPermaLink}) { if ($is_permalink eq 'true') { $h->{permaLink} = $guid->{_content}; } } else { $h->{permaLink} = $guid->{_content}; } } } elsif (defined (my $permaLink = $h->{permaLink})) { $h->{guid} = XML::RSS::LibXML::MagicElement->new( content => $permaLink, attributes => { isPermaLink => 'true' } ); } my $namespaces = $c->namespaces; foreach my $p (keys %$namespaces) { if ($h->{$p}) { $h->{ $namespaces->{$p} } = $h->{$p}; } } # add the item to the list if (defined($h->{mode}) && delete $h->{mode} eq 'insert') { unshift(@{$c->items}, $h); } else { push(@{$c->items}, $h); } # return reference to the list of items return $c->{items}; } 1; __END__ =head1 NAME XML::RSS::LibXML::ImplBase - Implementation Base For XML::RSS::LibXML =head1 SYNOPSIS # Internal use only =head1 DESCRIPTION =cut