# HG changeset patch # User Sergey # Date 1258385965 -10800 # Node ID a8086f85a5717efdd2fc59e6b8467feddc8dc7ab # Parent 0004faa276dc44d462fbf6e38092c9b024227102 Dom Builder diff -r 0004faa276dc -r a8086f85a571 Lib/IMPL/DOM/Navigator.pm --- 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. -=item C<$obj->Back()> +=item C<<$obj->Back()>> Возвращается в предыдущий узел, если таковой есть. Возвращает либо узел в который перешли, либо C. +=item C<<$obj->advanceNavigator()>> + +Переходит в следующую альтернативу, соответствующую текущему запросу. + =back =cut \ No newline at end of file diff -r 0004faa276dc -r a8086f85a571 Lib/IMPL/DOM/Navigator/Builder.pm --- 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 и если оно есть, то используется оно +При этом по имени узла ищется его схема, после чего определяется класс для +создания экземпляра и созданный узел доавляется в документ. -=item Проверяется свойство C самого элемента схемы, если оно есть, то -используется оно. - -=item В качестве класса берется имя узла - -=back - -Также имя создаваемого узла может быть переопределено свойством C. - -Т.о. узел вида C<< >> приведет -к созданию C<< Container->new(nodeName => 'Box') >>. +Также имя создаваемого узла НЕ может быть переопределено свойством nodeName, оно +будет проигнорировано. =back diff -r 0004faa276dc -r a8086f85a571 Lib/IMPL/DOM/Navigator/SchemaNavigator.pm --- 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 +Переходит на схему узла с указанным именем. Тоесть использует свойство C. +В данном случае всегда происходит безопасная навигация, тоесть в случае неудачи, +навигатор останется на прежней позиции. =item C<< $navi->SchemaBack >> diff -r 0004faa276dc -r a8086f85a571 Lib/IMPL/DOM/Node.pm --- 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 { diff -r 0004faa276dc -r a8086f85a571 Lib/IMPL/DOM/Schema.pm --- 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 @@ - - + @@ -198,18 +211,24 @@ - + - + + + + + + + diff -r 0004faa276dc -r a8086f85a571 _test/DOM.t --- 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); diff -r 0004faa276dc -r a8086f85a571 _test/Test/DOM/Builder.pm --- /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;