| 
407
 | 
     1 package IMPL::DOM::Navigator::SchemaNavigator;
 | 
| 
 | 
     2 use strict;
 | 
| 
 | 
     3 use warnings;
 | 
| 
 | 
     4 
 | 
| 
 | 
     5 use IMPL::Class::Property;
 | 
| 
 | 
     6 
 | 
| 
 | 
     7 require IMPL::DOM::Schema::ComplexType;
 | 
| 
 | 
     8 require IMPL::DOM::Schema::NodeSet;
 | 
| 
 | 
     9 require IMPL::DOM::Schema::AnyNode;
 | 
| 
 | 
    10 
 | 
| 
 | 
    11 use IMPL::declare {
 | 
| 
 | 
    12     base => [
 | 
| 
 | 
    13         'IMPL::DOM::Navigator' => '@_'
 | 
| 
 | 
    14     ]
 | 
| 
 | 
    15 };
 | 
| 
 | 
    16 
 | 
| 
 | 
    17 BEGIN {
 | 
| 
 | 
    18     public _direct property Schema => prop_get;
 | 
| 
 | 
    19     private _direct property _historySteps => prop_all;
 | 
| 
 | 
    20 }
 | 
| 
 | 
    21 
 | 
| 
 | 
    22 sub CTOR {
 | 
| 
 | 
    23     my ($this,$schema) = @_;
 | 
| 
 | 
    24     
 | 
| 
 | 
    25     $this->{$Schema} = $schema->isa('IMPL::DOM::Schema::ComplexNode') ? $schema->document : $schema;
 | 
| 
 | 
    26     
 | 
| 
 | 
    27     die new IMPL::InvalidArgumentException("A schema object is required") unless ref $this->{$Schema} && eval { $this->{$Schema}->isa('IMPL::DOM::Schema') };
 | 
| 
 | 
    28 }
 | 
| 
 | 
    29 
 | 
| 
 | 
    30 my $schemaAnyNode = IMPL::DOM::Schema::ComplexType->new(type => '::AnyNodeType', nativeType => 'IMPL::DOM::ComplexNode')->appendRange(
 | 
| 
 | 
    31     IMPL::DOM::Schema::NodeSet->new()->appendRange(
 | 
| 
 | 
    32         IMPL::DOM::Schema::AnyNode->new()
 | 
| 
 | 
    33     )
 | 
| 
 | 
    34 );
 | 
| 
 | 
    35 
 | 
| 
 | 
    36 sub NavigateName {
 | 
| 
 | 
    37     my ($this,$name) = @_;
 | 
| 
 | 
    38     
 | 
| 
 | 
    39     die new IMPL::InvalidArgumentException('name is required') unless defined $name;
 | 
| 
 | 
    40     
 | 
| 
 | 
    41     # perform a safe navigation
 | 
| 
 | 
    42     #return dosafe $this sub {
 | 
| 
 | 
    43         my $steps = 0;
 | 
| 
 | 
    44         # if we are currently in a ComplexNode, first go to it's content
 | 
| 
 | 
    45         if ($this->Current->isa('IMPL::DOM::Schema::ComplexNode')) {
 | 
| 
 | 
    46             # navigate to it's content
 | 
| 
 | 
    47             # ComplexNode
 | 
| 
 | 
    48             $this->internalNavigateNodeSet($this->Current->content);
 | 
| 
 | 
    49             $steps ++;
 | 
| 
 | 
    50         }
 | 
| 
 | 
    51         
 | 
| 
 | 
    52         # navigate to node
 | 
| 
 | 
    53         if (
 | 
| 
 | 
    54             my $node = $this->Navigate( sub {
 | 
| 
 | 
    55                 $_->isa('IMPL::DOM::Schema::Node') and (
 | 
| 
 | 
    56                     $_->name eq $name
 | 
| 
 | 
    57                     or
 | 
| 
 | 
    58                     $_->nodeName eq 'AnyNode'
 | 
| 
 | 
    59                     or
 | 
| 
 | 
    60                     ( $_->nodeName eq 'SwitchNode' and $_->selectNodes( sub { $_->name eq $name } ) )
 | 
| 
 | 
    61                 )
 | 
| 
 | 
    62             })
 | 
| 
 | 
    63         ) {
 | 
| 
 | 
    64             $steps ++;
 | 
| 
 | 
    65             if ($node->nodeName eq 'AnyNode') {
 | 
| 
 | 
    66                 # if we navigate to the anynode
 | 
| 
 | 
    67                 # assume it to be ComplexType by default
 | 
| 
 | 
    68                 $node = $node->type ? $this->{$Schema}->resolveType($node->type) : $schemaAnyNode;
 | 
| 
 | 
    69                 $this->internalNavigateNodeSet($node);
 | 
| 
 | 
    70                 $steps ++;
 | 
| 
 | 
    71             } elsif ($node->nodeName eq 'SwitchNode') {
 | 
| 
 | 
    72                 # if we are in the switchnode
 | 
| 
 | 
    73                 # navigate to the target node
 | 
| 
 | 
    74                 $node = $this->Navigate(sub { $_->name eq $name });
 | 
| 
 | 
    75                 $steps ++;
 | 
| 
 | 
    76             }
 | 
| 
 | 
    77             
 | 
| 
 | 
    78             die IMPL::Exception->new("A node is expected")
 | 
| 
 | 
    79                 unless $node;
 | 
| 
 | 
    80             if ($node->nodeName eq 'Node') {
 | 
| 
 | 
    81                 # if we navigate to a reference
 | 
| 
 | 
    82                 # resolve it
 | 
| 
 | 
    83                 $node = $this->{$Schema}->resolveType($node->type);
 | 
| 
 | 
    84                 $this->internalNavigateNodeSet($node);
 | 
| 
 | 
    85                 $steps++;
 | 
| 
 | 
    86             } 
 | 
| 
 | 
    87             
 | 
| 
 | 
    88             push @{$this->{$_historySteps}},$steps;
 | 
| 
 | 
    89             
 | 
| 
 | 
    90             # return found node schema
 | 
| 
 | 
    91             return $node;
 | 
| 
 | 
    92         } else {
 | 
| 
 | 
    93             return; # abort navigation
 | 
| 
 | 
    94         }
 | 
| 
 | 
    95     #}
 | 
| 
 | 
    96 }
 | 
| 
 | 
    97 
 | 
| 
 | 
    98 sub SchemaBack {
 | 
| 
 | 
    99     my ($this) = @_;
 | 
| 
 | 
   100     
 | 
| 
 | 
   101     $this->Back(pop @{$this->{$_historySteps}}) if $this->{$_historySteps};
 | 
| 
 | 
   102 }
 | 
| 
 | 
   103 
 | 
| 
 | 
   104 sub SourceSchemaNode {
 | 
| 
 | 
   105     my ($this) = @_;
 | 
| 
 | 
   106     
 | 
| 
 | 
   107     if ($this->Current->isa('IMPL::DOM::Schema::SimpleType') or
 | 
| 
 | 
   108         $this->Current->isa('IMPL::DOM::Schema::ComplexType')
 | 
| 
 | 
   109     ) {
 | 
| 
 | 
   110         # we are redirected
 | 
| 
 | 
   111         return $this->GetNodeFromHistory(-1);
 | 
| 
 | 
   112     } else {
 | 
| 
 | 
   113         return $this->Current;
 | 
| 
 | 
   114     }
 | 
| 
 | 
   115 }
 | 
| 
 | 
   116 
 | 
| 
 | 
   117 sub schema {
 | 
| 
 | 
   118 	goto &Schema;
 | 
| 
 | 
   119 }
 | 
| 
 | 
   120 
 | 
| 
 | 
   121 1;
 | 
| 
 | 
   122 __END__
 | 
| 
 | 
   123 
 | 
| 
 | 
   124 =pod
 | 
| 
 | 
   125 
 | 
| 
 | 
   126 =head1 DESCRIPTION
 | 
| 
 | 
   127 
 | 
| 
 | 
   128 Помимо стандартных методов навигации позволяет переходить по элементам документа,
 | 
| 
 | 
   129 который данной схемой описывается.
 | 
| 
 | 
   130 
 | 
| 
 | 
   131 =head1 METHODS
 | 
| 
 | 
   132 
 | 
| 
 | 
   133 =over
 | 
| 
 | 
   134 
 | 
| 
 | 
   135 =item C<NavigateName($name)>
 | 
| 
 | 
   136 
 | 
| 
 | 
   137 Переходит на схему узла с указанным именем. Тоесть использует свойство C<name>.
 | 
| 
 | 
   138 
 | 
| 
 | 
   139 =item C<SchemaBack>
 | 
| 
 | 
   140 
 | 
| 
 | 
   141 Возвращается на позицию до последней операции C<NavigateName>. Данный метод нужен
 | 
| 
 | 
   142 посокольку операция навигации по элементам описываемым схемой может приводить к
 | 
| 
 | 
   143 нескольким операциям навигации по самой схеме.
 | 
| 
 | 
   144 
 | 
| 
 | 
   145 =item C<SourceSchemaNode>
 | 
| 
 | 
   146 
 | 
| 
 | 
   147 Получает схему узла из которого было выполнено перенаправление, например, C<IMPL::DOM::Schema::Node>.
 | 
| 
 | 
   148 В остальных случаях совпадает со свойством C<Current>.
 | 
| 
 | 
   149 
 | 
| 
 | 
   150 =back
 | 
| 
 | 
   151 
 | 
| 
 | 
   152 =cut
 |