package Tree::Transform::Transformer; use Moose; use MooseX::AttributeHelpers; use Params::Validate ':all'; use Tree::Transform::Utils; use Tree::Transform::Context; use Tree::XPathEngine; use Carp::Clan qw(^Tree::Transform); has 'rules_package' => (is => 'ro', isa => 'ClassName'); has 'context_stack' => ( metaclass => 'Collection::Array', is => 'rw', isa => 'ArrayRef[Tree::Transform::Context]', default => sub { [] }, provides => { last => 'context', push => 'enter', pop => 'leave', empty => 'has_context', }, ); has 'engine' => ( is => 'ro', isa => 'Tree::XPathEngine', default => sub { Tree::XPathEngine->new() }, ); sub it { $_[0]->context->current_node } sub transform { my ($self,$tree)=@_; return $self->apply_rules($tree->xpath_get_root_node); } sub apply_rules { my ($self,@nodes)=@_; unless (@nodes) { unless ($self->has_context) { carp 'apply_rules called without nodes nor context!'; return; } @nodes=$self->it->xpath_get_child_nodes(); }; $self->enter(Tree::Transform::Context->new(node_list=>\@nodes)); my @ret; for my $node (@nodes) { $self->context->current_node($node); #warn "# applying rules to @{[ $node ]}"; my $rule=$self->find_rule(); push @ret,$rule->($self); } $self->leave; return @ret; } sub find_rule { my ($self,$context)=@_; $context||=$self->context; my $store=Tree::Transform::Utils::_rules_store($self->rules_package); # TODO inheritance my $rules=$store->{by_match}; my @candidates= sort { $b->{priority} <=> $a->{priority} } grep { $self->rule_matches($_) } @$rules; if (@candidates > 1 and $candidates[0]->{priority} == $candidates[1]->{priority}) { croak "Ambiguous rule application"; } elsif (@candidates == 0) { croak "No valid rule"; } return $candidates[0]->{action}; } sub rule_matches { my ($self,$rule,$context)=@_; $context||=$self->context; my $node=$context->current_node; my $path=$rule->{match}; # XXX check the semantic my $base_node=$node; while (1) { #warn "# Testing <$path> against @{[ $node ]} based on @{[ $base_node ]}"; if ($self->engine->matches($node,$path,$base_node)) { return 1; } if ($base_node->xpath_is_document_node) { return; } $base_node=$base_node->xpath_get_parent_node; } return; } __PACKAGE__->meta->make_immutable;no Moose;1; __END__