Mercurial > pub > Impl
view Lib/IMPL/DOM/Navigator/Builder.pm @ 284:f2a6bc5f3184
+IMPL::Object::InlineFactory: implement object factory as subroutine
author | sergey |
---|---|
date | Thu, 14 Feb 2013 19:14:02 +0400 |
parents | 4ddb27ff4a0b |
children | 010ceafd0c5a |
line wrap: on
line source
package IMPL::DOM::Navigator::Builder; use strict; use warnings; use IMPL::Const qw(:prop); use parent qw(IMPL::DOM::Navigator); use IMPL::Class::Property; require IMPL::DOM::Navigator::SchemaNavigator; require IMPL::DOM::Schema::ValidationError; use IMPL::DOM::Document; BEGIN { private _direct property _schemaNavi => PROP_RW; private _direct property _docClass => PROP_RW; public _direct property BuildErrors => PROP_RO | PROP_LIST; public _direct property Document => PROP_RO; public _direct property ignoreUndefined => PROP_RO; } our %CTOR = ( 'IMPL::DOM::Navigator' => sub { IMPL::DOM::Document->Empty; } ); sub CTOR { my ($this,$docClass,$schema,%opts) = @_; $this->{$_docClass} = $docClass; $this->{$_schemaNavi} = $schema ? IMPL::DOM::Navigator::SchemaNavigator->new($schema) : undef; $this->{$ignoreUndefined} = $opts{ignoreUndefined} if $opts{ignoreUndefined}; } sub NavigateCreate { my ($this,$nodeName,%props) = @_; if (my $schemaNode = $this->{$_schemaNavi}->NavigateName($nodeName)) { my $class = $schemaNode->can('nativeType') ? $schemaNode->nativeType || 'IMPL::DOM::Node' : 'IMPL::DOM::Node'; my $schemaSource = $this->{$_schemaNavi}->SourceSchemaNode; my @errors = $this->inflateProperties($schemaNode,\%props); $props{schema} = $schemaNode; $props{schemaSource} = $schemaSource; my $node; if (! $this->{$Document}) { $node = $this->{$Document} = $this->{$_docClass}->new(nodeName => $nodeName,%props); $this->_initNavigator($node); } else { die new IMPL::InvalidOperationException('Can\'t create a second top level element') unless $this->Current; $node = $this->{$Document}->Create($nodeName,$class,\%props); $this->Current->appendChild($node); $this->internalNavigateNodeSet($node); } if (@errors) { $this->BuildErrors->Append( map { IMPL::DOM::Schema::ValidationError->new( node => $node, source => $schemaSource, schema => $schemaNode, message => $schemaNode->messageInflateError, error => $_ ) } @errors ); } return $node; } else { die new IMPL::InvalidOperationException("The specified node is undefined", $nodeName) if !$this->ignoreUndefined; return; } } sub inflateProperties { my ($this,$schemaNode,$refProps) = @_; my @errors; foreach my $schemaProp ( $schemaNode->selectNodes('Property') ) { next if not exists $refProps->{$schemaProp->name}; my $result = eval {$schemaProp->inflateValue($refProps->{$schemaProp->name}) }; if (my $e = $@) { push @errors, $e; } else { $refProps->{$schemaProp->name} = $result; } } return @errors; } sub inflateValue { my ($this,$value,$node,$strict) = @_; $strict ||= 0; $node ||= $this->Current; my $nodeSchema = $this->{$_schemaNavi}->Current; my $result = eval { $nodeSchema->inflateValue($value) }; if (my $e=$@) { $this->BuildErrors->Append(new IMPL::DOM::Schema::ValidationError( schema => $nodeSchema, node => $node, error => $e, message => $nodeSchema->messageInflateError, source => $this->{$_schemaNavi}->SourceSchemaNode )); return $strict ? undef : $value ; } else { return $result; } } sub Back { my ($this) = @_; $this->{$_schemaNavi}->SchemaBack(); $this->SUPER::Back(); } sub saveState { my ($this) = @_; $this->{$_schemaNavi}->saveState; $this->SUPER::saveState; } sub restoreState { my ($this) = @_; $this->{$_schemaNavi}->restoreState; $this->SUPER::restoreState; } #compatibility sub buildErrors { goto &BuildErrors; } sub document { goto &Document; } 1; __END__ =pod =head1 NAME C< IMPL::DOM::Navigator::Builder > - Навигатор, строящий документ по указанной схеме. =head1 SYNOPSIS =begin code my $builder = new IMPL::DOM::Navigator::Builder(new MyApp::Document,$schema); my $reader = new IMPL::DOM::XMLReader(Navigator => $builder); $reader->ParseFile("document.xml"); my @errors = $schema->Validate($builder->Document); =end code =head1 DESCRIPTION Построитель DOM документов по указанной схеме. Обычно используется в связке с объектами для чтения такими как C<IMPL::DOM::XMLReader>. =head1 MEMBERS =head2 C< CTOR($classDocument,$schema, %opts) > Создает новый объект, принимает на вход класс документа (или фабрику, например L<IMPL::Object::Factory>) и схему. В процессе процедуры построения документа будет создан объект документа. Необязательные именованные параметры =over =item C<ignoreUndefined> C<NavigateCreate> не будет вызывать исключение, если запрашиваемый узел не найден в схеме, но будет возвращать C<undef>. =back =head2 C< NavigateCreate($nodeName,%props) > Создает новый узел с указанным именем и переходит в него. В случае если в схеме подходящий узел не найден, то вызывается исключение или будет возвращено C<undef> см. C<ignoreUndefined>. При этом по имени узла ищется его схема, после чего определяется класс для создания экземпляра узла и созданный узел доавляется в документ. При создании нового узла используется метод документа C<< IMPL::DOM::Document->Create >> Свойства узла передаются при создании через параметр C<%props>, но имя создаваемого узла НЕ может быть переопределено свойством C<nodeName>, оно будет проигнорировано. Свойства узла будут преобразованы при помощи заданных в схеме заполнителей C<inflator>. =head2 C<[get]document > Свойство, которое содержит документ по окончании процедуры построения. =head2 C<[get]buildErrors> Ошибки, возникшие в процессе построения документа. =head2 C<[get]ignoreUndefined> Опция, заданная при создании построителя, отвечающая за обработку узлов не найденных в схеме. =cut