File Coverage

blib/lib/PBib/Document.pm
Criterion Covered Total %
statement 191 261 73.1
branch 36 96 37.5
condition 4 17 23.5
subroutine 56 69 81.1
pod 18 55 32.7
total 305 498 61.2


line stmt bran cond sub pod time code
1             # --*-Perl-*--
2             # $Id: Document.pm 25 2005-09-17 21:45:54Z tandler $
3             #
4              
5             =head1 NAME
6              
7             PBib::Document - Abstract Base and Factory class for Documents
8              
9             =head1 SYNOPSIS
10              
11             use PBib::Document;
12             my $doc = new PBib::Document(
13             'filename' => $filename,
14             'mode' => 'r',
15             'verbose' => 1,
16             );
17             print $doc->filename();
18             my @paragraphs = $doc->paragraphs();
19             $doc->close();
20              
21             =head1 DESCRIPTION
22            
23             Factory class to create documents that are processed by PBib.
24            
25             =cut
26              
27             package PBib::Document;
28 1     1   31 use 5.006;
  1         4  
  1         42  
29 1     1   7 use strict;
  1         2  
  1         50  
30 1     1   6 use warnings;
  1         3  
  1         44  
31             #use English;
32              
33             # for debug:
34             #use Data::Dumper;
35              
36             BEGIN {
37 1     1   6 use vars qw($Revision $VERSION);
  1         2  
  1         120  
38 1 50   1   3 my $major = 1; q$Revision: 25 $ =~ /: (\d+)/; my ($minor) = ($1); $VERSION = "$major." . ($minor<10 ? '0' : '') . $minor;
  1         4  
  1         4  
  1         36  
39             }
40              
41             # superclass
42             #use YYYY;
43             #use vars qw(@ISA);
44             #@ISA = qw(YYYY);
45              
46             # used modules
47 1     1   7 use FileHandle;
  1         2  
  1         10  
48 1     1   451 use File::Spec;
  1         2  
  1         29  
49 1     1   8664 use Encode;
  1         17086  
  1         125  
50 1     1   16 use Carp;
  1         2  
  1         3505  
51              
52             # module variables
53             #use vars qw(mmmm);
54              
55             #
56             #
57             # constructor
58             #
59             #
60            
61             =head1 CONSTRUCTOR
62            
63             =over
64              
65             =item $doc = new PBib::Document('filename' => $filename);
66            
67             Arguments are passed as hash:
68            
69             =over
70              
71             =item B => string for filename
72            
73             =item B => document class to use, e.g. 'RTF' or 'MSWord'
74            
75             If class is not defined, it is tried to be guessed by looking at the file. (Currently, the filename's extension only.)
76            
77             =item B => hash with options passed to ReferenceFormator
78            
79             OBSOLETE??
80            
81             =item B => hash with options passed to BibItemFormator
82            
83             OBSOLETE??
84            
85             =item B => string for ReferenceConverter class
86            
87             OBSOLETE??
88            
89             =item B => string for ReferenceFormator class
90            
91             OBSOLETE??
92            
93             =item B => "r" | "w"
94            
95             Mode for this document, used to open the file.
96             "r" = Read, "w" = Write
97            
98             =item B => If true, produce more verbose output.
99              
100             =back
101            
102             =cut
103              
104             sub new {
105 4     4 1 9 my $self = shift;
106 4         26 my %args = @_;
107 4   33     22 my $class = ref($self) || $self;
108 4 50       12 if( $class eq 'PBib::Document' ) {
109 4         58 $class = findDocumentClass(%args);
110             }
111 4         13 my $doc = bless \%args, $class;
112             #print Dumper $doc;
113 4         18 $doc->initialize();
114 4         17 return $doc;
115             }
116              
117             sub initialize {
118 4     4 0 10 my $self = shift;
119             }
120              
121             sub findDocumentClass {
122 4     4 0 17 my %args = @_;
123 4         9 my $class = $args{'class'};
124 4         10 my $filename = $args{'filename'};
125 4 50       13 unless( defined $class ) {
126 4 50       13 if( defined $filename ) {
127 4 50       33 FILETYPE: {
128 4         6 foreach my $pattern (keys(%{$args{'file-types'} || {}})) {
  4         7  
129 0 0       0 if( $filename =~ /$pattern/i ) {
130 0         0 $class = $args{'file-types'}->{$pattern}; last FILETYPE;
  0         0  
131             }
132             }
133 4 50       27 if( $filename =~ /\.doc$/i ) { $class = 'MSWord'; last FILETYPE; }
  0         0  
  0         0  
134 4 50       18 if( $filename =~ /\.rtf$/i ) { $class = 'RTF'; last FILETYPE; }
  0         0  
  0         0  
135 4 50       15 if( $filename =~ /\.s[tx]w$/i ) { $class = 'OpenOfficeSXW'; last FILETYPE; }
  0         0  
  0         0  
136 4 50       16 if( $filename =~ /\.xml$/i ) { $class = 'XML'; last FILETYPE; }
  0         0  
  0         0  
137 4 50       19 if( $filename =~ /\.xhtml$/i ) { $class = 'XHTML'; last FILETYPE; }
  0         0  
  0         0  
138             # if( $filename =~ /\.html$/i ) { $class = 'XHTML'; last FILETYPE; }
139             }
140             }
141             }
142 4 50       10 if( defined $class ) {
143 0 0       0 unless( $class =~ /::/ ) {
144 0         0 $class = "PBib::Document::$class";
145             }
146             } else {
147 4         8 $class = 'PBib::Document';
148             }
149              
150 4 50       12 if( defined $class ) {
151             #print ("use $class; \$${class}::VERSION\n");
152 1     1   10 my $version = eval("use $class; \$${class}::VERSION");
  1     1   2  
  1     1   21  
  1     1   6  
  1         2  
  1         13  
  1         11  
  1         3  
  1         27  
  1         7  
  1         4  
  1         87  
  4         371  
153 4 50       16 unless( defined $version ) {
154 0         0 croak "Failed to open module $class\n";
155             }
156 4 50       14 print STDERR "using $class version $version\n" if $args{'verbose'};
157             }
158 4         16 return $class;
159             }
160              
161             #
162             #
163             # destructor
164             #
165             #
166              
167             sub DESTROY ($) {
168 0     0   0 my $self = shift;
169 0         0 $self->close();
170             }
171              
172              
173              
174             ##############################################
175            
176             =back
177            
178             =head1 File Handling Methods
179            
180             =over
181              
182             =cut
183            
184             =item $filename = $doc->filename();
185            
186             Return the document's filename.
187            
188             =cut
189              
190 12     12 1 19 sub filename { my $self = shift; return File::Spec->rel2abs($self->{'filename'}); }
  12         665  
191            
192             =item $handle = $doc->handle();
193            
194             Return the document's Perl FileHandle.
195            
196             Caution: This method might not be used by all subclasses of PBib::Document.
197            
198             =cut
199            
200            
201             sub handle {
202             # open file and return handle
203             # per default the handle is a FileHandle, but subclasses may use
204             # other internal handles, like Win32::OLE
205 4     4 1 7 my $self = shift;
206 4         10 my $fh = $self->{'filehandle'};
207 4 50       13 if( not defined($fh) ) {
208 4         18 my $filename = $self->filename();
209 4   50     20 my $mode = $self->{'mode'} || "<";
210 4 50       13 if( defined($filename) ) {
211 4 50       19 print STDERR "Open $filename ($mode)\n" unless $self->{quiet};
212 4         50 $fh = $self->{'filehandle'} = new FileHandle($filename, $mode);
213 4 50       796 if( not defined($fh) ) {
214 0         0 print STDERR "Can't open file $filename\n"; # always print error message(?)
215             }
216             } else {
217 0 0       0 if( $mode eq ">" ) {
218 0 0       0 print STDERR "Write to stdout\n" unless $self->{quiet};
219 0         0 $filename = "> -";
220             } else {
221 0 0       0 print STDERR "Read from stdin\n" unless $self->{quiet};
222 0         0 $filename = "< -";
223             }
224 0         0 $fh = $self->{'filehandle'} = new FileHandle($filename);
225 0 0       0 if( not defined($fh) ) {
226 0         0 print STDERR "Can't open stdin or stdout ... strange ...\n";
227             }
228             }
229             }
230 4         20 my $enc = $self->encoding();
231 4 50       13 if( defined($enc) ) {
232 0 0       0 if( Encode::perlio_ok($enc) ) {
233 0 0       0 print STDERR "encoding: $enc\n" if $self->{verbose};
234 0         0 binmode $fh, ":encoding($enc)"
235             } else {
236 0         0 print STDERR "Unsupported file encoding: $enc\n"; # print when quiet?
237             }
238             }
239 4         19 return $fh;
240             }
241            
242             =item $doc->encoding();
243            
244             Return the document's encoding.
245            
246             Returns undef if not specified.
247            
248             =cut
249            
250             sub encoding {
251 4     4 1 12 return undef;
252             }
253            
254             =item $doc->close();
255            
256             Close the file.
257            
258             =cut
259              
260             sub close {
261             # close file
262 8     8 1 14 my $self = shift;
263 8         15 my $fh = $self->{'filehandle'};
264 8 100       23 if( defined($fh) ) { $fh->close(); }
  4         14  
265 8         213 delete $self->{'filehandle'};
266             }
267            
268            
269             =item $doc->read()
270            
271             Read the document from disk. Return the content of the document
272             in a format internal to the document class.
273             (Per default a ref to an array of the lines.)
274            
275             =cut
276              
277             sub read {
278 2     2 1 5 my ($self) = @_;
279 2 50       9 my $fh = $self->handle() or return undef;
280 2         4 my @lines;
281             # don't chomp line ends!
282 2         19523 @lines = <$fh>;
283             # $fh->close(); # close it now or later?
284 2         56 return \@lines;
285             }
286            
287            
288             =item $doc->write()
289            
290             Write the document back to disk, if the document has been opened and it contains paragraphs().
291            
292             =cut
293              
294 2     2 1 4 sub write { my ($self) = @_;
295 2 50       11 my $fh = $self->handle() or return undef;
296 2         3 my @lines = @{$self->paragraphs()};
  2         6  
297 2 50       8 return unless @lines;
298 2         6 foreach my $p (@lines) {
299 1087         5855 $fh->print($p);
300             }
301 2         99 $self->close();
302             }
303              
304             =item $boolean = $doc->exists()
305            
306             Check, if this document is exists, independent of being accessable.
307             (Perl's -f test)
308            
309             =cut
310            
311             sub exists {
312 0     0 1 0 my $self = shift;
313 0   0     0 my $filename = $self->filename() || *STDIN;
314 0         0 return -f $filename;
315             }
316            
317             =item $boolean = $doc->isValid()
318            
319             Check, if this document is ok, e.g. if the file exists and can be accessed.
320             (Perl's -w or -r, depending on $doc->mode()
321              
322             =cut
323              
324             sub isValid {
325 0     0 1 0 my $self = shift;
326 0   0     0 my $filename = $self->filename() || *STDIN;
327 0 0       0 return $self->{'mode'} eq "w" ? -w $filename : -r $filename;
328             }
329            
330            
331             ##############################################
332              
333             =back
334            
335             =head1 Text Access Methods
336            
337             Methods used by PBib to access the document.
338            
339             =over
340              
341             =cut
342              
343             =item $inDoc->processParagraphs($func, $rc, $outDoc, @_)
344            
345             Process all paragraphs of the $inDoc by calling $func. If $outDoc is defined, the result of the function call is added to $outDoc.
346            
347             The default implementation assumes a linear sequence of paragraphs, using $inDoc's paragraphCount() and getParagraph().
348            
349             $func is called on $rc with the current paragraph, its index and @_ as parameters:
350            
351             $par = $rc->$func($par, $i, @_);
352            
353             =cut
354            
355             sub processParagraphs {
356 4     4 1 7 my $self = shift;
357 4         103 my $func = shift;
358 4         8 my $rc = shift;
359 4         5 my $outDoc = shift;
360 4         6 my $par;
361 4         15 my $numPars = $self->paragraphCount();
362 4         18 for( my $i = 0; $i < $numPars; $i++ ) {
363 2174         3032 $self->{currentParagraph} = $i;
364 2174         3556 $par = $self->getParagraph($i);
365 2174         5728 $par = $rc->$func($par, $i, @_);
366 2174 100       6530 $outDoc->addParagraph($par) if $outDoc;
367             }
368             }
369            
370            
371             =item $int = $doc->paragraphCount();
372            
373             Return the number of paragraphs in document.
374            
375             =cut
376            
377             sub paragraphCount {
378             # how many paragraphs does this doc have?
379 6     6 1 11 my $self = shift;
380 6         23 my $pars = $self->paragraphs();
381 6 50       183 return undef if ! defined $pars;
382 6         12 return scalar @{$pars};
  6         26  
383             }
384            
385             =item $string = $doc->getParagraph($int);
386            
387             Return the paragraph with index $int
388            
389             =cut
390            
391             sub getParagraph {
392 2174     2174 1 2689 my ($self, $idx) = @_;
393 2174         3474 return $self->paragraphs()->[$idx];
394             }
395            
396             =item @strings = $doc->paragraphs()
397            
398             Return all paragraphs in document as an array with all paragraphs as plain (ascii) strings.
399            
400             Calles $doc->read() if the file has not been read before.
401            
402             =cut
403              
404             sub paragraphs {
405 2182     2182 1 2541 my $self = shift;
406 2182 100       7870 return $self->{'paragraphs'} if defined($self->{'paragraphs'});
407 2         8 return $self->{'paragraphs'} = $self->read();
408             }
409              
410            
411             =item $doc->addParagraph($str1, $str2, ...), $doc->addParagraphs($str1, $str2, ...)
412            
413             Append paragraphs to document.
414            
415             =cut
416              
417 1087     1087 1 981 sub addParagraph { my $self = shift; return $self->addParagraphs(@_); }
  1087         1864  
418 1087     1087 1 911 sub addParagraphs { my $self = shift;
419 1087         4077 foreach my $p (@_) {
420 1087         901 push @{$self->{'paragraphs'}}, $p;
  1087         4622  
421             }
422             }
423            
424              
425             ##############################################
426            
427             =back
428            
429             =head1 Converting Methods
430            
431             Methods used by L.
432            
433             =over
434              
435             =cut
436            
437            
438             =item $doc->prepareConvert($conv)
439              
440             Do anything you want to before being converted. (Hook for subclasses.)
441             The document object that is returned is used for conversion.
442            
443             This is called by L on the I document with $conv as the current converter.
444            
445             =cut
446            
447             sub prepareConvert {
448 2     2 1 4 my ($self) = @_;
449 2         9 return $self;
450             }
451            
452             =item $doc->finalizeConvert($conv)
453              
454             Do anything you want to after being converted. (Hook for subclasses.)
455             The object that is returned is used for further processing.
456            
457             This is called by L on the I document with $conv as the current converter.
458            
459             =cut
460              
461             sub finalizeConvert {
462 2     2 1 6 my ($self) = @_;
463 2         8 return $self;
464             }
465              
466            
467             ##############################################
468            
469             =back
470            
471             =head1 Formatting Methods
472            
473             Methods used by PBib to create formatted text.
474            
475             =over
476              
477             =cut
478            
479              
480             #
481             #
482             # char set converting methods
483             #
484             #
485              
486 250     250 0 315 sub quote { my ($self, $text) = @_; return $text; }
  250         1023  
487 299     299 0 408 sub unquote { my ($self, $text) = @_; return $text; }
  299         734  
488              
489 253     253 0 353 sub quoteFieldId { my ($self, $text) = @_;
490             #
491             # return a valid field ID
492             #
493 253         657 return $text;
494             }
495              
496             #
497             #
498             # text formating methods
499             #
500             #
501              
502             sub formatRange {
503 84     84 0 107 my ($self, $text) = @_;
504 84 50       498 $text =~ s/\s*-(-?)\s*/-/g if defined $text;
505 84         269 return $text;
506             }
507              
508             sub paragraph {
509 2     2 0 3 my ($self, $text) = @_;
510 2 50       6 $text = '' unless $text;
511 2         144 return "$text\n";
512             }
513             sub linebreak {
514 0     0 0 0 my ($self, $text) = @_;
515 0 0       0 $text = '' unless $text;
516 0         0 return "$text\n";
517             }
518 0     0 0 0 sub singleQuotes { my ($self, $text) = @_;
519             # return $text enclosed in single quotes
520 0         0 return "'$text'";
521             }
522 0     0 0 0 sub doubleQuotes { my ($self, $text) = @_;
523             # return $text enclosed in double quotes
524 0         0 return "\"$text\"";
525             }
526              
527             # text styles
528              
529 58     58 0 71 sub italic { my ($self, $text) = @_;
530             # return $text as italic
531 58         291 return $text;
532             }
533 2     2 0 5 sub bold { my ($self, $text) = @_;
534             # return $text as bold
535 2         11 return $text;
536             }
537 0     0 0 0 sub underline { my ($self, $text) = @_;
538             # return $text as underlined
539 0         0 return $text;
540             }
541              
542 2     2 0 3 sub highlight { my ($self, $text) = @_;
543             # return $text highlighted, whatever this means.
544             # It could be bold + italic, or colored etc.
545 2         8 return $self->bold($self->italic($text));
546             }
547              
548             # fonts
549              
550 0     0 0 0 sub tt { my ($self, $text) = @_;
551             # return text in typewriter (Courier) font
552 0         0 return $text;
553             }
554              
555             # fields
556              
557             sub bookmark {
558             # return $text marked as bookmark (with $id as bookmark)
559 112     112 0 261 my ($self, $text, $id) = @_;
560 112         912 return $text;
561             }
562              
563             sub bookmarkLink {
564             # return $text marked as a hyperlink to bookmark $id
565 141     141 0 205 my ($self, $text, $id) = @_;
566 141         578 return $text;
567             }
568              
569             sub hyperlink {
570             # return $text marked as a hyperlink to $url
571 66     66 0 88 my ($self, $text, $url) = @_;
572 66 50       132 $url = $text unless( $url );
573 66 50       437 return $text eq $url ? $text : "$text ($url)";
574             }
575              
576 0     0 0 0 sub comment { my ($self, $text) = @_;
577 0         0 return $self->bold($self->italic($text));
578             }
579              
580             #
581             #
582             # bibliography formating methods
583             #
584             #
585              
586              
587 2     2 0 3 sub bibitems_start { my ($self) = @_; return ""; }
  2         8  
588 2     2 0 3 sub bibitems_separator { my ($self) = @_; return $self->paragraph(); }
  2         8  
589 2     2 0 5 sub bibitems_end { my ($self) = @_; return ""; }
  2         124  
590              
591 112     112 0 120 sub block_start { my ($self) = @_; return ""; }
  112         254  
592 112     112 0 110 sub block_separator { my ($self) = @_; return ' '; }
  112         315  
593 112     112 0 117 sub block_end { my ($self) = @_; return ""; }
  112         238  
594              
595 176     176 0 187 sub sentence_start { my ($self) = @_; return ""; }
  176         337  
596 176     176 0 174 sub sentence_separator { my ($self) = @_; return ". "; }
  176         468  
597 176     176 0 179 sub sentence_end { my ($self) = @_; return "."; }
  176         911  
598              
599 298     298 0 289 sub phrase_start { my ($self) = @_; return ""; }
  298         540  
600 298     298 0 281 sub phrase_separator { my ($self) = @_; return ", "; }
  298         487  
601 298     298 0 275 sub phrase_end { my ($self) = @_; return ""; }
  298         1299  
602              
603 130     130 0 133 sub spaceConnect { my $self = shift;
604             # connect all args with spaces
605 130         212 return join($self->quote(" "), @_);
606             }
607 108     108 0 137 sub tieConnect { my ($self, $a, $b) = @_;
608             # use non-breaking-space
609 108         183 return $self->spaceConnect($a, $b);
610             }
611 108     108 0 155 sub tieOrSpaceConnect { my ($self, $a, $b) = @_;
612             # use non-breaking-space, if $a or $b is short (i.e. < 3 characters)
613 108 0 33     185 return '' if ! defined $a && ! defined $b;
614 108 50       164 return $a if ! defined $b;
615 108 50       197 return $b if ! defined $a;
616 108 50 33     385 return length($a) < 5 || length($b) < 3 ?
617             $self->tieConnect($a, $b) :
618             $self->spaceConnect($a, $b);
619             }
620              
621              
622             ##############################################
623            
624             =back
625            
626             =head1 Interactive Editing Methods
627            
628             Methods used by PBib for interactive editing of documents, e.g. open in editor.
629            
630             =over
631              
632             =cut
633            
634              
635 0     0 0   sub openInEditor { my ($self) = @_;
636 0           my $filename = $self->filename();
637 0 0         if( not defined($filename) ) {
638 0           print STDERR "can't open document with no filename specified.\n";
639 0           return;
640             }
641 0           PBib::Document::openFile($filename);
642             }
643              
644             sub jumpToBookmark {
645 0     0 0   my ($self, $bookmark) = @_;
646             # this feature require some interaction with an appropriate editor
647             # application for this kind of document
648             # open the document in an editor, and jump to the given bookmark
649 0           $self->openInEditor();
650             #### Selection.GoTo What:=wdGoToBookmark, Name:="BookMark" ###, Which:=???
651             }
652              
653 0     0 0   sub searchInEditor { my ($self, $text) = @_;
654 0           $self->openInEditor();
655             }
656              
657            
658             ##############################################
659            
660             =back
661            
662             =head1 Class Methods
663            
664             =over
665              
666             =cut
667            
668             =item PBib::Document::openFile($filename);
669            
670             Open the file with it's associated default application.
671            
672             =cut
673              
674             sub openFile {
675 0     0 1   my $filename = shift;
676             # fork currently crashes ...
677 0           system($filename);
678 0           return;
679 0           my $pid = fork();
680 0 0         if( not defined($pid) ) {
681 0           print STDERR "fork failed opening $filename\n";
682 0           return;
683             }
684 0 0         if( $pid == 0 ) {
685 0 0         exec($filename) or print STDERR "exec $filename failed.\n";
686             }
687 0           return $pid;
688             }
689            
690            
691             =back
692            
693             =cut
694              
695             1;
696              
697             #
698             # $Log: Document.pm,v $
699             # Revision 1.15 2003/06/12 22:06:01 tandler
700             # support prepareConvert() / finalizeConvert()
701             #
702             # Revision 1.14 2003/05/22 11:54:23 tandler
703             # remove spaces around the dash (-) in page ranges (e.g. 3 - 7 ==> 3-7)
704             #
705             # Revision 1.13 2002/11/05 18:29:13 peter
706             # space/tie connect
707             #
708             # Revision 1.12 2002/11/03 22:14:51 peter
709             # minor
710             #
711             # Revision 1.11 2002/10/11 10:13:13 peter
712             # minor fixes and cleanup
713             #
714             # Revision 1.10 2002/10/01 21:27:22 ptandler
715             # fix: paragraph count with non-existing document
716             #
717             # Revision 1.9 2002/07/16 17:35:41 Diss
718             # check if file exists
719             #
720             # Revision 1.8 2002/06/24 10:42:14 Diss
721             # use PBib::Document as default class for all unknown file types
722             #
723             # Revision 1.7 2002/06/06 10:23:59 Diss
724             # searchInEditor support - jump to CiteKeys in editor
725             # (litUI uses PBib::Doc classes)
726             #
727             # Revision 1.6 2002/06/06 09:02:00 Diss
728             # constructor can now be used as uniform interface for several
729             # document types (RTF, MSWord)
730             #
731             # Revision 1.5 2002/05/27 10:22:41 Diss
732             # started editing support
733             #
734             # Revision 1.4 2002/04/03 10:18:24 Diss
735             # - new method 'highlight'
736             #
737             # Revision 1.3 2002/03/27 10:00:50 Diss
738             # new module structure, not yet included in LitRefs/LitUI (R2)
739             #
740             # Revision 1.2 2002/03/22 17:31:02 Diss
741             # small changes
742             #
743             # Revision 1.1 2002/03/18 11:15:50 Diss
744             # major additions: replace [] refs, generate bibliography using [{}], ...
745             #