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