Mercurial > pub > Impl
annotate Lib/IMPL/DOM/Navigator.pm @ 121:92c850d0bdb9
Minor changes
Fixed bug with templates base in the PageFormat module
Added an ability to remove current security context with a specified
| author | wizard |
|---|---|
| date | Tue, 08 Jun 2010 03:38:10 +0400 |
| parents | 196bf443b5e1 |
| children | a7efb3117295 |
| rev | line source |
|---|---|
| 49 | 1 package IMPL::DOM::Navigator; |
| 2 use strict; | |
| 3 use warnings; | |
| 4 | |
| 5 use base qw(IMPL::Object); | |
| 6 use IMPL::Class::Property; | |
| 7 use IMPL::Class::Property::Direct; | |
| 8 BEGIN { | |
| 9 private _direct property _path => prop_all; | |
| 10 private _direct property _state => prop_all; | |
| 11 private _direct property _savedstates => prop_all; | |
| 12 public property Current => {get => \&_getCurrent}; | |
| 13 } | |
| 14 | |
| 15 sub CTOR { | |
| 16 my ($this,$CurrentNode) = @_; | |
| 17 | |
| 18 die IMPL::InvalidArgumentException("A starting node is a required paramater") unless $CurrentNode; | |
| 19 | |
| 20 $this->{$_state} = { alternatives => [ $CurrentNode ], current => 0 }; | |
| 21 } | |
| 22 | |
| 23 sub _initNavigator { | |
| 24 my ($this,$CurrentNode) = @_; | |
| 25 | |
| 26 die IMPL::InvalidArgumentException("A starting node is a required paramater") unless $CurrentNode; | |
| 27 | |
| 28 $this->{$_state} = { alternatives => [ $CurrentNode ], current => 0 }; | |
| 29 delete $this->{$_path}; | |
| 30 delete $this->{$_savedstates}; | |
| 31 } | |
| 32 | |
| 33 sub _getCurrent { | |
| 34 $_[0]->{$_state}{alternatives}[$_[0]->{$_state}{current}] | |
| 35 } | |
| 36 | |
| 37 sub Navigate { | |
| 38 my ($this,@path) = @_; | |
| 39 | |
| 40 return unless @path; | |
| 41 | |
| 42 my $node; | |
| 43 | |
| 44 foreach my $query (@path) { | |
| 45 if (my $current = $this->Current) { | |
| 46 | |
| 47 my @alternatives = $current->selectNodes($query); | |
| 48 | |
| 49 unless (@alternatives) { | |
| 50 $current = $this->advanceNavigator or return undef; | |
| 51 @alternatives = $current->selectNodes($query); | |
| 52 } | |
| 53 | |
| 54 push @{$this->{$_path}},$this->{$_state}; | |
| 55 $this->{$_state} = { | |
| 56 alternatives => \@alternatives, | |
| 57 current => 0, | |
| 58 query => $query | |
| 59 }; | |
| 60 | |
| 61 $node = $alternatives[0]; | |
| 62 } else { | |
| 63 return undef; | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 $node; | |
| 68 } | |
| 69 | |
| 70 sub selectNodes { | |
| 71 my ($this,@path) = @_; | |
| 72 | |
| 73 return internalSelectNodes($this->Current,@path); | |
| 74 } | |
| 75 | |
| 76 sub internalSelectNodes { | |
| 77 my $node = shift; | |
| 78 my $query = shift; | |
| 79 | |
| 80 if (@_) { | |
| 81 return map internalSelectNodes($_,@_), $node->selectNodes($query); | |
| 82 } else { | |
| 83 return $node->selectNodes($query); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 sub internalNavigateNodeSet { | |
| 88 my ($this,@nodeSet) = @_; | |
| 89 | |
| 90 push @{$this->{$_path}}, $this->{$_state}; | |
| 91 | |
| 92 $this->{$_state} = { | |
| 93 alternatives => \@nodeSet, | |
| 94 current => 0 | |
| 95 }; | |
| 96 | |
| 97 $nodeSet[0]; | |
| 98 } | |
| 99 | |
| 100 sub fetch { | |
| 101 my ($this) = @_; | |
| 102 | |
| 103 my $result = $this->Current; | |
| 104 $this->advanceNavigator; | |
| 105 return $result; | |
| 106 } | |
| 107 | |
| 108 sub advanceNavigator { | |
| 109 my ($this) = @_; | |
| 110 | |
| 111 $this->{$_state}{current}++; | |
| 112 | |
| 113 if (@{$this->{$_state}{alternatives}} <= $this->{$_state}{current}) { | |
| 114 if ( exists $this->{$_state}{query} ) { | |
| 115 my $query = $this->{$_state}{query}; | |
| 116 | |
| 117 $this->Back or return undef; # that meams the end of the history | |
| 118 | |
| 119 undef while ( $this->advanceNavigator and not $this->Navigate($query)); | |
| 120 | |
| 121 return $this->Current; | |
| 122 } | |
| 123 return undef; | |
| 124 } | |
| 125 | |
| 126 return $this->Current; | |
| 127 } | |
| 128 | |
| 129 sub doeach { | |
| 130 my ($this,$code) = @_; | |
| 131 local $_; | |
| 132 | |
| 133 do { | |
| 134 for (my $i = $this->{$_state}{current}; $i < @{$this->{$_state}{alternatives}}; $i++) { | |
| 135 $_ = $this->{$_state}{alternatives}[$i]; | |
| 136 $code->(); | |
| 137 } | |
| 138 $this->{$_state}{current} = @{$this->{$_state}{alternatives}}; | |
| 139 } while ($this->advanceNavigator); | |
| 140 } | |
| 141 | |
| 142 sub Back { | |
| 143 my ($this,$steps) = @_; | |
| 144 if ($this->{$_path} and @{$this->{$_path}}) { | |
| 145 if ( (not $steps) || $steps == 1) { | |
| 146 $this->{$_state} = pop @{$this->{$_path}}; | |
| 147 } else { | |
| 148 $steps ||= 1; | |
| 149 | |
| 150 $steps = @{$this->{$_path}} - 1 if $steps >= @{$this->{$_path}}; | |
| 151 | |
| 152 $this->{$_state} = (splice @{$this->{$_path}},-$steps)[0]; | |
| 153 } | |
| 154 $this->Current if defined wantarray; | |
| 155 } else { | |
| 156 return undef; | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 sub PathToString { | |
| 161 my ($this,$delim) = @_; | |
| 162 | |
| 163 $delim ||= '/'; | |
| 164 | |
| 165 join($delim,map $_->{alternatives}[$_->{current}]->nodeName, $this->{$_path} ? (@{$this->{$_path}}, $this->{$_state}) : $this->{$_state}); | |
| 166 } | |
| 167 | |
|
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
168 sub pathLength { |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
169 my ($this) = @_; |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
170 $this->{$_path} ? scalar @{$this->{$_path}} : 0; |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
171 } |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
172 |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
173 sub GetNodeFromHistory { |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
174 my ($this,$index) = @_; |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
175 |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
176 if (my $state = $this->{$_path} ? $this->{$_path}->[$index] : undef ) { |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
177 return $state->{alternatives}[$state->{current}] |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
178 } else { |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
179 return undef; |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
180 } |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
181 } |
|
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
49
diff
changeset
|
182 |
| 49 | 183 sub clone { |
| 184 my ($this) = @_; | |
| 185 | |
| 186 my $newNavi = __PACKAGE__->surrogate; | |
| 187 | |
| 188 $newNavi->{$_path} = [ map { { %{ $_ } } } @{$this->{$_path}} ] if $this->{$_path}; | |
| 189 $newNavi->{$_state} = { %{$this->{$_state}} }; | |
| 190 | |
| 191 return $newNavi; | |
| 192 | |
| 193 } | |
| 194 | |
| 195 sub saveState { | |
| 196 my ($this) = @_; | |
| 197 | |
| 198 my %state; | |
| 199 | |
| 200 $state{path} = [ map { { %{ $_ } } } @{$this->{$_path}} ] if $this->{$_path}; | |
| 201 $state{state} = { %{$this->{$_state}} }; | |
| 202 | |
| 203 push @{$this->{$_savedstates}}, \%state; | |
| 204 } | |
| 205 | |
| 206 sub restoreState { | |
| 207 my ($this) = @_; | |
| 208 | |
| 209 if ( my $state = pop @{$this->{$_savedstates}||[]} ) { | |
| 210 $this->{$_path} = $state->{path}; | |
| 211 $this->{$_state} = $state->{state}; | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 sub applyState { | |
| 216 my ($this) = @_; | |
| 217 | |
| 218 pop @{$this->{$_savedstates}||[]}; | |
| 219 } | |
| 220 | |
| 221 sub dosafe { | |
| 222 my ($this,$transaction) = @_; | |
| 223 | |
| 224 $this->saveState(); | |
| 225 | |
| 226 my $result; | |
| 227 | |
| 228 eval { | |
| 229 $result = $transaction->(); | |
| 230 }; | |
| 231 | |
| 232 if ($@) { | |
| 233 $this->restoreState(); | |
| 234 return undef; | |
| 235 } else { | |
| 236 $this->applyState(); | |
| 237 return $result; | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 1; | |
| 242 | |
| 243 __END__ | |
| 244 =pod | |
| 245 | |
| 246 =head1 DESCRIPTION | |
| 247 | |
| 248 Объект для хождения по дереву DOM объектов. | |
| 249 | |
| 250 Результатом навигации является множество узлов (альтернатив). | |
| 251 | |
| 252 Состоянием навигатора является текущий набор узлов, позиция в данном наборе, | |
| 253 а также запрос по которому были получены данные результаты. | |
| 254 | |
| 255 Если при навигации указан путь сосящий из нескольких фильтров, то он разбивается | |
| 256 этапы простой навигации по кадой из частей пути. На каждом элементарном этапе | |
| 257 навигации образуется ряд альтернатив, и при каждом следующем этапе навигации | |
| 258 альтернативы предыдущих этапов могут перебираться, до получения положительного | |
| 259 результата навигации, в противном случае навигация считается невозможной. | |
| 260 | |
| 261 =head1 METHODS | |
| 262 | |
| 263 =over | |
| 264 | |
| 265 =item C<<$obj->new($nodeStart)>> | |
| 266 | |
| 267 Создает объект навигатора с указанной начальной позицией. | |
| 268 | |
| 269 =item C<<$obj->Navigate([$query,...])>> | |
| 270 | |
| 271 Перейти в новый узел используя запрос C<$query>. На данный момент запросом может | |
| 272 быть только имя узла и будет взят только первый узел. Если по запросу ничего не | |
| 273 найдено, переход не будет осуществлен. | |
| 274 | |
| 275 Возвращает либо новый узел в который перешли, либо C<undef>. | |
| 276 | |
| 277 =item C<<$obj->Back()>> | |
| 278 | |
| 279 Возвращается в предыдущий узел, если таковой есть. | |
| 280 | |
| 281 Возвращает либо узел в который перешли, либо C<undef>. | |
| 282 | |
| 283 =item C<<$obj->advanceNavigator()>> | |
| 284 | |
| 285 Переходит в следующую альтернативу, соответствующую текущему запросу. | |
| 286 | |
| 287 =back | |
| 288 | |
| 289 =cut |
