Mercurial > pub > Impl
diff lib/IMPL/DOM/Navigator/SchemaNavigator.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
author | cin |
---|---|
date | Fri, 04 Sep 2015 19:40:23 +0300 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/IMPL/DOM/Navigator/SchemaNavigator.pm Fri Sep 04 19:40:23 2015 +0300 @@ -0,0 +1,152 @@ +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