changeset 34:a8086f85a571

Dom Builder
author Sergey
date Mon, 16 Nov 2009 18:39:25 +0300
parents 0004faa276dc
children f25d021780b3
files Lib/IMPL/DOM/Navigator.pm Lib/IMPL/DOM/Navigator/Builder.pm Lib/IMPL/DOM/Navigator/SchemaNavigator.pm Lib/IMPL/DOM/Node.pm Lib/IMPL/DOM/Schema.pm _test/DOM.t _test/Test/DOM/Builder.pm
diffstat 7 files changed, 154 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/DOM/Navigator.pm	Mon Nov 09 16:49:39 2009 +0300
+++ b/Lib/IMPL/DOM/Navigator.pm	Mon Nov 16 18:39:25 2009 +0300
@@ -222,15 +222,21 @@
 Состоянием навигатора является текущий набор узлов, позиция в данном наборе,
 а также запрос по которому были получены данные результаты.
 
+Если при навигации указан путь сосящий из нескольких фильтров, то он разбивается
+этапы простой навигации по кадой из частей пути. На каждом элементарном этапе
+навигации образуется ряд альтернатив, и при каждом следующем этапе навигации
+альтернативы предыдущих этапов могут перебираться, до получения положительного
+результата навигации, в противном случае навигация считается невозможной.
+
 =head1 METHODS
 
 =over
 
-=item C<$obj->new($nodeStart)>
+=item C<<$obj->new($nodeStart)>>
 
 Создает объект навигатора с указанной начальной позицией.
 
-=item C<$obj->Navigate([$query,...])>
+=item C<<$obj->Navigate([$query,...])>>
 
 Перейти в новый узел используя запрос C<$query>. На данный момент запросом может
 быть только имя узла и будет взят только первый узел. Если по запросу ничего не
@@ -238,12 +244,16 @@
 
 Возвращает либо новый узел в который перешли, либо C<undef>.
 
-=item C<$obj->Back()>
+=item C<<$obj->Back()>>
 
 Возвращается в предыдущий узел, если таковой есть.
 
 Возвращает либо узел в который перешли, либо C<undef>.
 
+=item C<<$obj->advanceNavigator()>>
+
+Переходит в следующую альтернативу, соответствующую текущему запросу.
+
 =back
 
 =cut
\ No newline at end of file
--- a/Lib/IMPL/DOM/Navigator/Builder.pm	Mon Nov 09 16:49:39 2009 +0300
+++ b/Lib/IMPL/DOM/Navigator/Builder.pm	Mon Nov 16 18:39:25 2009 +0300
@@ -2,36 +2,55 @@
 use strict;
 use warnings;
 
-use base qw(IMPL::DOM::Navigator);
+use base qw(IMPL::Object);
 use IMPL::Class::Property;
 use IMPL::Class::Property::Direct;
 require IMPL::DOM::Navigator::SchemaNavigator;
 
 BEGIN {
-    protected _direct property _schemaNavi => prop_all;
+    private _direct property _schemaNavi => prop_all;
+    private _direct property _nodesPath => prop_all;
+    private _direct property _nodeCurrent => prop_all;
+    private _direct property _docClass => prop_all
     public _direct property Document => prop_get | owner_set;
 }
 
-our %CTOR = (
-    'IMPL::DOM::Navigator' => sub { $_[0] } # IMPL::DOM::Navigator($domDocument)
-);
-
 sub CTOR {
-    my ($this,$domDocument,$schema) = @_;
+    my ($this,$docClass,$schema) = @_;
     
-    $this->{$Document} = $domDocument;
-    $this->{$_schemaNavi} = $schema;
+    $this->{$_docClass} = $docClass;
+    $this->{$_schemaNavi} = IMPL::DOM::Navigator::SchemaNavigator->new($schema);
 }
 
 sub NavigateCreate {
     my ($this,$nodeName,%props) = @_;
     
-    
+    if (my $schemaNode = $this->{$_schemaNavi}->NavigateName($nodeName)) {
+        my $class = $schemaNode->can('nativeType') ? $schemaNode->nativeType : 'IMPL::DOM::Node';
+        
+        my $node;
+        if (! $this->{$Document}) {
+            $node = $this->{$Document} = $this->{$_docClass}->new(nodeName => $nodeName,%props);
+        } else {
+            die new IMPL::InvalidOperationException('Can\t create a second top level element');
+            $node = $this->{$Document}->Create($nodeName,$class,\%props);
+            push @{$this->{$_nodesPath}}, $this->{$_nodeCurrent};
+            $this->{$_nodeCurrent}->appendChild($node);
+        }
+        
+        $this->{$_nodeCurrent} = $node;
+        
+        return $node;
+    } else {
+        die new IMPL::InvalidOperationException("The specified node is undefined", $nodeName);
+    }
 }
 
 sub Back {
     my ($this) = @_;
     
+    $this->{$_schemaNavi}->SchemaBack();
+    $this->{$_nodeCurrent} = pop @{$this->{$_nodesPath}};
 }
 
 1;
@@ -67,24 +86,11 @@
 Создает новый узел с указанным именем и переходит в него. В случае если в схеме
 подходящий узел не найден, то вызывается исключение.
 
-При этом схема узла производится по имени, после чего определяется класс для
-создания узла, по алгоритму
-
-=over
-
-=item Проверяется свойство C<type> и если оно есть, то используется оно
+При этом по имени узла ищется его схема, после чего определяется класс для
+создания экземпляра и созданный узел доавляется в документ.
 
-=item Проверяется свойство C<type> самого элемента схемы, если оно есть, то
-используется оно.
-
-=item В качестве класса берется имя узла
-
-=back
-
-Также имя создаваемого узла может быть переопределено свойством C<nodeName>.
-
-Т.о. узел вида C<< <ComplexNode nodeName="Box" type="Container"> >> приведет
-к созданию C<< Container->new(nodeName => 'Box') >>.
+Также имя создаваемого узла НЕ может быть переопределено свойством nodeName, оно
+будет проигнорировано.
 
 =back
 
--- a/Lib/IMPL/DOM/Navigator/SchemaNavigator.pm	Mon Nov 09 16:49:39 2009 +0300
+++ b/Lib/IMPL/DOM/Navigator/SchemaNavigator.pm	Mon Nov 16 18:39:25 2009 +0300
@@ -34,6 +34,8 @@
 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 = 1;
@@ -107,7 +109,9 @@
 
 =item C<< $navi->NavigateName($name) >>
 
-Переходит на схему узла с указанным именем. Тоесть использует свойство C<name>
+Переходит на схему узла с указанным именем. Тоесть использует свойство C<name>.
+В данном случае всегда происходит безопасная навигация, тоесть в случае неудачи,
+навигатор останется на прежней позиции.
 
 =item C<< $navi->SchemaBack >>
 
--- a/Lib/IMPL/DOM/Node.pm	Mon Nov 09 16:49:39 2009 +0300
+++ b/Lib/IMPL/DOM/Node.pm	Mon Nov 16 18:39:25 2009 +0300
@@ -24,8 +24,10 @@
 sub CTOR {
     my ($this,%args) = @_;
     
-    $this->nodeName($args{nodeName}) or die new IMPL::InvalidArgumentException("A name is required");
-    $this->nodeValue($args{nodeValue});
+    $this->nodeName(delete $args{nodeName}) or die new IMPL::InvalidArgumentException("A name is required");
+    $this->nodeValue(delete $args{nodeValue});
+    
+    $this->{$_propertyMap} = \%args;
 }
 
 sub insertNode {
--- a/Lib/IMPL/DOM/Schema.pm	Mon Nov 09 16:49:39 2009 +0300
+++ b/Lib/IMPL/DOM/Schema.pm	Mon Nov 16 18:39:25 2009 +0300
@@ -23,12 +23,20 @@
 
 BEGIN {
     private _direct property _TypesMap => prop_all;
+    public _direct property BaseSchemas => prop_get | owner_set;
+    private _direct property _Validators => prop_all;
 }
 
 sub resolveType {
     $_[0]->{$_TypesMap}->{$_[1]};
 }
 
+#sub Create {
+#    my ($this,$nodeName,$class,$refArgs) = @_;
+    
+#    goto &SUPER::Create unless $class eq 'IMPL::DOM::Schema::Validator'
+#}
+
 sub Process {
     my ($this) = @_;
     
@@ -41,7 +49,7 @@
     if ( my ($schemaNode) = $this->selectNodes(sub { $_[0]->name eq $node->nodeName })) {
         $schemaNode->Validate($node);
     } else {
-        return IMPL::DOM::Schema::ValidationError(Message=> "A specified document doesn't match the schema");
+        return new IMPL::DOM::Schema::ValidationError(Message=> "A specified document doesn't match the schema");
     }
 }
 
@@ -99,7 +107,7 @@
                     IMPL::DOM::Schema::Node->new(name => 'NodeSet', type => 'NodeSet'),
                     IMPL::DOM::Schema::Node->new(name => 'NodeList',type => 'NodeList'),
                 ),
-                IMPL::DOM::Schema::AnyNode->new(maxOccur => 'unbounded', minOccur => 0)
+                IMPL::DOM::Schema::AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator')
             ),
             new IMPL::DOM::Schema::Property(name => 'type')
         ),
@@ -109,18 +117,24 @@
                     IMPL::DOM::Schema::Node->new(name => 'NodeSet', type => 'NodeSet'),
                     IMPL::DOM::Schema::Node->new(name => 'NodeList',type => 'NodeList'),
                 ),
-                IMPL::DOM::Schema::AnyNode->new(maxOccur => 'unbounded', minOccur => 0)
+                IMPL::DOM::Schema::AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator')
             ),
             new IMPL::DOM::Schema::Property(name => 'name')
         ),
         IMPL::DOM::Schema::ComplexType->new(type => 'SimpleType', nativeType => 'IMPL::DOM::Schema::SimpleType')->appendRange(
             IMPL::DOM::Schema::NodeSet->new()->appendRange(
-                IMPL::DOM::Schema::AnyNode->new(maxOccur => 'unbounded', minOccur => 0)
+                IMPL::DOM::Schema::AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator')
             ),
             new IMPL::DOM::Schema::Property(name => 'type')
         ),
         IMPL::DOM::Schema::ComplexType->new(type => 'SimpleNode', nativeType => 'IMPL::DOM::Schema::SimpleNode')->appendRange(
             IMPL::DOM::Schema::NodeSet->new()->appendRange(
+                IMPL::DOM::Schema::AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator')
+            ),
+            new IMPL::DOM::Schema::Property(name => 'name')
+        ),
+        IMPL::DOM::Schema::ComplexType->new(type => 'Validator', nativeType => 'IMPL::DOM::Schema::Validator')->appendRange(
+            IMPL::DOM::Schema::NodeList->new()->appendRange(
                 IMPL::DOM::Schema::AnyNode->new(maxOccur => 'unbounded', minOccur => 0)
             ),
             new IMPL::DOM::Schema::Property(name => 'name')
@@ -185,12 +199,11 @@
         </NodeSet>
     </ComplexType>
     
-    
     <ComplexType type="ComplexType">
         <NodeList>
             <Node name="NodeSet" type="NodeContainer" minOcuur=0/>
             <Node name="NodeList" type="NodeContainer" minOccur=0/>
-            <AnyNode minOccur="0" maxOccur="unbounded"/>
+            <AnyNode minOccur="0" maxOccur="unbounded"  type="Validator"/>
         </NodeList>
     </ComplexType>
     
@@ -198,18 +211,24 @@
         <NodeList>
             <Node name="NodeSet" type="NodeContainer" minOcuur=0/>
             <Node name="NodeList" type="NodeContainer" minOccur=0/>
-            <AnyNode minOccur="0" maxOccur="unbounded"/>
+            <AnyNode minOccur="0" maxOccur="unbounded"  type="Validator"/>
         </NodeList>
     </ComplexType>
     
     <ComplexType type="SimpleNode">
         <NodeSet>
-            <AnyNode minOccur=0 maxOccur="unbounded"/>
+            <AnyNode minOccur=0 maxOccur="unbounded" type="Validator"/>
         </NodeSet>
     </ComplexType>
     
     <ComplexType type="SimpleType">
         <NodeSet>
+            <AnyNode minOccur=0 maxOccur="unbounded" type="Validator"/>
+        </NodeSet>
+    </ComplexType>
+    
+    <ComplexType type="Validator">
+        <NodeSet>
             <AnyNode minOccur=0 maxOccur="unbounded"/>
         </NodeSet>
     </ComplexType>
--- a/_test/DOM.t	Mon Nov 09 16:49:39 2009 +0300
+++ b/_test/DOM.t	Mon Nov 16 18:39:25 2009 +0300
@@ -10,6 +10,7 @@
     Test::DOM::Node
     Test::DOM::Navigator
     Test::DOM::Schema
+    Test::DOM::Builder
 );
 
 $plan->AddListener(new IMPL::Test::TAPListener);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_test/Test/DOM/Builder.pm	Mon Nov 16 18:39:25 2009 +0300
@@ -0,0 +1,71 @@
+package Test::DOM::Builder;
+use strict;
+use warnings;
+
+use base qw(IMPL::Test::Unit);
+__PACKAGE__->PassThroughArgs;
+
+use IMPL::Class::Property;
+use IMPL::Test qw(test failed shared);
+
+require IMPL::DOM::Schema;
+require IMPL::DOM::Navigator::Builder;
+require IMPL::DOM::Document;
+
+BEGIN {
+    public property schemaDoc => prop_all;
+}
+
+sub CTOR {
+    my ($this) = @_;
+    
+    my $schema = new IMPL::DOM::Schema;
+    $schema->appendRange(
+        IMPL::DOM::Schema::ComplexNode->new( name => 'personInfo' )->appendRange(
+            IMPL::DOM::Schema::NodeSet->new()->appendRange(
+                new IMPL::DOM::Schema::SimpleNode( name => 'firstName' ),
+                new IMPL::DOM::Schema::SimpleNode( name => 'lastName' ),
+                new IMPL::DOM::Schema::ComplexNode( name => 'address' )->appendRange(
+                    IMPL::DOM::Schema::NodeSet->new()->appendRange(
+                        new IMPL::DOM::Schema::SimpleNode( name => 'street' ),
+                        new IMPL::DOM::Schema::SimpleNode( name => 'line', minOccur => 0 )
+                    )
+                )
+            )
+        )
+    );
+    
+    $this->schemaDoc( $schema );
+}
+
+test CreateBuilder => sub {
+    my ($this) = @_;
+    
+    my $builder = IMPL::DOM::Navigator::Builder->new(
+        new IMPL::DOM::Document(nodeName => 'personInfo'),
+        $this->schemaDoc
+    );
+};
+
+test BuildDocument => sub {
+    my ($this) = @_;
+    
+    my $builder = IMPL::DOM::Navigator::Builder->new(
+        'IMPL::DOM::Document',
+        $this->schemaDoc
+    );
+    
+    $builder->NavigateCreate('personInfo', version => '1');
+        $builder->NavigateCreate('firstName')->nodeValue('Nemo');
+        $builder->Back();
+        $builder->NavigateCreate('address', local => 1);
+            $builder->NavigateCreate('street')->nodeValue('Hellroad');
+            $builder->Back();
+        $builder->Back();
+    $builder->Back();
+    
+    return 1;
+};
+
+
+1;