# Copyright 2001-2005 The Apache Software Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # $Id$ package Apache::AxKit::Language::LibXSLT; use strict; use vars qw/@ISA $VERSION %DEPENDS/; use XML::LibXSLT 1.59; use XML::LibXML 1.59; use Apache; use Apache::Request; use Apache::AxKit::Language; use Apache::AxKit::Provider; use Apache::AxKit::LibXML; use Apache::AxKit::LibXMLCallbacks; use File::Basename qw(dirname); @ISA = 'Apache::AxKit::Language'; $VERSION = 1.0; # this fixes a CPAN.pm bug. Bah! my %style_cache; sub reset_depends { %DEPENDS = (); } sub add_depends { $DEPENDS{shift()}++; } sub get_depends { return keys %DEPENDS; } sub handler { my $class = shift; my ($r, $xml, $style, $last_in_chain) = @_; my ($xmlstring, $xml_doc); AxKit::Debug(7, "[LibXSLT] getting the XML"); if (my $dom = $r->pnotes('dom_tree')) { $xml_doc = $dom; delete $r->pnotes()->{'dom_tree'}; } else { $xmlstring = $r->pnotes('xml_string'); } my $parser = Apache::AxKit::LibXML->new(); $parser->expand_entities(1); $parser->expand_xinclude(1); local $Apache::AxKit::LibXMLCallbacks::provider_cb = sub { my $r = shift; my $provider = Apache::AxKit::Provider->new_content_provider($r); add_depends($provider->key()); return $provider; }; if (!$xml_doc && !$xmlstring) { $xml_doc = $xml->get_dom(); } elsif ($xmlstring) { $xml_doc = $parser->parse_string($xmlstring, $r->uri()); } AxKit::Debug(7, "[LibXSLT] parsing stylesheet"); my $stylesheet; my $cache = $style_cache{$style->key()}; if (ref($cache) eq 'HASH' && !$style->has_changed($cache->{mtime}) && ref($cache->{depends}) eq 'ARRAY') { AxKit::Debug(8, "[LibXSLT] checking if stylesheet is cached"); my $changed = 0; DEPENDS: foreach my $depends (@{ $cache->{depends} }) { my $p = Apache::AxKit::Provider->new_style_provider($r, key => $depends); if ( $p->has_changed( $cache->{mtime} ) ) { $changed = 1; last DEPENDS; } } if (!$changed) { AxKit::Debug(7, "[LibXSLT] stylesheet cached"); $stylesheet = $style_cache{$style->key()}{style}; } } if (!$stylesheet || ref($stylesheet) ne 'XML::LibXSLT::Stylesheet') { reset_depends(); my $style_uri = $style->get_document_uri(); AxKit::Debug(7, "[LibXSLT] parsing stylesheet $style_uri"); my $style_doc = $style->get_dom(); my $xslt = XML::LibXSLT->new(); $xslt->input_callbacks( Apache::AxKit::LibXMLCallbacks->input_callbacks() ); local $Apache::AxKit::LibXMLCallbacks::provider_cb = sub { my $r = shift; my $provider = Apache::AxKit::Provider->new_style_provider($r); add_depends($provider->key()); return $provider; }; $stylesheet = $xslt->parse_stylesheet($style_doc); unless ($r->dir_config('AxDisableXSLTStylesheetCache')) { $style_cache{$style->key()} = { style => $stylesheet, mtime => time, depends => [ get_depends() ] }; } } # get request form/querystring parameters my @params = fixup_params($class->get_params($r)); AxKit::Debug(7, "[LibXSLT] performing transformation"); my $results = $stylesheet->transform($xml_doc, @params); AxKit::Debug(7, "[LibXSLT] transformation finished, creating $results"); if ($last_in_chain) { AxKit::Debug(8, "[LibXSLT] outputting to \$r"); if ($XML::LibXSLT::VERSION >= 1.03) { my $encoding = $stylesheet->output_encoding; my $type = $stylesheet->media_type; $r->content_type("$type; charset=$encoding"); } $stylesheet->output_fh($results, $r); } AxKit::Debug(7, "[LibXSLT] storing results in pnotes(dom_tree) ($r)"); $r->pnotes('dom_tree', $results); # warn "LibXSLT returned $output \n"; # print $stylesheet->output_string($results); return Apache::Constants::OK; } sub fixup_params { my @results; while (@_) { push @results, XML::LibXSLT::xpath_to_string( splice(@_, 0, 2) ); } return @results; } 1;