line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
=head1 NAME |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
GCT::XSP::ActionTaglib - Helps Compose AxKit XSP taglibs in a simple and extensible manor. |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 SYNOPSIS |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package MyTaglib; |
9
|
|
|
|
|
|
|
use GCT::XSP::ActionTaglib; |
10
|
|
|
|
|
|
|
@ISA = qw(use GCT::XSP::ActionTaglib;); |
11
|
|
|
|
|
|
|
our $NS = 'MyTaglib-URI'; |
12
|
|
|
|
|
|
|
our $tagactions; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
$tagactions->{tag1}=[ ...actionlist for tag1... ]; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 DESCRIPTION |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
ActionTaglib helps write a talgib module for AxKit. One or more 'actions' are assigned to each XML element processed by the taglib. When the XSP page is 'run' ActionTaglib performs these actions as it parses the XML elements they are assigned to. An action is implemented as two Perl subroutines one for when the opening XML element is parsed, the 'start' subroutine, and one for when the closing XML element is parsed, the 'end' subroutine. For example: |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
by adding a tag1 entry to the tagactions hashref as follows: |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$tagactions->{tag1}=[{-action => 'myaction'}]; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
And adding the following tags to an XSP page (with the relevant namespace settings) |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
... |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
'myaction_start' will be called for the opening and 'myaction_end' will be called for the closing element . |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
This behavior extends Apache::AxKit::Language::XSP which does essentially the same thing but with one 'action' for all XML elements (parse_start / parse_end). The hope is to make taglibs easier to layout and read but more importantly it is possible to share actions between taglibs and thus save writing the same code twice for different taglibs. Libraries of actions can be written and shared by importing their actions into a taglib module that uses ActionTaglib. |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
ActionTaglib has two further features to enhance the idea and useage of 'actions'. The first is that a tag can be assigned multiple actions and the second is actions can be given arbitrary options. Multiple actions are assigned in the following manor: |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
$tagactions->{tag1}=[{-action => 'library_action'}, |
36
|
|
|
|
|
|
|
{-action => 'myaction'} ]; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
With our ... example 'library_action_start' followed by 'myaction_start' would be called for the opening element and 'myaction_end' followed by 'library_action_end' would be called for the closing element . Note the change in order, actions are proccess in the forward order for an opening element and in the reverse order for a closnig element. |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Options can be parsed in the following manor: |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
$tagactions->{tag1}= [{-action => 'myaction', |
43
|
|
|
|
|
|
|
-options => {-anyoption => 'anyvalue'} }]; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head1 THE tagactions HASHREF |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
The tagactions hashref, as introduced above, accosiates XML elements with the actions that should be performed to proccess them. The format is to specify an array, called an 'actionlist' for each tag as follows: |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
$tagactions->{tag1} = [..actionlist for tag1..] |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Actions are the spcifed, in order, as elements of the array as follows: |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
[action,action,action...] |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Each action is defined by a hash containing th following keys: |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
-action. Required string. the name of the action to call. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
-when. Optional string, Values can be 'start', 'end' or the default 'both'. If the value is 'start' is the action will only be called for the opening tag, if the value is 'end' the action will only be called for the closing tag and if the value is 'both' (or the key -when is ommited) the action will be called for both the opeing and closing tag. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
-pkg. Optional string, The pkg that contanis the action to be called. If ommited the default package is the package of the taglib being written, i.e. the current package.. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
-options. Optional hash, Any options to be passed to the action. |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
The options are passed as a hash of option value pairs. A complete example of a tagactions hashref is given bellow. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head1 WRITEING ACTIONS |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Actions are written in a very similar way to you would write the parse_start and parse_end subs when using AxKit::Lanaguage::XSP directly. Just like the parse* subs in AxKit::Langauge::XSP, action subs output some perl code, as a string, which will be added to the script that is being built for the current XSP page. The proccess, for taglibs in general, is that an XSP page is parsed (with a SAX parser), taglibs transfor it into a perl script, the perl script is executed and ouputs an XML document. The genrated script uses a DOM to build and output it's XML document but this is all handeled by AxKit::Langauge::XSP and a taglib author often need not know. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
If you have never written a parse_start or parse_end using AxKit::Language::XSP you can still write actions. They are defined by writing two subs: actionname_start{} and actionname_end{}. which are called or an element that is specified to use the action named 'actionname', that _start sub when the an opening tag is being parsed and the _end sub when a closing tag is being parsed. They both take the following arguments: |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
($parseargs,$options,$action,$i,$actionlist,$tagactions) |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Which are as follows: |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
$parseargs, array, contains ($e, $tag, %attribs) for the _start sub and ($e, $tag) for the end sub, which is the same idea as used with parse_start and parse_end in AxKit::Lanagauge::XSP. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
$options. hashref, the options to use for this action as specified in the $tagactionshash. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Note the passed values of $action, $i, $actionlist and $tagactions are used for wrtiting more advanced actions. They enable an action to modify the $tagactions hahref it is in and thus change the way actions are performed for tags while the source ducument is being parsed, in otherwords on-the-fly. This feature must be used with care becuase the $tagactions hash could easily be changed in a way that makes no sence. However it is enables us to write actions that and, remove or change other action specifications in the taglib. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
$action, hashref, the action currently being run. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
$i, integer, the index of the current action in it's action list. |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
$actionlist, arrayref, the actionlist in which the current action is specified. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
$tagactions, hasref, all the tagactions. |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head2 $parseargs |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
The variables in passed in parse args are as follows: |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
$e, the script being build |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
$tag, the name of the tag being parsed |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
$attribs, a hash of the attributes bellonging to the current tag. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
MORE TO COME... |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
An example |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
$tagactions->{helloworld} = [{-action => 'format', #the name of the action |
108
|
|
|
|
|
|
|
-pkg => 'htmlformating', #the package it is defined in |
109
|
|
|
|
|
|
|
-when => 'both', #when to call the action |
110
|
|
|
|
|
|
|
-options => {-type => 'bold'} |
111
|
|
|
|
|
|
|
},{ |
112
|
|
|
|
|
|
|
-action => 'text', |
113
|
|
|
|
|
|
|
-when => 'start', |
114
|
|
|
|
|
|
|
-options => { text => 'Hello World'} |
115
|
|
|
|
|
|
|
}]; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head2 EXPORT |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
None by default. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=cut |
124
|
|
|
|
|
|
|
|
125
|
1
|
|
|
1
|
|
40523
|
use 5.006; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
51
|
|
126
|
1
|
|
|
1
|
|
7
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
46
|
|
127
|
1
|
|
|
1
|
|
11
|
use warnings; |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
62
|
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
package GCT::XSP::ActionTaglib; |
130
|
|
|
|
|
|
|
|
131
|
1
|
|
|
1
|
|
1743
|
use Apache::AxKit::Language::XSP; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
our @ISA = ('Apache::AxKit::Language::XSP', 'Exporter'); |
133
|
|
|
|
|
|
|
# our $NS = ... |
134
|
|
|
|
|
|
|
# No namespace here, ActionTaglib is designed to be inherited from |
135
|
|
|
|
|
|
|
# Implementations should declare 'our $NS' with a unique URI. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
138
|
|
|
|
|
|
|
our $REVISION; $REVISION = '$Revision: 1.4 $'; |
139
|
|
|
|
|
|
|
our $dbug=0; |
140
|
|
|
|
|
|
|
our $dbug_tagactions=0; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
######################################## |
143
|
|
|
|
|
|
|
# package GCT::XSP::ActionTaglib; |
144
|
|
|
|
|
|
|
# the 'action processor', reads the action list |
145
|
|
|
|
|
|
|
# and calls the relevant subroutines. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub Debug{ |
148
|
|
|
|
|
|
|
AxKit::Debug(1,"[ActionTaglib]", @_); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
############################################################ |
151
|
|
|
|
|
|
|
#parses all elements and calls the relevant subroutine |
152
|
|
|
|
|
|
|
#to deal with them. |
153
|
|
|
|
|
|
|
sub parse_element{ |
154
|
|
|
|
|
|
|
my ($parseargs,$when) = @_; |
155
|
|
|
|
|
|
|
#$parseargs, arrayref, the standard AxKit arguments as given to |
156
|
|
|
|
|
|
|
#parse_start / parse_end.($e,$tag,%attribs). |
157
|
|
|
|
|
|
|
#$when, string, 'start' => opening element, 'end' => closing element. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
my $tag = $parseargs->[1]; |
160
|
|
|
|
|
|
|
#get the tagactions hashref that specifies how to deal with this element |
161
|
|
|
|
|
|
|
my $tagactions; |
162
|
|
|
|
|
|
|
my $pkg = $AxKit::XSP::TaglibPkg; #not very OOP! would be better if we could call $self->tagactions |
163
|
|
|
|
|
|
|
{no strict; |
164
|
|
|
|
|
|
|
$tagactions = ${"$pkg\::tagactions"};}; |
165
|
|
|
|
|
|
|
Debug("parse_start -$pkg-$tag-") if $dbug; |
166
|
|
|
|
|
|
|
#get the actionlist for this element |
167
|
|
|
|
|
|
|
if(my $actionlist = ($tagactions->{$tag})){ |
168
|
|
|
|
|
|
|
Debug("ActionTaglib Actions:") if $dbug; |
169
|
|
|
|
|
|
|
#TODO use a reference to the actionlist and avoid creating a ne array |
170
|
|
|
|
|
|
|
#that way the actionlist array will refer to the actual array rather |
171
|
|
|
|
|
|
|
#than a copy of it. |
172
|
|
|
|
|
|
|
Debug("parsing $tag $when ") if $dbug; |
173
|
|
|
|
|
|
|
#Do all the actions for this element, |
174
|
|
|
|
|
|
|
#pass $actionslist and $tagactions so the action can modify them is necessary. |
175
|
|
|
|
|
|
|
return processactionlist($parseargs,$actionlist,$tagactions,$when,$pkg) |
176
|
|
|
|
|
|
|
}else{ |
177
|
|
|
|
|
|
|
return ''; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
############################################################ |
182
|
|
|
|
|
|
|
#processes an array of actions, in forward order if they are |
183
|
|
|
|
|
|
|
#being started ($when = 'start') and in reverse order if |
184
|
|
|
|
|
|
|
#they are being ended ($when = 'end'). |
185
|
|
|
|
|
|
|
sub processactionlist{ |
186
|
|
|
|
|
|
|
my ($parseargs,$actionlist,$tagactions,$when,$default_pkg) = @_; |
187
|
|
|
|
|
|
|
#$parseargs, as above. |
188
|
|
|
|
|
|
|
#$actionlist, arrayref, a list of actions to do for this element. |
189
|
|
|
|
|
|
|
#$tagactions, hashref, all the tagactions in this taglib |
190
|
|
|
|
|
|
|
#$when, as above. |
191
|
|
|
|
|
|
|
#$default_pkg, string, the default package to contain the action sub's. |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
my $last = scalar @{$actionlist}; |
194
|
|
|
|
|
|
|
my $ret; |
195
|
|
|
|
|
|
|
Debug("COUNT: $last ACTIONS TODO ") if $dbug; |
196
|
|
|
|
|
|
|
if ($when eq 'start'){ #forward |
197
|
|
|
|
|
|
|
for (my $i=0;$i<$last;$i++){ |
198
|
|
|
|
|
|
|
$ret .= processactioni($parseargs,$i,$actionlist,$tagactions,$when,$default_pkg); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
}elsif($when eq 'end'){ #reverse |
201
|
|
|
|
|
|
|
for (my $i=$last;$i>-1;$i--){ |
202
|
|
|
|
|
|
|
$ret .= processactioni($parseargs,$i,$actionlist,$tagactions,$when,$default_pkg); |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
}else{ |
205
|
|
|
|
|
|
|
die "'$when' is not a valid when, value must be start or end"; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
return $ret; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
############################################################ |
211
|
|
|
|
|
|
|
#Process a numbered action from an actionlist but only if |
212
|
|
|
|
|
|
|
#the time ($when) is right. |
213
|
|
|
|
|
|
|
sub processactioni{ |
214
|
|
|
|
|
|
|
my ($parseargs,$i,$actionlist,$tagactions,$when,$default_pkg) = @_; |
215
|
|
|
|
|
|
|
#all arguments as above, except: |
216
|
|
|
|
|
|
|
#$i, integer, numbered action from $actionlist array to process. |
217
|
|
|
|
|
|
|
my $action = $actionlist->[$i]; |
218
|
|
|
|
|
|
|
#the -when option specifies when the action should be done. |
219
|
|
|
|
|
|
|
#'start' => only when an element is opening. |
220
|
|
|
|
|
|
|
#'end' => only when an element is closing. |
221
|
|
|
|
|
|
|
#'both' => both when an element is opening and when is is closing [default]. |
222
|
|
|
|
|
|
|
my $awhen = $action->{-when} || "both"; |
223
|
|
|
|
|
|
|
Debug("DOING ACTION $i ") if $dbug; |
224
|
|
|
|
|
|
|
if ( $awhen eq "both" || $awhen eq $when) { |
225
|
|
|
|
|
|
|
$action->{-pkg} ||= $default_pkg; |
226
|
|
|
|
|
|
|
return processaction($parseargs,$action,$i,$actionlist,$tagactions,$when); |
227
|
|
|
|
|
|
|
}else{ |
228
|
|
|
|
|
|
|
return ''; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
############################################################ |
233
|
|
|
|
|
|
|
#processes a single action |
234
|
|
|
|
|
|
|
sub processaction{ |
235
|
|
|
|
|
|
|
my ($parseargs,$action,$i,$actionlist,$tagactions,$when) = @_; |
236
|
|
|
|
|
|
|
#all arguments as above. |
237
|
|
|
|
|
|
|
my $methodpkg = $action->{-pkg}; #required. the package that contains the action |
238
|
|
|
|
|
|
|
my $method = $action->{-action} . "_$when"; #required. the name of the action to perform, appended |
239
|
|
|
|
|
|
|
#with the time (start or end) it is being performed |
240
|
|
|
|
|
|
|
my $options = $action->{-options}; #optional. any options the action might use/need. |
241
|
|
|
|
|
|
|
Debug("LOOKING TO SEE IF $methodpkg -> can ($method)") if $dbug; |
242
|
|
|
|
|
|
|
if (my $sub = $methodpkg->can($method)){ |
243
|
|
|
|
|
|
|
Debug("A SUB CALLED: $method for $parseargs->[1] ") if $dbug; |
244
|
|
|
|
|
|
|
#finally we do the action! |
245
|
|
|
|
|
|
|
#see the documentation on how to write an action |
246
|
|
|
|
|
|
|
return $sub->($parseargs,$options,$action,$i,$actionlist,$tagactions); |
247
|
|
|
|
|
|
|
}else{ |
248
|
|
|
|
|
|
|
return ''; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
######################################## |
252
|
|
|
|
|
|
|
# Overriding Apache::AxKit::Language::XSP subs; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
sub start_document{ |
255
|
|
|
|
|
|
|
Debug("[ActionTaglib] START DOCUMENT"); |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
if($debug_tagations){ |
258
|
|
|
|
|
|
|
my $tagactions; |
259
|
|
|
|
|
|
|
my $pkg = $AxKit::XSP::TaglibPkg; #holds the package name of the current taglib |
260
|
|
|
|
|
|
|
{no strict; |
261
|
|
|
|
|
|
|
$tagactions = \${"$pkg\::tagactions"};}; |
262
|
|
|
|
|
|
|
use Data::Dumper; |
263
|
|
|
|
|
|
|
Debug("TAGATIONS AT START OF DOUCUMENT: " . Dumper($tagactions)) if $dbug; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
return ''; |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
sub end_document{ |
269
|
|
|
|
|
|
|
Debug(1,warn "END DOCUMENT"); |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
if($debug_tagations){ |
272
|
|
|
|
|
|
|
my $tagactions; |
273
|
|
|
|
|
|
|
my $pkg = $AxKit::XSP::TaglibPkg; #not very OOP! |
274
|
|
|
|
|
|
|
{no strict; |
275
|
|
|
|
|
|
|
$tagactions = \${"$pkg\::tagactions"};}; |
276
|
|
|
|
|
|
|
use Data::Dumper; |
277
|
|
|
|
|
|
|
Debug("TAGATIONS END: " . Dumper($tagactions)) if $dbug; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
return ''; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# get the ActionTaglib parse_element to deal with all |
283
|
|
|
|
|
|
|
# elements opening (start) and closing (end). |
284
|
|
|
|
|
|
|
sub parse_start{ |
285
|
|
|
|
|
|
|
return parse_element(\@_,'start'); |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub parse_end{ |
289
|
|
|
|
|
|
|
return parse_element(\@_,'end'); |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
#default to add characters as text nodes |
293
|
|
|
|
|
|
|
sub parse_char{ |
294
|
|
|
|
|
|
|
my ($e, $text) = @_; |
295
|
|
|
|
|
|
|
$text =~ s/^\s*//; #remove leading |
296
|
|
|
|
|
|
|
$text =~ s/\s*$//; #and trailing spaces |
297
|
|
|
|
|
|
|
return '' unless $text; |
298
|
|
|
|
|
|
|
$text = Apache::AxKit::Language::XSP::makeSingleQuoted($text); |
299
|
|
|
|
|
|
|
return ". $text"; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
#drop comments |
303
|
|
|
|
|
|
|
sub parse_comment{ |
304
|
|
|
|
|
|
|
my ($e, $comment); |
305
|
|
|
|
|
|
|
return ''; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
1; |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
__END__ |