Mercurial > pub > Impl
view Lib/IMPL/DOM/Navigator/SchemaNavigator.pm @ 232:5c82eec23bb6
Fixed degradations due refactoring
author | sergey |
---|---|
date | Tue, 09 Oct 2012 20:12:47 +0400 |
parents | 6d8092d8ce1b |
children | 2904da230022 |
line wrap: on
line source
package IMPL::DOM::Navigator::SchemaNavigator; use strict; use warnings; use parent qw(IMPL::DOM::Navigator); use IMPL::Class::Property; use IMPL::Class::Property::Direct; require IMPL::DOM::Schema::ComplexType; require IMPL::DOM::Schema::NodeSet; require IMPL::DOM::Schema::AnyNode; __PACKAGE__->PassThroughArgs; BEGIN { public _direct property Schema => prop_get; private _direct property _historySteps => prop_all; } sub CTOR { my ($this,$schema) = @_; $this->{$Schema} = $schema; die new IMPL::InvalidArgumentException("A schema object is required") unless $schema->isa('IMPL::DOM::Schema'); } my $schemaAnyNode = IMPL::DOM::Schema::ComplexType->new(type => '::AnyNodeType', nativeType => 'IMPL::DOM::ComplexNode')->appendRange( IMPL::DOM::Schema::NodeSet->new()->appendRange( IMPL::DOM::Schema::AnyNode->new() ) ); sub NavigateName { my ($this,$name) = @_; die new IMPL::InvalidArgumentException('name is required') unless defined $name; # perform a safe navigation #return dosafe $this sub { my $steps = 0; # if we are currently in a ComplexNode, first go to it's content if ($this->Current->isa('IMPL::DOM::Schema::ComplexNode')) { # navigate to it's content # ComplexNode $this->internalNavigateNodeSet($this->Current->content); $steps ++; } # navigate to node if ( my $node = $this->Navigate( sub { $_->isa('IMPL::DOM::Schema::Node') and ( $_->name eq $name or $_->nodeName eq 'AnyNode' or ( $_->nodeName eq 'SwitchNode' and $_->selectNodes( sub { $_->name eq $name } ) ) ) }) ) { $steps ++; if ($node->nodeName eq 'AnyNode') { # if we navigate to the anynode # assume it to be ComplexType by default $node = $node->type ? $this->{$Schema}->resolveType($node->type) : $schemaAnyNode; $this->internalNavigateNodeSet($node); $steps ++; } elsif ($node->nodeName eq 'SwitchNode') { # if we are in the switchnode # navigate to the target node $node = $this->Navigate(sub { $_->name eq $name }); $steps ++; } if ($node->nodeName eq 'Node') { # if we navigate to a reference # resolve it $node = $this->{$Schema}->resolveType($node->type); $this->internalNavigateNodeSet($node); $steps++; } push @{$this->{$_historySteps}},$steps; # return found node schema return $node; } else { return; # abort navigation } #} } sub SchemaBack { my ($this) = @_; $this->Back(pop @{$this->{$_historySteps}}) if $this->{$_historySteps}; } sub SourceSchemaNode { my ($this) = @_; if ($this->Current->isa('IMPL::DOM::Schema::SimpleType') or $this->Current->isa('IMPL::DOM::Schema::ComplexType') ) { # we are redirected return $this->GetNodeFromHistory(-1); } else { return $this->Current; } } 1; __END__ =pod =head1 DESCRIPTION Помимо стандартных методов навигации позволяет переходить по элементам документа, который данной схемой описывается. =head1 METHODS =over =item C<NavigateName($name)> Переходит на схему узла с указанным именем. Тоесть использует свойство C<name>. =item C<SchemaBack> Возвращается на позицию до последней операции C<NavigateName>. Данный метод нужен посокольку операция навигации по элементам описываемым схемой может приводить к нескольким операциям навигации по самой схеме. =item C<SourceSchemaNode> Получает схему узла из которого было выполнено перенаправление, например, C<IMPL::DOM::Schema::Node>. В остальных случаях совпадает со свойством C<Current>. =back =cut