Mercurial > pub > Impl
comparison lib/IMPL/DOM/Transform/ObjectToDOM.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
| author | cin |
|---|---|
| date | Fri, 04 Sep 2015 19:40:23 +0300 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 406:f23fcb19d3c1 | 407:c6e90e02dd17 |
|---|---|
| 1 package IMPL::DOM::Transform::ObjectToDOM; | |
| 2 use strict; | |
| 3 | |
| 4 use IMPL::Const qw(:prop :access); | |
| 5 use IMPL::declare { | |
| 6 require => { | |
| 7 PropertyInfo => 'IMPL::Class::PropertyInfo', | |
| 8 Builder => 'IMPL::DOM::Navigator::Builder', | |
| 9 Exception => 'IMPL::Exception', | |
| 10 ArgumentException => '-IMPL::InvalidArgumentException', | |
| 11 OperationException => '-IMPL::InvalidOperationException' | |
| 12 }, | |
| 13 base => [ | |
| 14 'IMPL::Transform' => sub { | |
| 15 -plain => 'TransformPlain', | |
| 16 HASH => 'TransformHash', | |
| 17 -default => 'TransformDefault' | |
| 18 } | |
| 19 ], | |
| 20 props => [ | |
| 21 documentSchema => PROP_RO, | |
| 22 _schema => PROP_RW, | |
| 23 _navi => PROP_RW | |
| 24 ] | |
| 25 }; | |
| 26 | |
| 27 use constant { | |
| 28 SchemaNode => 'IMPL::DOM::Schema::Node', | |
| 29 ComplexNode => 'IMPL::DOM::Schema::ComplexNode' | |
| 30 }; | |
| 31 | |
| 32 sub CTOR { | |
| 33 my ($this,$docName,$docSchema,$transforms) = @_; | |
| 34 | |
| 35 my $docNodeSchema = $docSchema->selectSingleNode(sub { $_->isa(SchemaNode) and $_->name eq $docName } ) | |
| 36 or die OperationException->new("Can't find a node schema for the document '$docName'"); | |
| 37 | |
| 38 my $docClass = ($docNodeSchema->can('nativeType') ? $docNodeSchema->nativeType : undef) || 'IMPL::DOM::Document'; | |
| 39 | |
| 40 $this->documentSchema($docNodeSchema); | |
| 41 | |
| 42 $this->_navi( | |
| 43 Builder->new( | |
| 44 $docClass, | |
| 45 $docSchema, | |
| 46 ignoreUndefined => 1 | |
| 47 ) | |
| 48 ); | |
| 49 $this->_schema($docSchema); | |
| 50 | |
| 51 $this->_navi->NavigateCreate($docName); | |
| 52 $this->currentNode->nodeProperty(schemaDocument => $docSchema); | |
| 53 } | |
| 54 | |
| 55 sub TransformPlain { | |
| 56 my ($this,$data) = @_; | |
| 57 | |
| 58 $this->_navi->Current->nodeValue( $data ); | |
| 59 return $this->_navi->Current; | |
| 60 } | |
| 61 | |
| 62 sub currentNode { | |
| 63 shift->_navi->Current; | |
| 64 } | |
| 65 | |
| 66 sub TransformHash { | |
| 67 my ($this,$data) = @_; | |
| 68 | |
| 69 die ArgumentException->new(data => 'A HASH reference is required') | |
| 70 unless ref $data eq 'HASH'; | |
| 71 | |
| 72 return $this->StoreObject($this->currentNode,$data) | |
| 73 if !$this->currentNode->schemaType->isa(ComplexNode); | |
| 74 | |
| 75 KEYLOOP: foreach my $key (keys %$data) { | |
| 76 my $value = $data->{$key}; | |
| 77 | |
| 78 if (ref $value eq 'ARRAY') { | |
| 79 foreach my $subval (grep $_, @$value) { | |
| 80 | |
| 81 $this->_navi->saveState(); | |
| 82 | |
| 83 my $node = $this->_navi->NavigateCreate($key); | |
| 84 | |
| 85 unless(defined $node) { | |
| 86 #$this->_navi->Back(); | |
| 87 $this->_navi->restoreState(); | |
| 88 next KEYLOOP; | |
| 89 } | |
| 90 | |
| 91 $this->_navi->applyState(); | |
| 92 | |
| 93 $this->Transform($subval); | |
| 94 | |
| 95 $this->_navi->Back(); | |
| 96 } | |
| 97 } else { | |
| 98 $this->_navi->saveState(); | |
| 99 my $node = $this->_navi->NavigateCreate($key); | |
| 100 | |
| 101 unless(defined $node) { | |
| 102 #$this->_navi->Back(); | |
| 103 $this->_navi->restoreState(); | |
| 104 next KEYLOOP; | |
| 105 } | |
| 106 | |
| 107 $this->_navi->applyState(); | |
| 108 | |
| 109 $this->Transform($value); | |
| 110 | |
| 111 $this->_navi->Back(); | |
| 112 } | |
| 113 } | |
| 114 return $this->_navi->Current; | |
| 115 } | |
| 116 | |
| 117 # this method handles situatuions when a complex object must be stored in a | |
| 118 # simple node. | |
| 119 sub StoreObject { | |
| 120 my ($this,$node,$data) = @_; | |
| 121 | |
| 122 $node->nodeValue($data); | |
| 123 | |
| 124 return $node; | |
| 125 } | |
| 126 | |
| 127 sub TransformDefault { | |
| 128 my ($this,$data) = @_; | |
| 129 | |
| 130 return $this->StoreObject($this->currentNode,$data) | |
| 131 if !$this->currentNode->schemaType->isa(ComplexNode); | |
| 132 | |
| 133 if ( ref $data and eval { $data->can('GetMeta') } ) { | |
| 134 my %props = map { | |
| 135 $_->name, 1 | |
| 136 } $data->GetMeta(PropertyInfo, sub { $_->access == ACCESS_PUBLIC }, 1 ); | |
| 137 | |
| 138 | |
| 139 my %values = map { | |
| 140 $_, | |
| 141 scalar($data->$_()) | |
| 142 } keys %props; | |
| 143 | |
| 144 return $this->Transform(\%values); | |
| 145 } else { | |
| 146 die OperationException->new("Don't know how to transform $data"); | |
| 147 } | |
| 148 | |
| 149 return $this->_navi->Current; | |
| 150 } | |
| 151 | |
| 152 1; | |
| 153 | |
| 154 __END__ | |
| 155 | |
| 156 =pod | |
| 157 | |
| 158 =head1 NAME | |
| 159 | |
| 160 C<IMPL::DOM::Transform::ObjectToDOM> -преобразование объекта в DOM документ. | |
| 161 | |
| 162 =head1 SYNOPSIS | |
| 163 | |
| 164 =begin code | |
| 165 | |
| 166 use IMPL::require { | |
| 167 Schema => 'IMPL::DOM::Schema', | |
| 168 Config => 'IMPL::Config' | |
| 169 } | |
| 170 | |
| 171 my $data = { | |
| 172 id => '12313-232', | |
| 173 name => 'Peter', | |
| 174 age => 20 | |
| 175 }; | |
| 176 | |
| 177 my $schema = Schema->LoadSchema(Config->AppBase('schemas','person.xml')); | |
| 178 my $transorm = IMPL::DOM::Transform::ObjectToDOM->new('edit', $schema); | |
| 179 | |
| 180 my $form = $transform->Transform($data); | |
| 181 | |
| 182 my @errors; | |
| 183 | |
| 184 push @errors, $schema->Validate($doc); | |
| 185 | |
| 186 =end code | |
| 187 | |
| 188 =head1 DESCRIPTION | |
| 189 | |
| 190 Наследует C<IMPL::Transform>. Определяет базовые преобразования для хешей и | |
| 191 объектов, поддерживающих метаданные. | |
| 192 | |
| 193 Результатом выполнения преобразования является DOM документ. При построении | |
| 194 документа используется навигатор C<IMPL::DOM::Navigator::Builder> для | |
| 195 сопоставления схемы и свойств преобразуемого объекта. Элементы полученного | |
| 196 документа имеют ссылки на соответствующие им элементы схемы. | |
| 197 | |
| 198 После того, как документ построен и преобразование будет очищено, не останется | |
| 199 объектов, которые бы ссылались на документ со схемой, поскольку элементы схемы | |
| 200 имеют слабые ссылки на саму схему и не могут предотвратить ее удаление. | |
| 201 Для предотвращения очитски документа схемы, ссылка на него сохраняется в | |
| 202 атрибуте документа C<schemaDocument>, что обеспечит жизнь схемы на протяжении | |
| 203 жизни документа. | |
| 204 | |
| 205 Преобразование происходит рекурсивно, сначала используется метод | |
| 206 C<NavigateCreate> для создания элемента соответсвующего свойству объекта, | |
| 207 затем вызывается метод C<Transform> для преобразования значения свойства, при | |
| 208 этом C<currentNode> указывает на только что созданный элемент документа. | |
| 209 | |
| 210 Для изменения поведения преобразования можно добавлять новые обработчики, как | |
| 211 в случае со стандартным преобразованием, а также можно унаследовать текущий | |
| 212 класс для переопределения его некоторых методов. | |
| 213 | |
| 214 =head1 MEMBERS | |
| 215 | |
| 216 =head2 C<CTOR($docName,$schema)> | |
| 217 | |
| 218 Создает преобразование, при этом будет создан документ состоящий только из | |
| 219 корневого элемента с именем C<$docName> и будет найдена подходящий для него | |
| 220 элемент схемы C<$schema>. | |
| 221 | |
| 222 =over | |
| 223 | |
| 224 =item * C<$docName> | |
| 225 | |
| 226 Имя корневого узла документа, которое будет использовано для поиска | |
| 227 соответствующего элемента схемы C<$schema> | |
| 228 | |
| 229 =item * C<$schema> | |
| 230 | |
| 231 Схема, содержащая описание документа. Если в данной схеме нет описания корневого | |
| 232 элемента с именем C<$docName>, будет вызвано исключение. | |
| 233 | |
| 234 =back | |
| 235 | |
| 236 =head2 C<[get]documentSchema> | |
| 237 | |
| 238 Элемент схемы C<ComplexNode> соответствующий документу. Определяется в | |
| 239 конструкторе исходя из имени документа. | |
| 240 | |
| 241 =head2 C<[get]currentNode> | |
| 242 | |
| 243 Текущий элемент документа. После создания преобразования - это сам документ. | |
| 244 Данное свойство использется внутри преобразования для работы с текущим | |
| 245 элементом. | |
| 246 | |
| 247 =head2 C<[virtual]StoreObject($node,$data)> | |
| 248 | |
| 249 Метод, который вызывается преобразованием в случае если текущий узел документа | |
| 250 является простым, а значени которое ему соответсвует является объектом (ссылкой). | |
| 251 | |
| 252 По-умолчанию будет выполнено присваивание C<< $node->nodeValue($data) >>, однако | |
| 253 это можно заменить, например, на преобразование в строку. | |
| 254 | |
| 255 =cut |
