Mercurial > pub > Impl
view lib/IMPL/DOM/Navigator/SchemaNavigator.pm @ 416:cc2cf8c0edc2 ref20150831
sync
author | cin |
---|---|
date | Thu, 29 Oct 2015 03:50:25 +0300 |
parents | c6e90e02dd17 |
children |
line wrap: on
line source
package IMPL::DOM::Navigator::SchemaNavigator; use strict; use warnings; use IMPL::Class::Property; require IMPL::DOM::Schema::ComplexType; require IMPL::DOM::Schema::NodeSet; require IMPL::DOM::Schema::AnyNode; use IMPL::declare { base => [ 'IMPL::DOM::Navigator' => '@_' ] }; BEGIN { public _direct property Schema => prop_get; private _direct property _historySteps => prop_all; } sub CTOR { my ($this,$schema) = @_; $this->{$Schema} = $schema->isa('IMPL::DOM::Schema::ComplexNode') ? $schema->document : $schema; die new IMPL::InvalidArgumentException("A schema object is required") unless ref $this->{$Schema} && eval { $this->{$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 ++; } die IMPL::Exception->new("A node is expected") unless $node; 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; } } sub schema { goto &Schema; } 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