File Coverage

blib/lib/SWISH/Filter/Document.pm
Criterion Covered Total %
statement 18 109 16.5
branch 0 48 0.0
condition 0 17 0.0
subroutine 6 26 23.0
pod 8 16 50.0
total 32 216 14.8


line stmt bran cond sub pod time code
1             package SWISH::Filter::Document;
2 2     2   8 use strict;
  2         2  
  2         70  
3 2     2   8 use Carp;
  2         1  
  2         121  
4 2     2   1485 use File::Temp;
  2         31224  
  2         203  
5 2     2   14 use Symbol;
  2         2  
  2         97  
6              
7 2     2   9 use vars qw/ $VERSION $AUTOLOAD /;
  2         3  
  2         1768  
8              
9             $VERSION = '0.191';
10              
11             # Map content types to swish-e parsers.
12              
13             my %swish_parser_types = (
14             'text/html' => 'HTML*',
15             'text/xml' => 'XML*',
16             'application/xml' => 'XML*',
17             'text/plain' => 'TXT*',
18             );
19              
20             =pod
21              
22             =head1 NAME
23              
24             SWISH::Filter::Document - object model for result of SWISH::Filter
25              
26             =head1 DESCRIPTION
27              
28             A SWISH::Filter::Document object is returned by the SWISH::Filter convert()
29             method. This class is intended to be used privately, but you might subclass it
30             in order to extend or modify its behaviour.
31              
32             =head1 METHODS
33              
34             These methods are available to Filter authors, and also provide access to the
35             document after calling the convert() method to end-users of SWISH::Filter.
36              
37             End users of SWISH::Filter will use a subset of these methods. See L.
38              
39             Filter authors will also be interested in the L secion.
40             The filter() method in each
41             Filter module is passed a SWISH::Filter::Document object. Method calls may be made on this
42             object to check the document's current content type, or to fetch the document as either a
43             file name or a reference to a scalar containing the document content.
44              
45             =cut
46              
47             # Returns a new SWISH::Filter::document object
48             # or null if just can't process the document
49              
50             sub new {
51 0     0 0   my ( $class, $doc, $content_type ) = @_;
52              
53 0 0 0       return unless $doc && $content_type;
54              
55 0           my $self = bless {}, $class;
56              
57 0 0         if ( ref $doc ) {
58 0 0         unless ( length $$doc ) {
59 0           warn "Empty document passed to filter\n";
60 0           return;
61             }
62              
63             croak
64 0 0         "Must supply a content type when passing in a reference to a document\n"
65             unless $content_type;
66              
67             }
68             else {
69              
70 0 0         unless ( -r $doc ) {
71 0           warn "Filter unable to read doc '$doc': $!\n";
72 0           return;
73             }
74             }
75              
76 0           $self->set_content_type($content_type);
77              
78 0           $self->{cur_doc} = $doc;
79              
80 0           return $self;
81             }
82              
83             # Clean up any temporary files
84              
85             sub DESTROY {
86 0     0     my $self = shift;
87 0           $self->remove_temp_file;
88             }
89              
90             sub cur_doc {
91 0     0 0   my ( $self, $doc_ref ) = @_;
92 0 0         $self->{cur_doc} = $doc_ref if $doc_ref;
93 0           return $self->{cur_doc};
94             }
95              
96             sub remove_temp_file {
97 0     0 0   my $self = shift;
98              
99 0 0         unless ( $ENV{FILTER_DEBUG} ) {
100 0 0         unlink delete $self->{temp_file} if $self->{temp_file};
101             }
102             }
103              
104             # Used for tracking what filter(s) were used in processing
105              
106             sub filters_used {
107 0     0 0   my $self = shift;
108 0   0       return $self->{filters_used} || undef;
109             }
110              
111             sub dump_filters_used {
112 0     0 0   my $self = shift;
113 0           my $used = $self->filters_used;
114              
115 0           local $SIG{__WARN__};
116 0           warn "\nFinal Content type for ", $self->name, " is ",
117             $self->content_type,
118             "\n";
119              
120 0 0         unless ($used) {
121 0           warn " *No filters were used\n";
122 0           return;
123             }
124              
125             warn
126             " >Filter $_->{name} converted from [$_->{start_content_type}] to [$_->{end_content_type}]\n"
127 0           for @$used;
128             }
129              
130             =head1 User Methods
131              
132             These methods are intended primarily for those folks using SWISH::Filter. If you are
133             writing a filter, see also L.
134              
135             =head2 fetch_doc_reference
136              
137             Returns a scalar reference to the document. This can be used when the filter
138             can operate on the document in memory (or if an external program expects the input
139             to be from standard input).
140              
141             If the file is currently on disk then it will be read into memory. If the file was stored
142             in a temporary file on disk the file will be deleted once read into memory.
143             The file will be read in binmode if $doc-Eis_binary is true.
144              
145             Note that fetch_doc() is an alias.
146              
147             =cut
148              
149             sub fetch_doc_reference {
150 0     0 1   my ($self) = @_;
151              
152 0 0         return ref $self->{cur_doc} # just $self->read_file should work
153             ? $self->{cur_doc}
154             : $self->read_file;
155             }
156              
157             # here's an alias for fetching a document reference.
158              
159             *fetch_doc = *fetch_doc_reference;
160              
161             =head2 was_filtered
162              
163             Returns true if some filter processed the document
164              
165             =cut
166              
167             sub was_filtered {
168 0     0 1   my $self = shift;
169 0 0         return $self->filters_used ? 1 : 0;
170             }
171              
172             =head2 content_type
173              
174             Fetches the current content type for the document.
175              
176             Example:
177              
178             return unless $filter->content_type =~ m!application/pdf!;
179              
180              
181             =cut
182              
183             sub content_type {
184 0   0 0 1   return $_[0]->{content_type} || '';
185             }
186              
187             =head2 swish_parser_type
188              
189             Returns a parser type based on the content type. Returns undef
190             if no parser type is mapped.
191              
192             =cut
193              
194             sub swish_parser_type {
195 0     0 1   my $self = shift;
196              
197 0   0       my $content_type = $self->content_type || return;
198              
199 0           for ( keys %swish_parser_types ) {
200 0 0         return $swish_parser_types{$_}
201             if $content_type =~ /^\Q$_/;
202             }
203              
204 0           return;
205             }
206              
207             =head2 is_binary
208              
209             Returns true if the document's content-type does not match "text/".
210              
211             =cut
212              
213             sub is_binary {
214 0     0 1   my $self = shift;
215 0           return $self->content_type !~ m[^text|xml$];
216             }
217              
218             =head1 Author Methods
219              
220             These methods are intended for those folks writing filters.
221              
222             =head2 fetch_filename
223              
224             Returns a path to the document as stored on disk.
225             This name can be passed to external programs (e.g. C) that expect input
226             as a file name.
227              
228             If the document is currently in memory then a temporary file will be created. Do not expect
229             the file name passed to be the real path of the document.
230              
231             The file will be written in binmode if is_binary() returns true.
232              
233             This method is not normally used by end-users of SWISH::Filter.
234              
235             =cut
236              
237             # This will create a temporary file if file is in memory
238              
239             sub fetch_filename {
240 0     0 1   my ($self) = @_;
241              
242 0 0         return ref $self->{cur_doc}
243             ? $self->create_temp_file
244             : $self->{cur_doc};
245             }
246              
247             =head2 set_continue
248              
249             Processing will continue to the next filter if this is set to a true value.
250             This should be set for filters that change encodings or uncompress documents.
251              
252             =cut
253              
254             sub set_continue {
255 0     0 1   my ($self) = @_;
256 0           return $self->continue(1);
257             }
258              
259             sub continue {
260 0     0 0   my ( $self, $continue ) = @_;
261 0   0       my $old = $self->{continue} || 0;
262 0 0         $self->{continue}++ if $continue;
263 0           return $old;
264             }
265              
266             =head2 set_content_type( I );
267              
268             Sets the content type for a document.
269              
270             =cut
271              
272             sub set_content_type {
273 0     0 1   my ( $self, $type ) = @_;
274 0 0         croak "Failed to pass in new content type\n" unless $type;
275 0           $self->{content_type} = $type;
276             }
277              
278             sub read_file {
279 0     0 0   my $self = shift;
280 0           my $doc = $self->{cur_doc};
281 0 0         return $doc if ref $doc;
282              
283 0           my $sym = gensym();
284 0 0         open( $sym, "<$doc" ) or croak "Failed to open file '$doc': $!";
285 0 0         binmode $sym if $self->is_binary;
286 0           local $/ = undef;
287 0           my $content = <$sym>;
288 0           close $sym;
289 0           $self->{cur_doc} = \$content;
290              
291             # Remove the temporary file, if one was created.
292 0           $self->remove_temp_file;
293              
294 0           return $self->{cur_doc};
295             }
296              
297             # write file out to a temporary file
298              
299             sub create_temp_file {
300 0     0 0   my $self = shift;
301 0           my $doc = $self->{cur_doc};
302              
303 0 0         return $doc unless ref $doc;
304              
305 0           my ( $fh, $file_name ) = File::Temp::tempfile();
306              
307             # assume binmode if we need to filter...
308 0 0         binmode $fh if $self->is_binary;
309              
310 0 0         print $fh $$doc or croak "Failed to write to '$file_name': $!";
311 0 0         close $fh or croak "Failed to close '$file_name' $!";
312              
313 0           $self->{cur_doc} = $file_name;
314 0           $self->{temp_file} = $file_name;
315              
316 0           return $file_name;
317             }
318              
319             =head2 name
320              
321             Fetches the name of the current file. This is useful for printing out the
322             name of the file in an error message.
323             This is the name passed in to the SWISH::Filter convert() method.
324             It is optional and thus may not always be set.
325              
326             my $name = $doc_object->name || 'Unknown name';
327             warn "File '$name': failed to convert -- file may be corrupt\n";
328              
329              
330             =head2 user_data
331              
332             Fetches the the user_data passed in to the filter.
333             This can be any data or data structure passed into SWISH::Filter new().
334              
335             This is an easy way to pass special parameters into your filters.
336              
337             Example:
338              
339             my $data = $doc_object->user_data;
340             # see if a choice for the was passed in </td> </tr> <tr> <td class="h" > <a name="341">341</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> if ( ref $data eq 'HASH' && $data->{pdf2html}{title_field} { </td> </tr> <tr> <td class="h" > <a name="342">342</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ... </td> </tr> <tr> <td class="h" > <a name="343">343</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ... </td> </tr> <tr> <td class="h" > <a name="344">344</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> } </td> </tr> <tr> <td class="h" > <a name="345">345</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="346">346</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =cut </td> </tr> <tr> <td class="h" > <a name="347">347</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="348">348</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 meta_data </td> </tr> <tr> <td class="h" > <a name="349">349</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="350">350</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Similar to user_data() but specifically intended for name/value pairs </td> </tr> <tr> <td class="h" > <a name="351">351</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> in the C<meta> tags in HTML or XML documents. </td> </tr> <tr> <td class="h" > <a name="352">352</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="353">353</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> If set, either via new() or explicitly via the meta_data() method, </td> </tr> <tr> <td class="h" > <a name="354">354</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the value of meta_data() can be used in a filter to set meta headers. </td> </tr> <tr> <td class="h" > <a name="355">355</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="356">356</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The value of meta_data() should be a hash ref so that it is easy to pass </td> </tr> <tr> <td class="h" > <a name="357">357</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to SWISH::Filters::Base->format_meta_headers(). </td> </tr> <tr> <td class="h" > <a name="358">358</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="359">359</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> After a document is filtered, the meta_data() method can be used to retrieve </td> </tr> <tr> <td class="h" > <a name="360">360</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the values that the filter inserted into the filtered document. This value is </td> </tr> <tr> <td class="h" > <a name="361">361</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> (again) a hash ref, and is set by the SWISH::Filter module if the filter() </td> </tr> <tr> <td class="h" > <a name="362">362</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> method returns a second value. Because the filter module might also extract </td> </tr> <tr> <td class="h" > <a name="363">363</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> meta data from the document itself, and might insert some of its own, it is up </td> </tr> <tr> <td class="h" > <a name="364">364</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to the individual filter to determine how and what it handles meta data. </td> </tr> <tr> <td class="h" > <a name="365">365</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> See SWISH::Filters::Pdf2HTML for an example. </td> </tr> <tr> <td class="h" > <a name="366">366</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="367">367</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> See the filter() method description in SWISH::Filter, the section on WRITING FILTERS. </td> </tr> <tr> <td class="h" > <a name="368">368</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="369">369</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Example: </td> </tr> <tr> <td class="h" > <a name="370">370</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="371">371</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $doc = $filter->convert( meta_data => {foo => 'bar'} ); </td> </tr> <tr> <td class="h" > <a name="372">372</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $meta = $doc->meta_data; </td> </tr> <tr> <td class="h" > <a name="373">373</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> # $meta *probably* is {foo => 'bar'} but that's up to how the filter handled </td> </tr> <tr> <td class="h" > <a name="374">374</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> # the value passed in convert(). Could also be { foo => 'bar', title => 'some title' } </td> </tr> <tr> <td class="h" > <a name="375">375</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> # for example. </td> </tr> <tr> <td class="h" > <a name="376">376</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="377">377</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =cut </td> </tr> <tr> <td class="h" > <a name="378">378</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="379">379</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sub AUTOLOAD { </td> </tr> <tr> <td class="h" > <a name="380">380</a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td class="c0" > <a href="blib-lib-SWISH-Filter-Document-pm--subroutine.html#380-1"> 0 </a> </td> <td >   </td> <td >   </td> <td class="s"> my ( $self, $newval ) = @_; </td> </tr> <tr> <td class="h" > <a name="381">381</a> </td> <td class="c3" > 2 </td> <td >   </td> <td >   </td> <td class="c3" > <a href="blib-lib-SWISH-Filter-Document-pm--subroutine.html#381-1"> 2 </a> </td> <td >   </td> <td > 12 </td> <td class="s"> no strict 'refs'; </td> </tr> <tr> <td class="h" > <a > </a> </td> <td class="c3" > 2 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td > 3 </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a > </a> </td> <td class="c3" > 2 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td > 364 </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="382">382</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="383">383</a> </td> <td class="c0" > 0 </td> <td class="c0" > <a href="blib-lib-SWISH-Filter-Document-pm--branch.html#383-1"> 0 </a> </td> <td class="c0" > <a href="blib-lib-SWISH-Filter-Document-pm--condition.html#383-1"> 0 </a> </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> if ( $AUTOLOAD =~ /.*::set_(\w+)/ && $SWISH::Filter::extra_methods{$1} ) { </td> </tr> <tr> <td class="h" > <a > </a> </td> <td >   </td> <td class="c0" > <a href="blib-lib-SWISH-Filter-Document-pm--branch.html#-2"> 0 </a> </td> <td class="c0" > <a href="blib-lib-SWISH-Filter-Document-pm--condition.html#-2"> 0 </a> </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="384">384</a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $attr_name = $1; </td> </tr> <tr> <td class="h" > <a name="385">385</a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td class="c0" > <a href="blib-lib-SWISH-Filter-Document-pm--subroutine.html#385-1"> 0 </a> </td> <td >   </td> <td >   </td> <td class="s"> *{$AUTOLOAD} = sub { $_[0]->{$attr_name} = $_[1]; return }; </td> </tr> <tr> <td class="h" > <a > </a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a > </a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a > </a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="386">386</a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> return $self->{$attr_name} = $newval; </td> </tr> <tr> <td class="h" > <a name="387">387</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> } </td> </tr> <tr> <td class="h" > <a name="388">388</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="389">389</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> elsif ( $AUTOLOAD =~ /.*::(\w+)/ && $SWISH::Filter::extra_methods{$1} ) { </td> </tr> <tr> <td class="h" > <a name="390">390</a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $attr_name = $1; </td> </tr> <tr> <td class="h" > <a name="391">391</a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td class="c0" > <a href="blib-lib-SWISH-Filter-Document-pm--subroutine.html#391-1"> 0 </a> </td> <td >   </td> <td >   </td> <td class="s"> *{$AUTOLOAD} = sub { return $_[0]->{$attr_name} }; </td> </tr> <tr> <td class="h" > <a > </a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a > </a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="392">392</a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> return $self->{$attr_name}; </td> </tr> <tr> <td class="h" > <a name="393">393</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> } </td> </tr> <tr> <td class="h" > <a name="394">394</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="395">395</a> </td> <td class="c0" > 0 </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> croak "No such method: $AUTOLOAD\n"; </td> </tr> <tr> <td class="h" > <a name="396">396</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> } </td> </tr> <tr> <td class="h" > <a name="397">397</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="398">398</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 1; </td> </tr> <tr> <td class="h" > <a name="399">399</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="400">400</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> __END__ </td> </tr> </table> </body> </html>