/[suikacvs]/messaging/manakai/lib/Message/DOM/SerialWalker.pm
Suika

Contents of /messaging/manakai/lib/Message/DOM/SerialWalker.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (show annotations) (download)
Sat Jul 14 16:32:28 2007 UTC (17 years, 4 months ago) by wakaba
Branch: MAIN
CVS Tags: manakai-release-0-4-0, HEAD
Error occurred while calculating annotation data.
++ manakai/t/ChangeLog	14 Jul 2007 16:32:13 -0000
2007-07-15  Wakaba  <wakaba@suika.fam.cx>

	* DOM-TreeWalker.t, DOM-SerialWalker.t: New test scripts.

	* DOM-DOMImplementation.t: Tests for |Traversal| feature
	are added.

	* DOM-Node.t: Tests for |Traversal| feature are added.

++ manakai/lib/Message/DOM/ChangeLog	14 Jul 2007 16:31:23 -0000
2007-07-15  Wakaba  <wakaba@suika.fam.cx>

	* TreeWalker.pm, SerialWalker.pm: New Perl modules.

	* Text.pm (whole_text): Parameter index number has
	been changed to support new |NodeFilter| Perl binding
	definition.

2007-07-14  Wakaba  <wakaba@suika.fam.cx>

	* AttributeDefinition.pm, DOMElement.pm, DocumentType.pm,
	ElementTypeDefinition.pm, Entity.pm, EntityReference.pm,
	Notation.pm, ProcessingInstruction.pm (AUTOLOAD): Don't croak even if an attempt is made to modify a read-only attribute.

	* DOMConfiguration.pm (can_set_parameter,
	set_parameter): Don't allow to set the value
	to a string other than <http://www.w3.org/TR/REC-xml> (XML 1.0 DTD).

	* DOMDocument.pm (Message::IF::DocumentTraversal): New interface.
	(create_tree_walker, manakai_create_serial_walker): References
	and prototypes are added.

	* DOMException.pm (NULLPO_ERR): New error type:-).

	* DOMImplementation.pm ($HasFeature): Feature |Traversal|,
	version |2.0|, is added.

1 package Message::DOM::SerialWalker;
2 use strict;
3 our $VERSION=do{my @r=(q$Revision: 1.8 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 push our @ISA, 'Message::IF::SerialWalker';
5
6 sub AUTOLOAD {
7 my $method_name = our $AUTOLOAD;
8 $method_name =~ s/.*:://;
9 return if $method_name eq 'DESTROY';
10
11 if ({
12 current_index => 1,
13 current_node => 1,
14 current_phase => 1,
15 expand_entity_references => 1,
16 filter => 1,
17 root => 1,
18 what_to_show => 1,
19 }->{$method_name}) {
20 no strict 'refs';
21 eval qq{
22 sub $method_name (\$) { \$_[0]->{$method_name} }
23 };
24 goto &{ $AUTOLOAD };
25 } else {
26 require Carp;
27 Carp::croak (qq<Can't locate method "$AUTOLOAD">);
28 }
29 } # AUTOLOAD
30
31 ## |SerialWalker| constants
32
33 ## |SerialWalkerPhase|
34 sub PRE_PHASE () { 1 }
35 sub IN_PHASE () { 2 }
36 sub POST_PHASE () { 3 }
37
38 ## |SerialWalker| attributes
39
40 sub current_index ($);
41
42 sub current_node ($);
43
44 sub current_phase ($);
45
46 sub expand_entity_references ($);
47
48 sub filter ($);
49
50 sub root ($);
51
52 sub what_to_show ($);
53
54 ## |SerialWalker| methods
55
56 sub next_node ($) {
57 my $self = $_[0];
58
59 A: {
60 unless (@{$self->{next}}) {
61 #
62 } elsif ($self->{next}->[0]->[1] != 0) {
63 my $v = shift @{$self->{next}};
64 $self->{current_phase} = $v->[1];
65 $self->{current_index} = $self->{_index}->{$v->[2]}++;
66 return ($self->{current_node} = $v->[0]);
67 } else {
68 my $v = shift @{$self->{next}};
69 my $vresult = $self->_test_node ($v->[0]);
70
71 my @pnext;
72 if ($vresult == 1) { # FILTER_ACCEPT
73 my $key = $v->[2].'.'.$self->{_index}->{$v->[2]};
74
75 push @pnext, [$v->[3], IN_PHASE, $v->[4]]
76 if defined $v->[3] and $self->{_index}->{$v->[4].'.1'};
77
78 $self->{_index}->{$key} = 0;
79 push @pnext, [$v->[0], PRE_PHASE, $key];
80
81 push @pnext, [$_, 0, $key, $v->[0], $key]
82 for @{$v->[0]->child_nodes};
83 push @pnext, [$v->[0], POST_PHASE, $key];
84 } elsif ($vresult == 3) { # FILTER_SKIP
85 push @pnext, [$_, 0, $v->[2], $v->[3], $v->[4]]
86 for @{$v->[0]->child_nodes};
87 } elsif ($vresult == 12101) { # MANAKAI_FILTER_OPAQUE
88 my $key = $v->[2].'.'.$self->{_index}->{$v->[2]};
89
90 push @pnext, [$v->[3], IN_PHASE, $v->[4]]
91 if defined $v->[3] and $self->{_index}->{$v->[4].'.1'};
92
93 $self->{_index}->{$key} = 0;
94 push @pnext, [$v->[0], PRE_PHASE, $key];
95 push @pnext, [$v->[0], POST_PHASE, $key];
96 }
97 unshift @{$self->{next}}, @pnext;
98 redo A;
99 }
100 } # A
101
102 return undef;
103 } # next_node
104
105 sub _test_node ($$) {
106 ## NOTE: There is a code clone in |TreeWalker.pm|.
107
108 unless ($_[0]->{expand_entity_references}) {
109 my $parent = $_[1]->parent_node;
110 if (defined $parent and $parent->node_type == 5) { # ENTITY_REFERENCE_NODE
111 return 2; # FILTER_REJECT ## NOTE: Even if |NodeIterator|.
112 }
113 }
114
115 if ($_[0]->{what_to_show} != 0xFFFFFFFF) { # SHOW_ALL
116 my $nt = $_[1]->node_type;
117 if ($nt < 33 and ($_[0]->{what_to_show} & (1 << ($nt-1)))) {
118 #
119 } else {
120 return 3; # FILTER_SKIP
121 }
122 }
123
124 if (defined $_[0]->{filter}) {
125 local $Error::Depth = $Error::Depth + 1;
126 return $_[0]->{filter}->($_[1]);
127 } else {
128 return 1; # FILTER_ACCEPT
129 }
130 } # _test_node
131
132 package Message::IF::SerialWalker;
133
134 package Message::DOM::Document;
135
136 sub manakai_create_serial_walker ($$;$$$) {
137 unless (defined $_[1]) {
138 require Message::DOM::DOMException;
139 report Message::DOM::DOMException
140 -object => $_[0],
141 -type => 'NOT_SUPPORTED_ERR',
142 -subtype => 'NULLPO_ERR';
143 }
144
145 ## TODO: Initials for current_index and current_phase
146 return bless {
147 root => $_[1],
148 what_to_show => 0+($_[2] or 0),
149 filter => $_[3],
150 expand_entity_references => $_[4] ? 1 : 0,
151 current_node => $_[1],
152 next => [[$_[1], 0, '']],
153 _index => {'' => 0},
154 }, 'Message::DOM::SerialWalker';
155 } # manakai_create_serial_walker
156
157 =pod
158
159 ## TODO: Documentation
160
161 @LXMethod:
162 @@Name: manakaiCreateSerialWalker
163 @@enDesc:
164 Creates a new <IF::SerialWalker> object.
165 @@Param:
166 @@@Name: root
167 @@@Type: Node
168 @@@enDesc:
169 The node that will serve as the root for the <IF::SerialWalker>.
170
171 The <P::whatToShow> flags and the <IF::NodeFilter>
172 are not considered when setting this value; any node type
173 will be accepted as the root.
174
175 The <A::TreeWalker.currentNode> of the created <IF::TreeWalker>
176 is initialized to the <P::root> node, whether or not it
177 is visible.
178
179 The <P::root> functions as a stopping point for traversal
180 methods that look upwards in the document structure, i.e.
181 <M::SerialWalker.nextNode>.
182
183 The <P::root> <kwd:MUST-NOT> be <DOM::null>. If it is,
184 the implementation <kwd:MUST> throw an exception.
185 @@Param:
186 @@@Name: whatToShow
187 @@@Type: unsignedShort
188 @@@actualType: WhatToShow
189 @@@enDesc:
190 The flags that specifies which node types may appear in the
191 logical view of the tree presented by the <IF::SerialWalker>.
192
193 The set of the flags are defined in the <IF::NodeFilter> interface.
194 They can be combined using the binary <CODE::OR> operation.
195 @@Param:
196 @@@Name: filter
197 @@@Type: NodeFilter
198 @@@enDesc:
199 The <IF::NodeFilter> to be used with the created <IF::SerialWalker>.
200 @@@nullCase:
201 @@@@enDesc:
202 Indicates that no filter is used.
203 @@Param:
204 @@@Name: entityReferenceExpansion
205 @@@Type: boolean
206 @@@enDesc:
207 The <A::SerialWalker.expandEntityReferences> flag of
208 the created <IF::TreeWalker>.
209 @@@TrueCase:
210 @@@@enDesc:
211 The contents of the <IF::tx|EntityReference> nodes
212 <EM::are> presented in the logical view.
213 @@@FalseCase:
214 @@@@enDesc:
215 The contents of the <IF::tx|EntityReference> nodes
216 are <EM::not> presented in the logical view.
217 @@Return:
218 @@@Type: SerialWalker
219 @@@enDesc:
220 {P:: The newly created <IF::SerialWalker>. It <kwd:MUST>
221 have attributes set as:
222
223 - <A::SerialWalker.currentNode>::: <P::root>.
224
225 - <A::SerialWalker.expandEntityReferences>:::
226 <P::entityReferenceExpansion>.
227
228 - <A::SerialWalker.filter>::: <P::filter>.
229
230 - <A::SerialWalker.root>::: <P::root>.
231
232 - <A::SerialWalker.whatToShow>::: <P::whatToShow>.
233
234 }
235 @@@dx:raises:
236 @@@@@: c|SET_TO_NULL_ERR
237 @@@@enDesc:
238 If the specified <P::root> is <DOM::null>.
239 {ISSUE::
240 Better name?
241 }
242
243 A <IF::SerialWalker> object can be used to traverse
244 the subtree rooted by a node in document order.
245 Unlike <IF::TreeWalker> and <IF::NodeIterator>, the
246 <IF::SerialWalker> object cannot go back toward
247 nodes that had already been seen. The
248 <IF::SerialWalker>, however, revisits a node after its
249 children are visited. This might be useful when
250 an application wants to serialize a subtree in a format
251 where delimiter and / or terminator should be inserted
252 between and / or after nodes.
253
254 How the <IF::SerialWalker> works is defined by the following
255 reference algorithm. An implementation does not have to
256 implement this exact algorithm, but it <kwd:MUST> implement
257 its method so that it has the same effect as the
258 reference algorithm when the underlying subtree has not
259 been modified.
260
261 At the time of creation of the <IF::SerialWalker>,
262 the implementation <kwd:MUST> prepare a queue and
263 <kwd:MUST> initialize it by pushing the <A::SerialWalker.root> node.
264
265 {P:: When the <M::SerialWalker.nextNode> method is invoked,
266
267 - If the queue is empty, the method <kwd:MUST> return
268 <DOM::null>. It <kwd:MUST-NOT> change
269 <A::SerialWalker.currentNode>, <A::SerialWalker.currentPhase>,
270 and <A::SerialWalker.currentIndex> attributes.
271
272 {LI:: Otherwise,
273
274 - Shift a node from the queue. If the node has
275 the phase value, the method <kwd:MUST> return
276 the node. In addition, it <kwd:MUST> set
277 the <A::SerialWalker.currentNode> attribute to the node,
278 the <A::SerialWalker.currentPhase> attribute to
279 the associated phase value, and the
280 <A::SerialWalker.currentIndex> attribute to
281 the integer value that is large by one than
282 the most recent <A::SerialWalker.currentIndex> value
283 when the <A::SerialWalker.currentNode> was
284 same as the shiftted node. If it is the first time
285 the node is set to the <A::SerialWalker.currentNode>
286 attribute, the <A::SerialWalker.currentIndex>
287 attribute <kwd:MUST> set to zero.
288
289 {LI:: Otherwise, determine whether the shifted node
290 should be accepted or not, using
291 <A::SerialWalker.expandEntityReferences>,
292 <A::SerialWalker.whatToShow>, and
293 <A::SerialWalker.filter> attributes, as defined
294 for the <IF::TreeWalker> interface.
295
296 {LI:: If the node is <C::NodeFilter.FILTER_ACCEPT>ed,
297
298 = Prepend the node to the queue, with
299 phase value set to <C::SerialWalker.POST_PHASE>.
300
301 = Prepend the child nodes of the node, if any,
302 to the queue keeping the document order,
303 without phase value. That is, the first child
304 <kwd:MUST> be located at the top (first
305 position) of the queue.
306
307 = Prepend the node to the queue, with
308 phase value set to <C::SerialWalker.PRE_PHASE>.
309
310 = If the node has a previous sibling in the logical view,
311 preprend the parent node of the node in the logical
312 view, with phase value set to <C::SerialWalker.IN_PHASE>.
313
314 = Invoke the algorithm recursively.
315
316 }
317
318 {LI:: If the node is <C::NodeFilter.FILTER_SKIP>ped,
319
320 = Prepend the child nodes of the node, if any,
321 to the queue keeping the document order,
322 without phase value. That is, the first child
323 <kwd:MUST> be located at the top (first
324 position) of the queue.
325
326 = Invoke the algorithm recursively.
327
328 }
329
330 {LI:: If the node is <C::NodeFilter.MANAKAI_FILTER_OPAQUE>,
331
332 = Prepend the node to the queue, with
333 phase value set to <C::SerialWalker.POST_PHASE>.
334
335 = Prepend the node to the queue, with
336 phase value set to <C::SerialWalker.PRE_PHASE>.
337
338 = If the node has a previous sibling in the logical view,
339 preprend the parent node of the node in the logical
340 view, with phase value set to <C::SerialWalker.IN_PHASE>.
341
342 = Invoke the algorithm recursively.
343
344 }
345
346 - Otherwise, invoke the algorithm recursively.
347
348 }
349 }
350
351 }
352
353 {NOTE::
354 This algorithm will stop unless nodes are simulataneously
355 populated to the underlying tree by application via
356 e.g. <IF::NodeFilter>.
357 }
358
359 Implementation <kwd:MAY> choose to implement the <IF::SerialWalker>
360 using another algorithm. In particular, when the <IF::NodeFilter>
361 is invoked might vary across implementations. Implementations
362 should try to return the same effect as the reference algorithm
363 even if the underlying subtree is modified, but applications
364 <kwd:MUST-NOT> rely on the particular implementation's behavior.
365
366 As long as the underlying subtree is not modified, a node will
367 never set to the <A::SerialWalker.currentNode> attribute
368 with the <A::SerialWalker.currentPhase> of <C::SerialWalker.PRE_PHASE>.
369 If the underlying subtree is modified, depending on how
370 the method is implemented, it might be in case. For
371 the purpose of description below, such duplication was considered
372 as if they were different nodes.
373
374 {P:: Even if the underlying subtree has been modified during
375 the traversal, the implementation <kwd:MUST> ensure
376 the following conditions are met:
377
378 - A node <kwd:MUST-NOT> be set to the <A::SerialWalker.currentNode>
379 attribute with the <A::SerialWalker.currentPhase> of
380 <C::SerialWalker.PRE_PHASE> or <C::SerialWalker.POST_PHASE>
381 more than once respectively.
382
383 - A node <kwd:MUST-NOT> be set to the <A::SerialWalker.currentNode>
384 attribute with the <A::SerialWalker.currentPhase> of
385 <C::SerialWalker.IN_PHASE> before it is once set to
386 the same node with the <A::SerialWalker.currentPhase>
387 of <C::SerialWalker.PRE_PHASE>, or after it is once set
388 to the same node with the <A::SerialWalker.currentPhase>
389 of <C::SerialWalker.POST_PHASE>.
390
391 - A node <kwd:MUST-NOT> be set to the <A::SerialWalker.currentNode>
392 attribute with the <A::SerialWalker.currentPhase> of
393 <C::SerialWalker.POST_PHASE> before it is once set to
394 the same node with the <A::SerialWalker.currentPhase>
395 of <C::SerialWalker.PRE_PHASE>.
396
397 - A node <kwd:MUST> be set to the <A::SerialWalker.currentNode>
398 attribute with the <A::SerialWalker.currentPhase> of
399 <C::SerialWalker.POST_PHASE> later if it is once set to
400 the same node with the <A::SerialWalker.currentPhase>
401 of <C::SerialWalker.PRE_PHASE>.
402
403 - The <A::SerialWalker.currentPhase> <kwd:MUST-NOT> set
404 to <C::SerialWalker.PRE_PHASE> unless its value is
405 <C::SerialWalker.PRE_PHASE> or <C::SerialWalker.IN_PHASE>.
406
407 - The <A::SerialWalker.currentPhase> <kwd:MUST-NOT> set
408 to <C::SerialWalker.IN_PHASE> unless its value is
409 <C::SerialWalker.POST_PHASE>.
410
411 - The <A::SerialWalker.currentPhase> <kwd:MUST-NOT> set
412 to <C::SerialWalker.POST_PHASE> unless its value is
413 <C::SerialWalker.PRE_PHASE> or <C::SerialWalker.POST_PHASE>.
414
415 - If a node <VAR::N> is set to the <A::SerialWalker.currentNode>
416 attribute and then another node <VAR::M> is set to the attribute
417 with both <A::SerialWalker.currentPhase> of <C::SerialWalker.PRE_PHASE>,
418 the <A::SerialWalker.currentNode> attribute <kwd:MUST-NOT> be set to
419 <VAR::N> with <A::SerialWalker.currentPhase> of
420 <C::SerialWalker.POST_PHASE> before once the
421 <A::SerialWalker.currentNode> attribute is set to
422 <VAR::M> with <A::SerialWalker.currentPhase> of
423 <C::SerialWalker.POST_PHASE>.
424
425 - For each time the <A::SerialWalker.currentNode> attribute is
426 set to a node, the <A::SerialWalker.currentIndex> <kwd:MUST>
427 be increased by one. If the node is set to the attribute
428 for the first time, the <A::SerialWalker.currentIndex>
429 <kwd:MUST> be set to zero. That is, with and only with
430 <C::SerialWalker.PRE_PHASE> the <A::SerialWalker.currentIndex>
431 can be zero.
432 }
433
434 @LXAttr:
435 @@Name: root
436 @@enDesc:
437 The root node of the <IF::SerialWalker>.
438 @@Type: Node
439 @@Get:
440 @@@enDesc:
441 The root node set when the <IF::SerialWalker> is created.
442
443 @LXAttr:
444 @@Name: currentNode
445 @@enDesc:
446 The current node of the <IF::SerialWalker>.
447
448 {NOTE::
449 Unlike <IF::TreeWalker>, the <A::SerialWalker.currentNode>
450 attribute is read-only.
451 }
452 @@Type: Node
453 @@Get:
454 @@@enDesc:
455 The current node.
456
457 @mShortConstGroup:
458 @@IFQName: SerialWalkerPhase
459 @@enDesc:
460 An integer to indicate a phase of <IF::SerialWalker>.
461
462 @@mConst:
463 @@@Name: PRE_PHASE
464 @@@intValue: 1
465 @@@enDesc:
466 The <A::SerialWalker.currentNode> is visited by
467 the <IF::SerialWalker> for the first time. It is
468 expected that the same node will be revisited
469 with <A::SerialWalker.currentPhase> of
470 <C::SerialWalker.IN_PHASE> (optionally) and
471 <C::SerialWalker.POST_PHASE> later.
472 @@mConst:
473 @@@Name: IN_PHASE
474 @@@intValue: 2
475 @@@enDesc:
476 The <A::SerialWalker.currentNode> is visited
477 by the <IF::SerialWalker>, not for the first
478 or last time, between visitings to its two child nodes.
479 @@mConst:
480 @@@Name: POST_PHASE
481 @@@intValue: 3
482 @@@enDesc:
483 The <A::SerialWalker.currentNode> is visited
484 by the <IF::SerialWalker> for the last time.
485
486 @LXAttr:
487 @@Name: currentPhase
488 @@enDesc:
489 The current phase of the <IF::SerialWalker>.
490 @@Type: unsignedShort
491 @@actualType: SerialWalkerPhase
492 @@Get:
493 @@@enDesc:
494 An integer to indicate the current phase.
495
496 @LXAttr:
497 @@Name: currentIndex
498 @@enDesc:
499 The current index of the <IF::SerialWalker>. The
500 <DFN::current index> represents how many times the
501 <A::SerialWalker.currentNode> is exposed through
502 the <IF::SerialWalker>.
503 @@Type: unsignedLong
504 @@Get:
505 @@@enDesc:
506 An integer to indicate the current index.
507
508 @LXAttr:
509 @@Name: expandEntityReferences
510 @@enDesc:
511 The value of the flag that determines whether the children of
512 <IF::tx|EntityReference> nodes are visible to the <IF::SerialWalker>.
513
514 {NOTE::
515 To produce a view of the document that has entity references
516 expanded and does not expose the <IF::tx|EntityReference> nodes
517 themselves, use the <A::SerialWalker.whatToShow> flags to hide the
518 <IF::tx|EntityReference> nodes and set the
519 <A::SerialWalker.expandEntityReferences> flag to <DOM::true>
520 when creating the <IF::SerialWalker>.
521
522 To produce a view of the document that has <IF::tx|EntityReference>
523 nodes but no entity expansion, use the <A::SerialWalker.whatToShow>
524 flags to show the <IF::tx|EntityReference> nodes and set the
525 <A::SerialWalker.expandEntityReferences> flag to <DOM::false>
526 when creating the <IF::SerialWalker>.
527 }
528 @@Type: boolean
529 @@Get:
530 @@@enDesc:
531 The <CODE::entityReferenceExpansion> value that
532 was specified when the <IF::SerialWalker> was created.
533 @@@TrueCase:
534 @@@@enDesc:
535 The children and their descendants of <IF::tx|EntityReference>
536 nodes will <EM::not> be rejected. Note that
537 the <A::SerialWalker.whatToShow> flags and the
538 <A::SerialWalker.filter> set to the <IF::SerialWalker>
539 might reject them as usual way.
540 @@@FalseCase:
541 @@@@enDesc:
542 The children and their descendants of entity reference nodes
543 will be rejected. This rejection takes precedence over
544 the <A::SerialWalker.whatToShow> flags and the
545 <A::SerialWalker.filter> set to the
546 <IF::SerialWalker>, if any.
547
548 @LXAttr:
549 @@Name: filter
550 @@enDesc:
551 The filter used to screen nodes.
552 @@Type: NodeFilter
553 @@Get:
554 @@@enDesc:
555 The <IF::NodeFilter> that was specified when the
556 <IF::SerialWalker> was created.
557 @@@nullCase:
558 @@@@enDesc:
559 If no <IF::NodeFilter> was specified when the
560 <IF::SerialWalker> was created.
561
562 @LXAttr:
563 @@Name: whatToShow
564 @@enDesc:
565 The flags that determines which node types are presented
566 via the <IF::SerialWalker>. The available set of constants
567 is defined in the <IF::NodeFilter> interface.
568
569 Nodes not accepted by the <A::SerialWalker.whatToShow>
570 flags will be skipped, but their children may still be
571 considered. This skip takes precedence over the
572 <A::SerialWalker.filter> set to the <IF::SerialWalker>, if any.
573 @@Type: unsignedShort
574 @@actualType: WhatToShow
575 @@Get:
576 @@@enDesc:
577 The <CODE::whatToShow> value that was specified
578 when the <IF::SerialWalker> was created.
579
580 @LXMethod:
581 @@Name: nextNode
582 @@enDesc:
583 Returns the node next to the <A::SerialWalker.currentNode>
584 of the <IF::SerialWalker>.
585 @@Return:
586 @@@Type: Node
587 @@@enDesc:
588 The next node.
589 @@@nullCase:
590 @@@@enDesc:
591 If no next node.
592
593 =cut
594
595 =head1 LICENSE
596
597 Copyright 2007 Wakaba <w@suika.fam.cx>
598
599 This program is free software; you can redistribute it and/or
600 modify it under the same terms as Perl itself.
601
602 =cut
603
604 1;
605 ## $Date: 2007/07/14 09:19:11 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24