--- nfo/perl/libs/XML/XUpdate/XSLT.pm 2003/04/30 02:36:32 1.1 +++ nfo/perl/libs/XML/XUpdate/XSLT.pm 2003/05/07 03:11:28 1.6 @@ -4,18 +4,107 @@ # # By Andreas Motl, andreas.motl@ilo.de # -# $Id: XSLT.pm,v 1.1 2003/04/30 02:36:32 joko Exp $ +# $Id: XSLT.pm,v 1.6 2003/05/07 03:11:28 joko Exp $ # # $Log: XSLT.pm,v $ +# Revision 1.6 2003/05/07 03:11:28 joko +# updated pod: new section "References", added item to "Todo" section +# +# Revision 1.5 2003/05/06 14:24:06 joko +# doesn't use Carp any more +# updated pod +# attempt to rewrite xml to make it more human readable using helper modules from CPAN (node indentation, etc.) +# +# Revision 1.4 2003/05/01 23:40:32 joko +# minor update: commented debugging part +# +# Revision 1.3 2003/05/01 20:11:49 joko +# * added pod from xupdate.pl +# - extracted xml to external files +# # Revision 1.1 2003/04/30 02:36:32 joko # initial commit # # ############################################################################### +=pod + + =head1 NAME -XML::XUpdate::XSLT - A perl module for updating xml documents using XUpdate via XSLT. + XML::XUpdate::XSLT - A perl module for updating xml documents using XUpdate via XSLT. + + +=head3 Overview + + This is not the same xupdate currently available from CPAN at + http://search.cpan.org/author/PAJAS/XML-XUpdate-LibXML-0.4.0/xupdate . + + Its intention - however - is identical: + xupdate - Process XUpdate commands against an XML document. + + +=head4 Their implementations differ: + + 1. xupdate (by Petr Pajas) uses ... + XML::XUpdate::LibXML - Simple implementation of XUpdate format + + ... which is based on XML::LibXML which in turn is: + [...] + This module is an interface to the gnome libxml2 DOM parser (no SAX parser support yet), + and the DOM tree. It also provides an XML::XPath-like findnodes() interface, providing + access to the XPath API in libxml2. + [...] + + 2. This xupdate attempts to implement the XUpdate specs using XSLT only. + + +=head4 Yet another xupdate - facts in short: + + S: It would be nice to have a pure perl thingy which does (almost) the same stuff.... + + Q: Can we achieve compliance with its (XML::XUpdate::LibXML) API? (or just a subset ....) + + Q: Can we achieve the processing using CPAN's XML::XSLT? + S: Proposal: XML::XUpdate::XSLT!? + + Q: Can we mimic/use the interface of the - already established - 'xupdate' program??? + + Q: Should we follow the CRUD path first? + (CRUD is the acronym for the datastore action primitives: Create, Retrieve, Update, Delete) + S?: Proposal: XML::XUpdate::XSLT::API uses XML::XUpdate::XSLT::CRUD + + +=head3 References + + - XUpdate: + Requirements: http://www.xmldb.org/xupdate/xupdate-req.html + Working Draft: http://www.xmldb.org/xupdate/xupdate-wd.html + - XML API: + - XML::XUpdate::LibXML: http://search.cpan.org/author/PAJAS/XML-XUpdate-LibXML-0.4.0/lib/XML/XUpdate/LibXML.pm + - XSL / XSLT: + http://www.w3.org/TR/xslt + http://www.xsl-rp.de/ + http://xml.klute-thiemann.de/w3c-de/REC-xslt-20020318/ + http://xmlxslt.sourceforge.net/ + - misc pointers: + "Re: Modify XML documents": http://aspn.activestate.com/ASPN/Mail/Message/perl-xml/1265431 + XSL Extensions: http://xmlsoft.org/XSLT/extensions.html + EXSLT: http://www.exslt.org/set/functions/difference/index.html + "how to insert element at required position in document tree?": http://p2p.wrox.com/archive/xslt/2001-06/98.asp + XML APIs for Databases: http://www.javaworld.com/javaworld/jw-01-2000/jw-01-dbxml.html + + +=head3 Todo + + o What about proper encoding? (ISO-8859-1 or UTF-8) + o Is it possible to create the required "xsl_template.xml" at runtime via XSL itself? + o Cache contents of external files (*.xml). Performance! + o Can exception / error-code handling be improved somehow? + Esp.: It would be interesting, if the XUpdate payload actually could be applied, or not... + o , etc. + =cut @@ -27,9 +116,10 @@ use strict; use warnings; -use XML::XSLT 0.41; -use Carp; use Data::Dumper; +use File::Basename; +use XML::XSLT 0.41; +use XML::XUpdate::Rewrite; # Namespace constants @@ -37,7 +127,7 @@ use vars qw ( $VERSION ); -$VERSION = '0.02'; +$VERSION = '0.01'; ###################################################################### @@ -48,25 +138,33 @@ my $self = bless {}, $class; my $args = $self->__parse_args(@_); - $self->__init_default_macros(); + $self->{DEBUG} = $args->{debug}; + $self->{WARNINGS} = $args->{warnings}; -#=pod - $self->{XSLT_ENGINE} = XML::XSLT->new( - Source => $self->get_macro("xupdate2xsl"), - debug => $args->{debug}, - warnings => $args->{warnings} - ); -#=cut + $self->__init_default_stylesheets(); - #print Dumper($self); return $self; } -sub get_macro { +sub get_stylesheet { my $self = shift; my $name = shift; - return $self->{MACROS}->{$name}; + return $self->{XML}->{xsl}->{$name}; +} + +sub set_stylesheet { + my $self = shift; + my $name = shift; + my $xml = shift; + my $options = shift; + if ($options->{encap}) { + my $template = $self->__slurp_file("xsl_template.xml"); + # FIXME! What about the quirky? Is there a better one? + $template =~ s!!$xml!; + $xml = $template; + } + $self->{XML}->{xsl}->{$name} = $xml; } sub open_document { @@ -85,27 +183,74 @@ sub process { my $self = shift; - # first, translate xupdate to xsl - $self->{XSLT_ENGINE}->open_xml( $self->{XML}->{xupdate} ); - $self->{XSLT_ENGINE}->process(); - $self->{XML}->{XSL} = $self->{XSLT_ENGINE}->toString(); + $self->_calculate(); + $self->_apply(); } -sub toString { +# First, translate the xupdate payload to xsl. +# FIXME: do DOM only! (don't use "->toString") +sub _calculate { my $self = shift; - return $self->{XML}->{XSL}; + $self->{XSLT_ENGINE_PREP} = XML::XSLT->new( + Source => $self->get_stylesheet("xupdate2xsl"), + debug => $self->{DEBUG}, + warnings => $self->{WARNINGS} + ); + $self->{XSLT_ENGINE_PREP}->open_xml( $self->{XML}->{xupdate} ); + $self->{XSLT_ENGINE_PREP}->process(); + $self->set_stylesheet( "_worker", $self->{XSLT_ENGINE_PREP}->toString(), { encap => 1 } ); } +# After that, use this worker xsl to actually apply the changes to the original document. +# FIXME: do DOM only! +sub _apply { + my $self = shift; + + # debug - print the calculated xsl on STDERR + print STDERR $self->get_stylesheet("_worker"), "\n"; + + #return; + $self->{XSLT_ENGINE_LIVE} = XML::XSLT->new( + Source => $self->get_stylesheet("_worker"), + debug => $self->{DEBUG}, + warnings => $self->{WARNINGS} + ); + $self->{XSLT_ENGINE_LIVE}->open_xml( $self->{XML}->{document} ); + $self->{XSLT_ENGINE_LIVE}->process(); + $self->{XML}->{result} = $self->{XSLT_ENGINE_LIVE}->toString(); +} + +sub toString { + my $self = shift; + + # use rest of argument list as hash of option values + my $options = {@_}; + + # short circuit - just return what we have - don't modify anything + return $self->{XML}->{result} unless $options and $options->{rewrite}; + + # Rewrite the xml document with certain engine to desired style. + # 'engine' defaults to "XMLParser" if not specified. + my $rewrite = XML::XUpdate::Rewrite->new( style => $options->{mode}, engine => $options->{using} ); + $rewrite->set_document( $self->{XML}->{result} ); + # TODO: Implement a configurable fallback here to return the un-rewritten payload if desired. + $rewrite->process() or die(__PACKAGE__ . ": Error while rewriting XML document."); + return $rewrite->get_document(); +} + ###################################################################### # AUXILIARY METHODS -# Argument parsing with backwards compatibility. +# Argument list parsing. +# ... from XML::XUpdate::LibXML (w/o backwards compatibility hook). +# Could this make up a Class::AutoFill::__read_arglist which transparently +# makes object attributes from constructor arguments in a configurable way? sub __parse_args { my $self = shift; my %args; - if(@_ % 2 ) { + if ( @_ % 2 ) { $args{dummy} = shift; %args = (%args, @_); } else { @@ -116,80 +261,30 @@ } -sub __init_default_macros { +sub __init_default_stylesheets { my $self = shift; - $self->{MACROS}->{xupdate2xsl} = qq( - - + if (my $payload = $self->__slurp_file("xupdate2xsl.xml")) { + $self->{XML}->{xsl}->{xupdate2xsl} = $payload; + } +} + +sub __slurp_file { + my $self = shift; + my $filename = shift; - - - - - - - - - - - - - - - - - - - 1. passthru logic - - passthru - - - - * - passthru - - - comment() - passthru - - - - - - - - - - 2. context finder - - - - - - 3. rewrite / vivify elements/attributes - - - - - - - - - - - - - - - + # does file exist? + if (! -e $filename) { + $filename = dirname(__FILE__) . "/$filename"; + if (! -e $filename) { + die("File $filename does not exist."); + } + } - ); - + open(FH, "<" . $filename); + my @lines = ; + my $content = join("", @lines); + close(FH); + return $content; } 1;