package Tree::Transform::XSLTish; use strict; use warnings; use Sub::Exporter; use Params::Validate ':all'; use Tree::Transform::XSLTish::Utils; use Tree::Transform::XSLTish::Transformer; use Carp::Clan qw(^Tree::Transform::XSLTish); use 5.006; our $VERSION='0.2'; my @DEFAULT_EXPORTS=('tree_rule', 'default_rules', 'new_transformer' => {-as => 'new'}, ); Sub::Exporter::setup_exporter({ exports => [qw(tree_rule default_rules new_transformer engine_class engine_factory)], groups => { default => \@DEFAULT_EXPORTS, engine => [@DEFAULT_EXPORTS, qw(engine_class engine_factory)], } }); sub default_rules { my $store=Tree::Transform::XSLTish::Utils::_rules_store(scalar caller); push @{$store->{by_match}}, {match=> '/',priority=>0,action=>sub { $_[0]->apply_rules } }, {match=> '*',priority=>0,action=>sub { $_[0]->apply_rules } }, ; return; } sub tree_rule { my (%args)=validate(@_, { match => { type => SCALAR, optional => 1 }, action => { type => CODEREF }, name => { type => SCALAR, optional => 1}, priority => { type => SCALAR, default => 1 }, }); # TODO at least one of 'name' and 'match' must be specified # TODO default priority based on match my $store=Tree::Transform::XSLTish::Utils::_rules_store(scalar caller); if ($args{match}) { push @{$store->{by_match}},\%args; } if ($args{name}) { if (exists $store->{by_name}{$args{name}}) { carp "Duplicate rule named $args{name}, ignoring"; return; } $store->{by_name}{$args{name}}=\%args; } return; } sub engine_class { my ($classname)=@_; Tree::Transform::XSLTish::Utils::_set_engine_factory( scalar caller, sub{$classname->new()}, ); return; } sub engine_factory(&) { my ($new_factory)=@_; Tree::Transform::XSLTish::Utils::_set_engine_factory( scalar caller, $new_factory, ); return; } sub new_transformer { my $rules_package=shift; return Tree::Transform::XSLTish::Transformer->new(rules_package=>$rules_package,@_); } 1; __END__ =head1 NAME Tree::Transform::XSLTish - transform tree data, like XSLT but in Perl =head1 SYNOPSIS package MyTransform; use Tree::Transform::XSLTish; default_rules; tree_rule match => 'node[@id=5]', action => sub { return $_[0]->it->data(); }; package main; use My::Tree; my $tree= My::Tree->new(); # build something inside the tree my ($node5_data)=MyTransform->new->transform($tree); Transforming an HTML document: package HtmlTransform; use Tree::Transform::XSLTish; use strict; use warnings; engine_class 'XML::XPathEngine'; default_rules; tree_rule match => 'img[@alt="pick"]', action => sub { return $_[0]->it->findvalue('@src'); }; package main; use HTML::TreeBuilder::XPath; my $tree=HTML::TreeBuilder::XPath->new(); $tree->parse_file('mypage.html'); my $trans=HtmlTransform->new(); my ($image_srce)=$trans->transform($tree); =head1 DESCRIPTION This module allows you to transform tree with Perl subroutines, just like XSLT does for XML documents. It tries to model as closely as reasonable the semantic of XSLT. =head1 REQUIREMENTS By default, this module uses L as its XPath engine, but you can use any other similar module, provided it implements the method C with the same signature and meaning. L is a good candidate, or you could use L. The tree that you intend to manipulate must be implemented by classes that are compatible with the XPath engine; for example, L if you use L, or L if you use L. =head1 EXPORTS =head2 C tree_rule match => '//node_name', priority => 1, action => sub { ... }; This is the basic fuction to declare a transformation rule; it's equivalent to the C