/[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 - (hide 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
++ 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 wakaba 1.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