File Coverage

blib/lib/PBib/ReferenceConverter.pm
Criterion Covered Total %
statement 204 228 89.4
branch 53 80 66.2
condition 15 36 41.6
subroutine 45 51 88.2
pod 4 45 8.8
total 321 440 72.9


line stmt bran cond sub pod time code
1             # --*-Perl-*--
2             # $Id: ReferenceConverter.pm 18 2004-12-12 07:41:44Z tandler $
3             #
4            
5             =head1 NAME
6              
7             PBib::ReferenceConverter - Main controller class for processing references within documents
8              
9             =head1 SYNOPSIS
10              
11             use PBib::ReferenceConverter;
12             my $inDoc = new PBib::Document(
13             'filename' => 'sample.xml',
14             'mode' => 'r',
15             );
16             my $outDoc = new PBib::Document(
17             'filename' => 'sample-pbib.xml',
18             'mode' => 'w',
19             );
20             my $conv = new PBib::ReferenceConverter(
21             'inDoc' => $inDoc,
22             'outDoc' => $outDoc,
23             'refStyle' => new PBib::ReferenceStyle(),
24             'labelStyle' => new PBib::LabelStyle(),
25             'bibStyle' => new PBib::BibliographyStyle(),
26             'itemStyle' => new PBib::BibItemStyle(),
27             'verbose' => 1,
28             'quiet' => 0,
29             );
30             $conv->convert($refs);
31             $inDoc->close();
32             $outDoc->close();
33              
34             =head1 DESCRIPTION
35            
36             Main controller class of the PBib system for processing references within documents.
37            
38             See module L for an example how to use it.
39              
40             =cut
41              
42             package PBib::ReferenceConverter;
43 1     1   7 use strict;
  1         3  
  1         41  
44 1     1   6 use warnings;
  1         4  
  1         30  
45             #use English;
46            
47             # for debug:
48 1     1   6 use Data::Dumper;
  1         3  
  1         90  
49            
50             BEGIN {
51 1     1   7 use vars qw($Revision $VERSION);
  1         1  
  1         126  
52 1 50   1   3 my $major = 1; q$Revision: 18 $ =~ /: (\d+)/; my ($minor) = ($1); $VERSION = "$major." . ($minor<10 ? '0' : '') . $minor;
  1         6  
  1         4  
  1         39  
53             }
54            
55             # superclass
56             #use YYYY;
57             #use vars qw(@ISA);
58             #@ISA = qw(YYYY);
59            
60             # used modules
61             # used own modules
62 1     1   7 use PBib::Document;
  1         8  
  1         4838  
63            
64             # module variables
65             #use vars qw(mmmm);
66            
67            
68             =head1 METHODS
69            
70             =over
71            
72             =cut
73            
74             #
75             #
76             # constructor
77             #
78             #
79            
80             sub new {
81             # see access methods below for list of valid args
82             # print STDERR "\n1: ", localtime(), "\n";
83 2     2 0 8 my $self = shift;
84 2         33 my %args = @_;
85             # foreach my $arg qw/inDoc outDoc refStyle labelStyle bibStyle itemStyle/ {
86             # print STDERR "argument $arg missing in call to new $self\n"
87             # unless exists $args{$arg};
88             # }
89            
90 2   33     17 my $class = ref($self) || $self;
91             # hook for documents to choose different converter ...
92             #print "def. class $class\n";
93 2   33     14 $class = $args{'inDoc'}->referenceConverterClass($class) || $class;
94 2 50       11 print STDERR "converter class $class\n" if $args{'verbose'};
95            
96 2         4 $self = \%args;
97 2 50 33     12 print STDERR Dumper {
98             "refStyle" => $args{'refStyle'},
99             "refOptions" => $args{'refOptions'},
100             "bibStyle" => $args{'bibStyle'},
101             "bibOptions" => $args{'bibOptions'},
102             "itemStyle" => $args{'itemStyle'},
103             "itemOptions" => $args{'itemOptions'},
104             "labelStyle" => $args{'labelStyle'},
105             "labelOptions" => $args{'labelOptions'},
106             } if $args{'verbose'} && $args{'verbose'} > 1;
107 2         8 $self = bless $self, $class;
108 2 50       11 $self->refStyle()->setConverter($self) if defined $self->refStyle();
109 2 50       9 $self->labelStyle()->setConverter($self) if defined $self->labelStyle();
110 2 50       11 $self->bibStyle()->setConverter($self) if defined $self->bibStyle();
111 2 50       11 $self->itemStyle()->setConverter($self) if defined $self->itemStyle();
112 2         8 return $self;
113             }
114            
115             sub setArgs {
116 0     0 0 0 my $self = shift;
117 0         0 my %args = @_;
118 0         0 foreach my $arg (keys %args) {
119 0         0 $self->{$arg} = $args{$arg};
120             }
121             }
122            
123             #
124             #
125             # access methods
126             #
127             #
128            
129 1408     1408 0 1561 sub inDoc { my $self = shift; return $self->{'inDoc'}; }
  1408         2806  
130 1444     1444 0 1471 sub outDoc { my $self = shift; return $self->{'outDoc'}; }
  1444         3773  
131            
132             # options for the ref' converter share the ref'style's options.
133 2     2 0 4 sub option { my ($self, $opt) = @_; return $self->refOptions()->{$opt}; }
  2         13  
134            
135 508   50 508 0 509 sub refOptions { my $self = shift; return $self->{'refOptions'} || {}; }
  508         2915  
136 1056   50 1056 0 1124 sub labelOptions { my $self = shift; return $self->{'labelOptions'} || {}; }
  1056         8836  
137 4   50 4 0 7 sub bibOptions { my $self = shift; return $self->{'bibOptions'} || {}; }
  4         39  
138 1220   50 1220 0 1173 sub itemOptions { my $self = shift; return $self->{'itemOptions'} || {}; }
  1220         7473  
139            
140 234     234 0 289 sub refStyle { my $self = shift; return $self->{'refStyle'}; }
  234         768  
141 489     489 0 508 sub labelStyle { my $self = shift; return $self->{'labelStyle'}; }
  489         1007  
142 8     8 0 13 sub bibStyle { my $self = shift; return $self->{'bibStyle'}; }
  8         50  
143 116     116 0 127 sub itemStyle { my $self = shift; return $self->{'itemStyle'}; }
  116         256  
144            
145            
146            
147             sub messages {
148 32     32 0 38 my ($self) = @_;
149 32 100       176 return $self->{'messages'} if $self->{'messages'};
150 2         24 return $self->{'messages'} = [];
151             }
152            
153             sub clearMessages {
154 0     0 0 0 my ($self) = @_;
155 0         0 delete $self->{'messages'}
156             }
157            
158             sub logMessage {
159 6     6 0 8 my $self = shift;
160 6 50       20 print STDERR utf8_to_ascii("@_\n") unless $self->{'quiet'};
161 6         8 push @{$self->messages()}, "@_";
  6         17  
162             }
163            
164             sub traceMessage {
165 24     24 0 49 my $self = shift;
166 24 50       65 print STDERR utf8_to_ascii("@_\n") if $self->{'verbose'};
167 24         37 push @{$self->messages()}, "@_";
  24         50  
168             }
169            
170             sub warn {
171 0     0 0 0 my $self = shift;
172 0         0 $self->logMessage("WARNING: @_");
173             }
174            
175            
176             sub utf8_to_ascii {
177             # on my system (win), STDERR does not support utf8 (per default)
178             # this function maps unicode to plain ascii to avoid warnings
179             # about unprintable wide characters.
180 0 0       0 return join("",
181 0     0 0 0 map { $_ > 255 ? # if wide character...
182             sprintf("&#x%04X;", $_) : # \x{...}
183             chr($_) # else as themselves
184             } unpack("U*", $_[0])); # unpack Unicode characters
185             }
186            
187            
188             #
189             #
190             # scanning methods
191             #
192             #
193            
194             # all information about found references
195 12     12 0 18 sub foundInfo { my ($self) = @_; $self->scan(); return $self->{'foundInfo'}; }
  12         30  
  12         227  
196            
197             # all information about found paragraphs with references
198 1089     1089 0 1157 sub parInfo { my ($self) = @_; $self->scan(); return $self->{'parInfo'}; }
  1089         1504  
  1089         2685  
199            
200             # all indexes of paragraphs containing refs
201 0     0 0 0 sub parIndexes { my ($self) = @_; return [keys(%{$self->parInfo()})]; }
  0         0  
  0         0  
202            
203             # all ID of found references
204 10     10 0 15 sub foundIDs { my ($self) = @_; return [keys(%{$self->foundInfo()})]; }
  10         12  
  10         29  
205            
206             sub knownIDs {
207             #
208             # return known and found ref IDs => the items for the bibliography
209             #
210 8     8 0 21 my ($self) = @_;
211 8         12 my @items;
212 8         28 my $refs = $self->refs();
213            
214             # remove unknown reference IDs
215 8         13 foreach my $ref (@{$self->foundIDs()}) {
  8         21  
216 464 100       981 push @items, $ref
217             if defined($refs->{$ref});
218             }
219 8         71 return \@items;
220             }
221            
222             sub unknownIDs {
223             #
224             # return unknown and found ref IDs => possible errors!
225             #
226 2     2 0 5 my ($self) = @_;
227 2         3 my @items;
228 2         7 my $refs = $self->refs();
229            
230             # remove known reference IDs
231 2         4 foreach my $ref (@{$self->foundIDs()}) {
  2         9  
232 116 100       217 push @items, $ref
233             unless defined($refs->{$ref});
234             }
235 2         19 return \@items;
236             }
237            
238            
239             =item $rc->scan()
240            
241             Scan $rc's inDoc for used references and paragraphs that need to be converted.
242             This does preprocessing for convert().
243            
244             The fields "parInfo" and "foundInfo" are set.
245            
246             =cut
247            
248             sub scan {
249             # look for all paragraphs that might have references
250             # return an array with all found refs
251 1101     1101 1 1191 my ($self) = @_;
252 1101         1527 my $inDoc = $self->inDoc();
253            
254             # have we scanned already?
255 1101 100       2484 return $self->{'foundInfo'} if( defined($self->{'foundInfo'}) );
256            
257 2         4 my (%parInfo, %foundInfo, %fields);
258 0         0 my $par;
259 2         11 my $numPars = $inDoc->paragraphCount();
260 2         23 $self->traceMessage("scanning $numPars paragraphs in ", $inDoc->filename());
261 2         17 $inDoc->processParagraphs(\&scanParagraph, $self, undef,
262             \%parInfo, \%foundInfo, \%fields);
263 2         12 $self->traceMessage("scanning done");
264            
265 2         8 $self->{'parInfo'} = \%parInfo;
266 2         8 $self->{'foundInfo'} = \%foundInfo;
267             #print Dumper \%parInfo, \%foundInfo;
268 2         9 return \%foundInfo;
269             }
270            
271             #
272             # some definitions to make pattern construction easier for me ...
273             #
274             # $B = no bracket char (everything not [ nor ]
275             my $B = "(?:[^\\[\\]])";
276             # $bb = [...] with no embedded []
277             my $bb = "(?:\\[$B*\\])";
278             my $opt = "(?:\\s*:$B*\\|\\s*)";
279             # now define pattern for ref and bib
280             my $fieldPattern = "(?:$B|$bb)+";
281             my $refPattern = "(?:$B*$bb(?:$B|$bb)*)";
282             my $bibPattern = "(?:$opt?\{$B*\})";
283             my $optionPattern = "(?::$B*:)";
284             my $todoPattern = "(?:(?:<$B*>)|(?:\#$B*\#))";
285             my $todoMarker = "(?:#+)|(?:\\?{2,})|(?:[<>]{3,})";
286            
287            
288             =item $rc->scanParagraph($par, $i, $parInfo, $foundInfo, $fields)
289            
290             Scan the inDoc for paragraphs with references and collect information
291             on which references are used, how often they are used, and in which
292             parahraphs they are used.
293            
294             This method is called by scan() via inDoc's processParagraphs().
295            
296             =cut
297            
298             sub scanParagraph {
299             # look for reference ids in the given string
300 1087     1087 1 1614 my ($self, $par, $i, $parInfo, $foundInfo, $fields) = @_;
301 1087         1124 my $ref;
302             # print $par;
303 1087 100       3938 return unless( $par =~ /\[/ );
304            
305 115         1232 while ( $par =~ s/\[($B+)\]/\[\]/ ) {
306 162         371 $ref = $self->unquote($1);
307             # print "-- $ref\n";
308 162 100       581 $foundInfo->{$ref} = 0 unless defined($foundInfo->{$ref});
309 162         224 $foundInfo->{$ref} ++;
310 162 100       644 $parInfo->{$i} = {} unless defined($parInfo->{$i});
311 162         1621 $parInfo->{$i}->{$ref} = 1;
312             }
313             }
314            
315            
316             #
317             #
318             # converting methods
319             #
320             #
321            
322            
323 2994   50 2994 0 3253 sub refs { my $self = shift; return $self->{'refs'} || {}; }
  2994         7940  
324 116   50 116 0 120 sub refPattern { my $self = shift; return $self->{'refPattern'} || ''; }
  116         381  
325 2     2 0 3 sub currentParagraph { my ($self) = @_; return $self->inDoc()->{currentParagraph}; }
  2         5  
326            
327            
328             =item $rc->convert($refs, $inDoc, $outDoc)
329            
330             Convert $inDoc to $outDoc.
331            
332             This is the main method of ReferenceConverter.
333            
334             =cut
335            
336             sub convert {
337             # $refs points to a hash with all refId => bibitem hash (bp-canonical form)
338 2     2 1 6 my ($self, $refs, $inDoc, $outDoc) = @_;
339 2         9 $self->{'refs'} = $refs;
340 2 50       10 $self->{inDoc} = $inDoc if $inDoc;
341 2 50       9 $self->{outDoc} = $outDoc if $outDoc;
342 2         9 $inDoc = $self->{inDoc} = $self->inDoc()->prepareConvert($self);
343 2 50       10 return undef unless $inDoc;
344 2         8 $outDoc = $self->outDoc(); # get outDoc after prepareConvert as it might have been changed ...
345 2         12 $outDoc->close(); # in case it is still open (e.g. in Word)
346            
347 2         10 my $knownIDs = $self->knownIDs();
348            
349             # refPattern is used by the ReferenceStyle for matching.
350 2         18 my $refPattern = '(?:' . join("|",
351             map('(?:'.quotePattern($_).')', @$knownIDs)) . ')';
352             # print "$refPattern\n";
353 2         16 $self->{'refPattern'} = $refPattern;
354            
355 2         4 $self->traceMessage(scalar(keys %{$refs}), " known references\n");
  2         15  
356 2         4 $self->traceMessage(scalar(@{$knownIDs}), " found references\n");
  2         9  
357 2         4 $self->traceMessage("converting ", scalar(keys %{$self->parInfo()}), " paragraphs ...\n");
  2         9  
358             # print Dumper $self->parInfo();
359 2         13 $inDoc->processParagraphs(\&convertParagraph, $self, $outDoc,
360             $self->option("final"));
361 2         12 $self->traceMessage("\n... converting done\n");
362            
363 2         12 $self->{'outDoc'} = $outDoc->finalizeConvert($self);
364 2         10 $outDoc->write();
365             }
366            
367             =item $rc->convertParagraph($par, $i, $final)
368            
369             Replace references in $par. $i is the index of this paragraph (as used in parInfo().
370            
371             This method is called by convert() via inDoc's processParagraphs().
372            
373             =cut
374            
375             sub convertParagraph {
376 1087     1087 1 1388 my ($self, $par, $i, $final) = @_;
377             # convert only paragraphs that contain at least one reference
378 1087 100       1811 if( exists($self->parInfo()->{$i}) ) {
379 111 50       223 print STDERR "o" unless $self->{'quiet'};
380             # my $refPattern = $self->{'refPattern'};
381             # look for all [...] where ... is
382             # - non-zero length
383             # - has no more then one-level of embedded [...]
384             # $par =~ s/\[((?:(?:[^\[\]])|(?:\[[^\[\]]*\]))+)\]/
385             # there must be at least on embedded $bb, maybe more
386             # print substr($par, 0, 150), "\n";
387             #if( $par =~ /\[.*\{.*\}.*\]/ ) { print $par; }
388             # $par =~ s/\[($ref|$bib)\]/
389 111         1021 $par =~ s/\[($fieldPattern)\]/ $self->expandField($1) /ge;
  137         286  
390             }# else { print STDERR "."; }
391 1087 50       1987 if( $final ) {
392 0 0       0 print STDERR "." unless $self->{'quiet'};
393             # check for some todo items that might remain in the document.
394 0 0       0 if( $par =~ /($todoMarker)/ ) {
395 0         0 $self->addToDoItem("ToDo Marker $1 found");
396             }
397             }
398 1087         2389 return $par;
399             }
400            
401            
402             ### ToDo: refactor different kind of fields into different field classes!
403             ### each field class (1) knows its pattern (2) knows how to be replaced
404             sub expandField {
405             # return text for the given reference
406 137     137 0 265 my ($self, $refField) = @_;
407            
408             # print STDERR "found field [$refField]\n";
409             # convert field to standard char set
410 137         234 my $text = $self->unquote($refField);
411            
412             # print STDERR "[$text] ";
413             # is it a bibliography field?
414 137 100       1402 if( $text =~ /^$bibPattern$/ ) {
    50          
    100          
    100          
415 2         8 $text = $self->bibStyle()->text($text);
416             } elsif( $text =~ /^$optionPattern$/ ) {
417             ##### s.th. like: $self->refStyle()->processOption($text);
418 0         0 return ''; # remove options
419             } elsif( $text =~ /^$refPattern$/ ) {
420 116         213 $text = $self->refStyle()->text($text);
421             } elsif( $text =~ /^$todoPattern$/ ) {
422 2         9 return $self->processToDoItem($text);
423             } else {
424             # nothing supported --> better leave unchanged!
425             # print STDERR "=> (unchanged)\n";
426 17         92 return "[$refField]";
427             }
428             # print STDERR "=> ", substr($text, 0, 40), "\n";
429 118         293 return $self->quote($text);
430             }
431            
432 4     4 0 10 sub toDoItems { my $self = shift;
433 4 100       21 $self->{'todo'} = [] unless defined($self->{'todo'});
434 4         10 return $self->{'todo'};
435             }
436 2     2 0 8 sub addToDoItem { my ($self, $text, %keys) = @_;
437 2         12 $self->logMessage("todo: $text\n");
438 2         7 my $todo = $self->toDoItems();
439 2         9 push @$todo, {'text' => $text, 'par' => $self->currentParagraph(), %keys};
440             }
441 2     2 0 4 sub processToDoItem { my ($self, $text) = @_;
442             # remove todo delimiters of the form:
443 2         29 $text =~ s/(?:^[#<]+\s*)|(?:\s*[#>]+$)//g;
444 2         9 $self->addToDoItem($text);
445 2         9 return $self->outDoc()->highlight(
446             $self->outDoc()->quote("<<$text>>"));
447             }
448            
449            
450             sub unquote {
451 299     299 0 528 my ($self, $text) = @_;
452 299         526 $text = $self->inDoc()->unquote($text);
453 299         508 $text =~ s/\c//g;
454 299         546 return $text;
455             }
456             sub quote {
457 118     118 0 136 my ($self, $text) = @_;
458 118         194 $text = $self->outDoc()->quote($text);
459 118         677 return $text;
460             }
461            
462             #
463             #
464             # reference entry access methods
465             #
466             #
467            
468             sub entries {
469             #
470             # return the entries for a given reference ID
471             #
472 2984     2984 0 3500 my ($self, $refID) = @_;
473 2984         4261 my $ref = $self->refs()->{$refID};
474 2984 50       4763 if( $ref ) {
475             #print Dumper $ref if $refID eq "Phidgets-PhysicalWidgets";
476             #print Dumper $ref if $refID eq 'iRoom-PointRight';
477 2984 100       5656 if( exists $ref->{'CrossRef'} ) {
478 584         938 $self->expandCrossRef($ref);
479             }
480             } else {
481 0         0 $self->warn("Can't find CiteKey $refID");
482             }
483 2984         5960 return $ref;
484             }
485 2498     2498 0 3295 sub entry { my ($self, $refID, $entry, $check) = @_;
486 2498         4175 my $e = $self->entries($refID)->{$entry};
487             #
488             # I could include a return-once-only check, i.e.
489             # if called a second time, it will return ().
490             # this could make the writing of some style a lot
491             # easier, I guess ...
492             #
493 2498 100       4060 if( !$e ) {
494 874 50       1348 if( $check ) {
495 0         0 $self->warn("entry '$entry' not defined in $refID");
496 0 0       0 return "{\\b \\i <<$entry missing>>}" if
497             $self->refOptions()->{'debug-undef-entries'};
498             }
499 874         2598 return ();
500             }
501 1624 100       2773 if( $e eq '{}' ) {
502 4         14 return ();
503             }
504 1620 50 33     8585 return defined($e) && $e ne '' ? $e : ();
505             }
506 0     0 0 0 sub entryExists { my ($self, $refID, $entry) = @_;
507 0         0 return exists($self->entries($refID)->{$entry});
508             }
509 476     476 0 596 sub entryNotEmpty { my ($self, $refID, $entry) = @_;
510 476         737 my $e = $self->entries($refID)->{$entry};
511 476   66     2433 return defined($e) && $e ne '';
512             }
513            
514            
515             # when expanding crossref, move data among fields, e.g.
516             # the Title of a book will be the SuperTitle of a incollection
517             my %crossRefFields = qw(
518             Title SuperTitle
519             );
520             # when expanding crossref, adapt the CiteType of the reference
521             # e.g. a part of a proceedings will be inproceedings
522             # or an "article" is a part of a "journal".
523             my %crossRefTypes = qw(
524             book incollection
525             proceedings inproceedings
526             journal article
527             );
528            
529             sub expandCrossRef {
530             #
531             # support for CrossRef entry: get all referenced field values
532             #
533 584     584 0 608 my ($self, $ref) = @_;
534 584 100       1206 if( exists $ref->{'CrossRef__expanded__'} ) { return $ref; }
  574         867  
535 10         45 $self->traceMessage("expand crossref $ref->{'CiteKey'} --> $ref->{'CrossRef'}");
536 10         31 foreach my $xrefID (split(/,/, $ref->{'CrossRef'})) {
537 10         20 my $xref = $self->entries($xrefID);
538             # adapt CiteType
539 10 50 33     33 if( ! defined($ref->{'CiteType'}) && defined($xref->{'CiteType'}) ) {
540             # print STDERR Dumper({'ref', $ref, 'xref', $xref});
541 0   0     0 $ref->{'CiteType'} = $crossRefTypes{$xref->{'CiteType'}}
542             || $xref->{'CiteType'};
543             }
544 10         10 foreach my $xentry (keys %{$xref}) {
  10         52  
545 128   66     340 my $entry = $crossRefFields{$xentry} || $xentry;
546 128 100       254 if( !exists $ref->{$entry} ) {
547             # print STDERR "$xentry->$entry; ";
548 37         105 $ref->{$entry} = $xref->{$xentry};
549             }
550             }
551             }
552             # print STDERR "\n";
553 10         21 $ref->{'CrossRef__expanded__'} = 1;
554 10         18 return $ref;
555             }
556            
557             #
558             #
559             # class methods
560             #
561             #
562            
563             sub quotePattern {
564 112     112 0 134 my $pattern = shift;
565 112         558 $pattern =~ s/([\-\[\]\*\+\{\}\.\\\$\^])/\\$1/g;
566 112         422 return $pattern;
567             }
568            
569             =back
570            
571             =cut
572            
573             #
574             #
575             # extension of package PBib::Document;
576             #
577             #
578            
579             package PBib::Document;
580            
581             sub referenceConverterClass {
582             #
583             # return which class of reference converter to use (undef for default)
584             #
585 2     2 0 4 my ($self, $rcClass) = @_;
586             #print "sub referenceConverterClass\n";
587 2         15 return undef;
588             }
589            
590            
591             1;
592            
593             #
594             # $Log: ReferenceConverter.pm,v $
595             # Revision 1.20 2004/03/29 13:10:40 tandler
596             # setArgs
597             #
598             # Revision 1.19 2003/12/22 21:59:41 tandler
599             # toni's changes: include explaination field in UI
600             #
601             # Revision 1.18 2003/11/20 16:07:57 gotovac
602             # reveals clicked CiteKey
603             #
604             # Revision 1.17 2003/06/12 22:04:38 tandler
605             # support for logMessage() and warn()
606             # support prepareConvert() / finalizeConvert()
607             #
608             # Revision 1.16 2003/05/22 11:53:38 tandler
609             # expand cross ref: also adapt CiteType if the referenced CiteType is used.
610             # - warn, if a referenced CiteKey is not found.
611             #
612             # Revision 1.15 2003/01/21 10:26:00 ptandler
613             # log warnings/messages
614             #
615             # Revision 1.14 2002/11/05 18:29:51 peter
616             # multiple IDs in CrossRef field
617             #
618             # Revision 1.13 2002/11/03 22:13:28 peter
619             # minor
620             #
621             # Revision 1.12 2002/10/01 21:25:48 ptandler
622             # new status query accessor: unknownIDs
623             #
624             # Revision 1.11 2002/09/22 10:59:07 peter
625             # CrossRef support
626             #
627             # Revision 1.10 2002/08/22 10:40:41 peter
628             # - changed debug output
629             #
630             # Revision 1.9 2002/08/08 08:22:08 Diss
631             # minor changes
632             #
633             # Revision 1.8 2002/07/16 17:35:20 Diss
634             # allow documents to specify a different ref-converter + small changes
635             #
636             # Revision 1.7 2002/05/27 10:21:48 Diss
637             # small fixes ...
638             #
639             # Revision 1.6 2002/04/03 10:19:08 Diss
640             # - started support for "final" check, todo-items, comments etc.
641             #
642             # Revision 1.5 2002/03/28 13:23:00 Diss
643             # added pbib-export.pl, with some support for bp
644             #
645             # Revision 1.4 2002/03/27 10:23:15 Diss
646             # small fixes ...
647             #
648             # Revision 1.3 2002/03/27 10:00:51 Diss
649             # new module structure, not yet included in LitRefs/LitUI (R2)
650             #
651             # Revision 1.2 2002/03/22 17:31:01 Diss
652             # small changes
653             #
654             # Revision 1.1 2002/03/18 11:15:50 Diss
655             # major additions: replace [] refs, generate bibliography using [{}], ...
656             #