| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Dancer2::Plugin::Swagger2; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 2 |  |  | 2 |  | 131009 | use strict; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 44 |  | 
| 4 | 2 |  |  | 2 |  | 8 | use warnings; | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 60 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | # ABSTRACT: A Dancer2 plugin for creating routes from a Swagger2 spec | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.003_001'; # TRIAL VERSION | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 2 |  |  | 2 |  | 741 | use Dancer2 ':syntax'; | 
|  | 2 |  |  |  |  | 428863 |  | 
|  | 2 |  |  |  |  | 11 |  | 
| 10 | 2 |  |  | 2 |  | 86456 | use Dancer2::Plugin; | 
|  | 2 |  |  |  |  | 3493 |  | 
|  | 2 |  |  |  |  | 9 |  | 
| 11 | 2 |  |  | 2 |  | 1144 | use Module::Load; | 
|  | 2 |  |  |  |  | 1483 |  | 
|  | 2 |  |  |  |  | 10 |  | 
| 12 | 2 |  |  | 2 |  | 792 | use Swagger2; | 
|  | 2 |  |  |  |  | 114314 |  | 
|  | 2 |  |  |  |  | 16 |  | 
| 13 | 2 |  |  | 2 |  | 68 | use Swagger2::SchemaValidator; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 2455 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 | 0 |  |  | 0 | 0 | 0 | sub DEBUG { !!$ENV{SWAGGER2_DEBUG} } | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | register swagger2 => sub { | 
| 20 |  |  |  |  |  |  | my ( $dsl, %args ) = @_; | 
| 21 |  |  |  |  |  |  | my $conf = plugin_setting; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | ### get arguments/config values/defaults ### | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | my $controller_factory = | 
| 26 |  |  |  |  |  |  | $args{controller_factory} || \&_default_controller_factory; | 
| 27 |  |  |  |  |  |  | my $url = $args{url} or die "argument 'url' missing"; | 
| 28 |  |  |  |  |  |  | my $create_options_route = | 
| 29 |  |  |  |  |  |  | exists $args{create_options_route}   ? !!$args{create_options_route} | 
| 30 |  |  |  |  |  |  | : exists $conf->{create_options_route} ? !!$conf->{create_options_route} | 
| 31 |  |  |  |  |  |  | :                                        ''; | 
| 32 |  |  |  |  |  |  | my $validate_spec = | 
| 33 |  |  |  |  |  |  | exists $args{validate_spec}   ? !!$args{validate_spec} | 
| 34 |  |  |  |  |  |  | : exists $conf->{validate_spec} ? !!$conf->{validate_spec} | 
| 35 |  |  |  |  |  |  | :                                 1; | 
| 36 |  |  |  |  |  |  | my $validate_requests = | 
| 37 |  |  |  |  |  |  | exists $args{validate_requests}   ? !!$args{validate_requests} | 
| 38 |  |  |  |  |  |  | : exists $conf->{validate_requests} ? !!$conf->{validate_requests} | 
| 39 |  |  |  |  |  |  | :                                     $validate_spec; | 
| 40 |  |  |  |  |  |  | my $validate_responses = | 
| 41 |  |  |  |  |  |  | exists $args{validate_responses}   ? !!$args{validate_responses} | 
| 42 |  |  |  |  |  |  | : exists $conf->{validate_responses} ? !!$conf->{validate_responses} | 
| 43 |  |  |  |  |  |  | :                                      $validate_spec; | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | # parse Swagger2 file | 
| 46 |  |  |  |  |  |  | my $spec = Swagger2->new($url)->expand; | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | if ( $validate_spec or $validate_requests or $validate_responses ) { | 
| 49 |  |  |  |  |  |  | if ( my @errors = $spec->validate ) { | 
| 50 |  |  |  |  |  |  | if ($validate_spec) { | 
| 51 |  |  |  |  |  |  | die join "\n" => "Swagger2: Invalid spec:", @errors; | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  | else { | 
| 54 |  |  |  |  |  |  | warn "Spec contains errors but" | 
| 55 |  |  |  |  |  |  | . " request/response validation is enabled!"; | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | my $basePath = $spec->api_spec->get('/basePath'); | 
| 61 |  |  |  |  |  |  | my $paths    = $spec->api_spec->get('/paths');    # TODO might be undef? | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | while ( my ( $path => $path_spec ) = each %$paths ) { | 
| 64 |  |  |  |  |  |  | my $dancer2_path = $path; | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | $basePath and $dancer2_path = $basePath . $dancer2_path; | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | # adapt Swagger2 syntax for URL path arguments to Dancer2 syntax | 
| 69 |  |  |  |  |  |  | # '/path/{argument}' -> '/path/:argument' | 
| 70 |  |  |  |  |  |  | $dancer2_path =~ s/\{([^{}]+?)\}/:$1/g; | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | my @http_methods = sort keys %$path_spec; | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | if ($create_options_route) { | 
| 75 |  |  |  |  |  |  | my $allow_methods = join ', ' => 'OPTIONS', map uc, @http_methods; | 
| 76 |  |  |  |  |  |  | $dsl->options( | 
| 77 |  |  |  |  |  |  | $dancer2_path => sub { | 
| 78 |  |  |  |  |  |  | $dsl->headers( | 
| 79 |  |  |  |  |  |  | Allow => $allow_methods,    # RFC 2616 HTTP/1.1 | 
| 80 |  |  |  |  |  |  | 'Access-Control-Allow-Methods' => $allow_methods, # CORS | 
| 81 |  |  |  |  |  |  | 'Access-Control-Max-Age'       => 60 * 60 * 24, | 
| 82 |  |  |  |  |  |  | ); | 
| 83 |  |  |  |  |  |  | }, | 
| 84 |  |  |  |  |  |  | ); | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | for my $http_method (@http_methods) { | 
| 88 |  |  |  |  |  |  | my $method_spec = $path_spec->{ $http_method }; | 
| 89 |  |  |  |  |  |  | my $coderef = $controller_factory->( | 
| 90 |  |  |  |  |  |  | $method_spec, $http_method, $path, $dsl, $conf, \%args | 
| 91 |  |  |  |  |  |  | ) or next; | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | DEBUG and warn "Add route $http_method $dancer2_path"; | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | my $params = $method_spec->{parameters}; | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | # Dancer2 DSL keyword is different from HTTP method | 
| 98 |  |  |  |  |  |  | $http_method eq 'delete' and $http_method = 'del'; | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | $dsl->$http_method( | 
| 101 |  |  |  |  |  |  | $dancer2_path => sub { | 
| 102 |  |  |  |  |  |  | if ($validate_requests) { | 
| 103 |  |  |  |  |  |  | my @errors = | 
| 104 |  |  |  |  |  |  | _validate_request( $method_spec, $dsl->request ); | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | if (@errors) { | 
| 107 |  |  |  |  |  |  | DEBUG and warn "Invalid request: @errors\n"; | 
| 108 |  |  |  |  |  |  | $dsl->status(400); | 
| 109 |  |  |  |  |  |  | return { errors => [ map { "$_" } @errors ] }; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | my $result = $coderef->(); | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | if ($validate_responses) { | 
| 116 |  |  |  |  |  |  | my @errors = | 
| 117 |  |  |  |  |  |  | _validate_response( $method_spec, $dsl->response, | 
| 118 |  |  |  |  |  |  | $result ); | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | if (@errors) { | 
| 121 |  |  |  |  |  |  | DEBUG and warn "Invalid response: @errors\n"; | 
| 122 |  |  |  |  |  |  | $dsl->status(500); | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | # TODO hide details of server-side errors? | 
| 125 |  |  |  |  |  |  | return { errors => [ map { "$_" } @errors ] }; | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | return $result; | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  | ); | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  | }; | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | register_plugin; | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub _validate_request { | 
| 139 | 4 |  |  | 4 |  | 3158 | my ( $method_spec, $request ) = @_; | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 4 |  |  |  |  | 5 | my @errors; | 
| 142 |  |  |  |  |  |  |  | 
| 143 | 4 |  |  |  |  | 5 | for my $parameter_spec ( @{ $method_spec->{parameters} } ) { | 
|  | 4 |  |  |  |  | 7 |  | 
| 144 | 4 |  |  |  |  | 4 | my $in       = $parameter_spec->{in}; | 
| 145 | 4 |  |  |  |  | 4 | my $name     = $parameter_spec->{name}; | 
| 146 | 4 |  |  |  |  | 4 | my $required = $parameter_spec->{required}; | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 4 | 50 |  |  |  | 7 | if ( $in eq 'body' ) {    # complex data structure in HTTP body | 
| 149 | 0 |  |  |  |  | 0 | my $input  = $request->data; | 
| 150 | 0 |  |  |  |  | 0 | my $schema = $parameter_spec->{schema}; | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 0 |  |  |  |  | 0 | push @errors, _validator()->validate_input( $input, $schema ); | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  | else {    # simple key-value-pair in HTTP header/query/path/form | 
| 155 | 4 |  |  |  |  | 4 | my $type = $parameter_spec->{type}; | 
| 156 | 4 |  |  |  |  | 4 | my @values; | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 4 | 50 |  |  |  | 9 | if ( $in eq 'header' ) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 159 | 0 |  |  |  |  | 0 | @values = $request->header($name); | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  | elsif ( $in eq 'query' ) { | 
| 162 | 4 |  |  |  |  | 15 | @values = $request->query_parameters->get_all($name); | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | elsif ( $in eq 'path' ) { | 
| 165 | 0 |  |  |  |  | 0 | @values = $request->route_parameters->get_all($name); | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  | elsif ( $in eq 'formData' ) { | 
| 168 | 0 |  |  |  |  | 0 | @values = $request->body_parameters->get_all($name); | 
| 169 |  |  |  |  |  |  | } | 
| 170 | 0 |  |  |  |  | 0 | else { die "Unknown value for property 'in' of parameter '$name'" } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | # TODO align error messages to output style of SchemaValidator | 
| 173 | 4 | 100 | 100 |  |  | 171 | if ( @values == 0 and $required ) { | 
|  |  | 100 |  |  |  |  |  | 
| 174 | 1 | 50 |  |  |  | 4 | $required and push @errors, "No value for parameter '$name'"; | 
| 175 | 1 |  |  |  |  | 3 | next; | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  | elsif ( @values > 1 ) { | 
| 178 | 1 |  |  |  |  | 3 | push @errors, "Multiple values for parameter '$name'"; | 
| 179 | 1 |  |  |  |  | 3 | next; | 
| 180 |  |  |  |  |  |  | } | 
| 181 |  |  |  |  |  |  |  | 
| 182 | 2 |  |  |  |  | 3 | my $value  = $values[0]; | 
| 183 | 2 |  |  |  |  | 4 | my %input  = ( $name => $value ); | 
| 184 | 2 |  |  |  |  | 5 | my %schema = ( properties => { $name => $parameter_spec } ); | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 2 | 50 |  |  |  | 4 | $required and $schema{required} = [$name]; | 
| 187 |  |  |  |  |  |  |  | 
| 188 | 2 |  |  |  |  | 4 | push @errors, _validator()->validate_input( \%input, \%schema ); | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 | 4 |  |  |  |  | 349 | return @errors; | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | sub _validate_response { | 
| 196 | 3 |  |  | 3 |  | 1509 | my ( $method_spec, $response, $result ) = @_; | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 3 |  |  |  |  | 6 | my $responses = $method_spec->{responses}; | 
| 199 | 3 |  |  |  |  | 18 | my $status    = $response->status; | 
| 200 |  |  |  |  |  |  |  | 
| 201 | 3 |  |  |  |  | 124 | my @errors; | 
| 202 |  |  |  |  |  |  |  | 
| 203 | 3 | 50 | 33 |  |  | 11 | if ( my $response_spec = $responses->{$status} || $responses->{default} ) { | 
| 204 |  |  |  |  |  |  |  | 
| 205 | 3 |  |  |  |  | 3 | my $headers = $response_spec->{headers}; | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 3 |  |  |  |  | 12 | while ( my ( $name => $header_spec ) = each %$headers ) { | 
| 208 | 2 |  |  |  |  | 9 | my @values = $response->header($name); | 
| 209 |  |  |  |  |  |  |  | 
| 210 | 2 | 50 |  |  |  | 71 | if ( $header_spec->{type} eq 'array' ) { | 
| 211 | 0 |  |  |  |  | 0 | push @errors, | 
| 212 |  |  |  |  |  |  | _validator()->validate_input( \@values, $header_spec ); | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  | else { | 
| 215 | 2 | 50 |  |  |  | 10 | if ( @values == 0 ) { | 
|  |  | 50 |  |  |  |  |  | 
| 216 | 0 |  |  |  |  | 0 | next;    # you can't make a header 'required' in Swagger2 | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  | elsif ( @values > 1 ) { | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | # TODO align error message to output style of SchemaValidator | 
| 221 | 0 |  |  |  |  | 0 | push @errors, "header '$name' has multiple values"; | 
| 222 | 0 |  |  |  |  | 0 | next; | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 2 |  |  |  |  | 5 | push @errors, | 
| 226 |  |  |  |  |  |  | _validator()->validate_input( $values[0], $header_spec ); | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  |  | 
| 230 | 3 | 100 |  |  |  | 197 | if ( my $schema = $response_spec->{schema} ) { | 
| 231 | 1 |  |  |  |  | 2 | push @errors, _validator()->validate_input( $result, $schema ); | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  | } | 
| 234 |  |  |  |  |  |  | else { | 
| 235 |  |  |  |  |  |  | # TODO Call validate_input($response, {}) like | 
| 236 |  |  |  |  |  |  | #      in Mojolicious::Plugin::Swagger2? | 
| 237 |  |  |  |  |  |  | # Swagger2-0.71/lib/Mojolicious/Plugin/Swagger2.pm line L315 | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 3 |  |  |  |  | 207 | return @errors; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | sub _default_controller_factory { | 
| 245 |  |  |  |  |  |  | # TODO simplify argument list | 
| 246 | 0 |  |  | 0 |  | 0 | my ( $method_spec, $http_method, $path, $dsl, $conf, $args, ) = @_; | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | # from Dancer2 app | 
| 249 | 0 |  | 0 |  |  | 0 | my $namespace = $args->{controller} || $conf->{controller}; | 
| 250 | 0 |  |  |  |  | 0 | my $app = $dsl->app->name; | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | # from Swagger2 file | 
| 253 | 0 |  |  |  |  | 0 | my $module; | 
| 254 | 0 |  |  |  |  | 0 | my $method = $method_spec->{operationId}; | 
| 255 | 0 | 0 |  |  |  | 0 | if ( $method =~ s/^(.+)::// ) {    # looks like Perl module | 
| 256 | 0 |  |  |  |  | 0 | $module = $1; | 
| 257 |  |  |  |  |  |  | } | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | # different candidates possibly reflecting operationId | 
| 260 | 0 |  |  |  |  | 0 | my @controller_candidates = do { | 
| 261 | 0 | 0 |  |  |  | 0 | if ($namespace) { | 
| 262 | 0 | 0 |  |  |  | 0 | if ($module) { $namespace . '::' . $module, $module } | 
|  | 0 |  |  |  |  | 0 |  | 
| 263 | 0 |  |  |  |  | 0 | else         { $namespace } | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  | else { | 
| 266 | 0 | 0 |  |  |  | 0 | if ($module) { | 
| 267 |  |  |  |  |  |  | (                      # parens for better layout by Perl::Tidy | 
| 268 | 0 |  |  |  |  | 0 | $app . '::' . $module, | 
| 269 |  |  |  |  |  |  | $app . '::Controller::' . $module, | 
| 270 |  |  |  |  |  |  | $module,           # maybe a top level module name? | 
| 271 |  |  |  |  |  |  | ); | 
| 272 |  |  |  |  |  |  | } | 
| 273 | 0 |  |  |  |  | 0 | else { $app, $app . '::Controller' } | 
| 274 |  |  |  |  |  |  | } | 
| 275 |  |  |  |  |  |  | }; | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | # check candidates | 
| 278 | 0 |  |  |  |  | 0 | for my $controller (@controller_candidates) { | 
| 279 | 0 |  |  |  |  | 0 | local $@; | 
| 280 | 0 |  |  |  |  | 0 | eval { load $controller }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 281 | 0 | 0 |  |  |  | 0 | if ($@) { | 
| 282 | 0 | 0 |  |  |  | 0 | if ( $@ =~ m/^Can't locate / ) {    # module doesn't exist | 
| 283 | 0 | 0 |  |  |  | 0 | DEBUG and warn "Can't load '$controller'"; | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | # don't do `next` here because controller could be | 
| 286 |  |  |  |  |  |  | # defined in other package ... | 
| 287 |  |  |  |  |  |  | } | 
| 288 |  |  |  |  |  |  | else {    # module doesn't compile | 
| 289 | 0 |  |  |  |  | 0 | die $@; | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  | } | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 0 | 0 |  |  |  | 0 | if ( my $cb = $controller->can($method) ) { | 
| 294 | 0 |  |  |  |  | 0 | return $cb;    # confirmed candidate | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  | else { | 
| 297 | 0 | 0 |  |  |  | 0 | DEBUG and warn "Controller '$controller' can't '$method'"; | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  | } | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | # none found | 
| 302 | 0 |  |  |  |  | 0 | warn "Can't find any handler for operationId '$method_spec->{operationId}'"; | 
| 303 | 0 |  |  |  |  | 0 | return; | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | my $validator; | 
| 307 | 5 |  | 66 | 5 |  | 57 | sub _validator { $validator ||= Swagger2::SchemaValidator->new } | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | 1; | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | __END__ |