/[webpac2]/trunk/lib/WebPAC/Input.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/lib/WebPAC/Input.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 761 - (hide annotations)
Wed Oct 25 17:10:08 2006 UTC (17 years, 6 months ago) by dpavlin
File size: 15290 byte(s)
implemented load_row and save_row closures to serialize
input databases (using WebPAC::Store probably).
This will allow lookups to share on-disk storage with
low_mem option of WebPAC::Input, which is now gone
(under pressure of 600000+ record database which we
are now testing on)

1 dpavlin 1 package WebPAC::Input;
2    
3     use warnings;
4     use strict;
5    
6 dpavlin 507 use blib;
7    
8 dpavlin 487 use WebPAC::Common;
9 dpavlin 285 use base qw/WebPAC::Common/;
10 dpavlin 308 use Data::Dumper;
11 dpavlin 624 use Encode qw/from_to/;
12 dpavlin 285
13 dpavlin 1 =head1 NAME
14    
15 dpavlin 286 WebPAC::Input - read different file formats into WebPAC
16 dpavlin 1
17     =head1 VERSION
18    
19 dpavlin 761 Version 0.14
20 dpavlin 1
21     =cut
22    
23 dpavlin 761 our $VERSION = '0.14';
24 dpavlin 1
25     =head1 SYNOPSIS
26    
27 dpavlin 286 This module implements input as database which have fixed and known
28     I<size> while indexing and single unique numeric identifier for database
29     position ranging from 1 to I<size>.
30 dpavlin 1
31 dpavlin 286 Simply, something that is indexed by unmber from 1 .. I<size>.
32    
33     Examples of such databases are CDS/ISIS files, MARC files, lines in
34     text file, and so on.
35    
36     Specific file formats are implemented using low-level interface modules,
37     located in C<WebPAC::Input::*> namespace which export C<open_db>,
38     C<fetch_rec> and optional C<init> functions.
39    
40 dpavlin 1 Perhaps a little code snippet.
41    
42 dpavlin 597 use WebPAC::Input;
43 dpavlin 1
44 dpavlin 597 my $db = WebPAC::Input->new(
45     module => 'WebPAC::Input::ISIS',
46     );
47 dpavlin 1
48 dpavlin 597 $db->open( path => '/path/to/database' );
49 dpavlin 416 print "database size: ",$db->size,"\n";
50     while (my $rec = $db->fetch) {
51     # do something with $rec
52     }
53 dpavlin 1
54 dpavlin 286
55    
56 dpavlin 1 =head1 FUNCTIONS
57    
58 dpavlin 3 =head2 new
59 dpavlin 1
60 dpavlin 3 Create new input database object.
61    
62 dpavlin 9 my $db = new WebPAC::Input(
63 dpavlin 286 module => 'WebPAC::Input::MARC',
64 dpavlin 585 encoding => 'ISO-8859-2',
65 dpavlin 416 recode => 'char pairs',
66 dpavlin 483 no_progress_bar => 1,
67 dpavlin 9 );
68 dpavlin 3
69 dpavlin 597 C<module> is low-level file format module. See L<WebPAC::Input::ISIS> and
70 dpavlin 286 L<WebPAC::Input::MARC>.
71    
72 dpavlin 585 Optional parametar C<encoding> specify application code page (which will be
73 dpavlin 9 used internally). This should probably be your terminal encoding, and by
74     default, it C<ISO-8859-2>.
75    
76 dpavlin 483 C<recode> is optional string constisting of character or words pairs that
77     should be replaced in input stream.
78    
79     C<no_progress_bar> disables progress bar output on C<STDOUT>
80    
81 dpavlin 285 This function will also call low-level C<init> if it exists with same
82     parametars.
83    
84 dpavlin 1 =cut
85    
86 dpavlin 3 sub new {
87 dpavlin 285 my $class = shift;
88     my $self = {@_};
89 dpavlin 3 bless($self, $class);
90    
91 dpavlin 285 my $log = $self->_get_logger;
92    
93 dpavlin 585 $log->logconfess("code_page argument is not suppored any more. change it to encoding") if ($self->{lookup});
94     $log->logconfess("lookup argument is not suppored any more. rewrite call to lookup_ref") if ($self->{lookup});
95 dpavlin 761 $log->logconfess("low_mem argument is not suppored any more. rewrite it to load_row and save_row") if ($self->{low_mem});
96 dpavlin 585
97 dpavlin 286 $log->logconfess("specify low-level file format module") unless ($self->{module});
98 dpavlin 726 my $module_path = $self->{module};
99     $module_path =~ s#::#/#g;
100     $module_path .= '.pm';
101     $log->debug("require low-level module $self->{module} from $module_path");
102 dpavlin 289
103 dpavlin 726 require $module_path;
104 dpavlin 286
105 dpavlin 285 # check if required subclasses are implemented
106 dpavlin 652 foreach my $subclass (qw/open_db fetch_rec init dump_rec/) {
107 dpavlin 726 # FIXME
108 dpavlin 285 }
109    
110 dpavlin 585 $self->{'encoding'} ||= 'ISO-8859-2';
111 dpavlin 9
112 dpavlin 3 $self ? return $self : return undef;
113 dpavlin 1 }
114    
115 dpavlin 285 =head2 open
116    
117     This function will read whole database in memory and produce lookups.
118    
119 dpavlin 761 my $store; # simple in-memory hash
120    
121 dpavlin 286 $input->open(
122 dpavlin 285 path => '/path/to/database/file',
123 dpavlin 624 code_page => 'cp852',
124 dpavlin 286 limit => 500,
125     offset => 6000,
126 dpavlin 506 stats => 1,
127 dpavlin 707 lookup_coderef => sub {
128     my $rec = shift;
129     # store lookups
130 dpavlin 585 },
131 dpavlin 597 modify_records => {
132     900 => { '^a' => { ' : ' => '^b' } },
133     901 => { '*' => { '^b' => ' ; ' } },
134     },
135 dpavlin 636 modify_file => 'conf/modify/mapping.map',
136 dpavlin 761 save_row => sub {
137     my $a = shift;
138     $store->{ $a->{id} } = $a->{row};
139     },
140     load_row => sub {
141     my $a = shift;
142     return defined($store->{ $a->{id} }) &&
143     $store->{ $a->{id} };
144     },
145    
146 dpavlin 285 );
147    
148 dpavlin 624 By default, C<code_page> is assumed to be C<cp852>.
149 dpavlin 285
150 dpavlin 286 C<offset> is optional parametar to position at some offset before reading from database.
151 dpavlin 285
152 dpavlin 286 C<limit> is optional parametar to read just C<limit> records from database
153 dpavlin 285
154 dpavlin 506 C<stats> create optional report about usage of fields and subfields
155    
156 dpavlin 707 C<lookup_coderef> is closure to called to save data into lookups
157 dpavlin 585
158 dpavlin 597 C<modify_records> specify mapping from subfields to delimiters or from
159     delimiters to subfields, as well as oprations on fields (if subfield is
160     defined as C<*>.
161    
162 dpavlin 636 C<modify_file> is alternative for C<modify_records> above which preserves order and offers
163     (hopefully) simplier sintax than YAML or perl (see L</modify_file_regex>). This option
164     overrides C<modify_records> if both exists for same input.
165    
166 dpavlin 761 C<save_row> and C<load_row> are low-level implementation of store engine. Calling convention
167     is documented in example above.
168    
169 dpavlin 286 Returns size of database, regardless of C<offset> and C<limit>
170     parametars, see also C<size>.
171 dpavlin 285
172     =cut
173    
174     sub open {
175     my $self = shift;
176     my $arg = {@_};
177    
178     my $log = $self->_get_logger();
179    
180 dpavlin 585 $log->logconfess("lookup argument is not suppored any more. rewrite call to lookup_coderef") if ($arg->{lookup});
181     $log->logconfess("lookup_coderef must be CODE, not ",ref($arg->{lookup_coderef}))
182     if ($arg->{lookup_coderef} && ref($arg->{lookup_coderef}) ne 'CODE');
183    
184 dpavlin 707 $log->debug( $arg->{lookup_coderef} ? '' : 'not ', "using lookup_coderef");
185    
186 dpavlin 285 $log->logcroak("need path") if (! $arg->{'path'});
187 dpavlin 624 my $code_page = $arg->{'code_page'} || 'cp852';
188 dpavlin 285
189     # store data in object
190 dpavlin 292 $self->{'input_code_page'} = $code_page;
191 dpavlin 286 foreach my $v (qw/path offset limit/) {
192 dpavlin 285 $self->{$v} = $arg->{$v} if ($arg->{$v});
193     }
194    
195 dpavlin 761 if ($arg->{load_row} || $arg->{save_row}) {
196     $log->logconfess("save_row and load_row must be defined in pair and be CODE") unless (
197     ref($arg->{load_row}) eq 'CODE' &&
198     ref($arg->{save_row}) eq 'CODE'
199     );
200     $self->{load_row} = $arg->{load_row};
201     $self->{save_row} = $arg->{save_row};
202     $log->debug("using load_row and save_row instead of in-memory hash");
203     }
204    
205 dpavlin 416 my $filter_ref;
206 dpavlin 597 my $recode_regex;
207     my $recode_map;
208 dpavlin 416
209     if ($self->{recode}) {
210     my @r = split(/\s/, $self->{recode});
211     if ($#r % 2 != 1) {
212     $log->logwarn("recode needs even number of elements (some number of valid pairs)");
213     } else {
214     while (@r) {
215     my $from = shift @r;
216     my $to = shift @r;
217 dpavlin 597 $recode_map->{$from} = $to;
218 dpavlin 416 }
219    
220 dpavlin 597 $recode_regex = join '|' => keys %{ $recode_map };
221 dpavlin 416
222 dpavlin 597 $log->debug("using recode regex: $recode_regex");
223 dpavlin 416 }
224    
225     }
226    
227 dpavlin 636 my $rec_regex;
228     if (my $p = $arg->{modify_file}) {
229     $log->debug("using modify_file $p");
230     $rec_regex = $self->modify_file_regexps( $p );
231     } elsif (my $h = $arg->{modify_records}) {
232     $log->debug("using modify_records ", Dumper( $h ));
233     $rec_regex = $self->modify_record_regexps(%{ $h });
234     }
235     $log->debug("rec_regex: ", Dumper($rec_regex)) if ($rec_regex);
236 dpavlin 597
237 dpavlin 726 my $class = $self->{module} || $log->logconfess("can't get low-level module name!");
238    
239     my $ll_db = $class->new(
240 dpavlin 285 path => $arg->{path},
241 dpavlin 624 # filter => sub {
242     # my ($l,$f_nr) = @_;
243     # return unless defined($l);
244     # from_to($l, $code_page, $self->{'encoding'});
245     # $l =~ s/($recode_regex)/$recode_map->{$1}/g if ($recode_regex && $recode_map);
246     # return $l;
247     # },
248 dpavlin 523 %{ $arg },
249 dpavlin 285 );
250    
251 dpavlin 726 unless (defined($ll_db)) {
252 dpavlin 285 $log->logwarn("can't open database $arg->{path}, skipping...");
253     return;
254     }
255    
256 dpavlin 726 my $size = $ll_db->size;
257    
258 dpavlin 285 unless ($size) {
259     $log->logwarn("no records in database $arg->{path}, skipping...");
260     return;
261     }
262    
263 dpavlin 339 my $from_rec = 1;
264     my $to_rec = $size;
265 dpavlin 285
266 dpavlin 286 if (my $s = $self->{offset}) {
267 dpavlin 513 $log->debug("skipping to MFN $s");
268 dpavlin 339 $from_rec = $s;
269 dpavlin 285 } else {
270 dpavlin 339 $self->{offset} = $from_rec;
271 dpavlin 285 }
272    
273 dpavlin 286 if ($self->{limit}) {
274 dpavlin 301 $log->debug("limiting to ",$self->{limit}," records");
275 dpavlin 339 $to_rec = $from_rec + $self->{limit} - 1;
276     $to_rec = $size if ($to_rec > $size);
277 dpavlin 285 }
278    
279     # store size for later
280 dpavlin 339 $self->{size} = ($to_rec - $from_rec) ? ($to_rec - $from_rec + 1) : 0;
281 dpavlin 285
282 dpavlin 585 $log->info("processing $self->{size}/$size records [$from_rec-$to_rec] convert $code_page -> $self->{encoding}", $self->{stats} ? ' [stats]' : '');
283 dpavlin 285
284     # read database
285 dpavlin 339 for (my $pos = $from_rec; $pos <= $to_rec; $pos++) {
286 dpavlin 285
287 dpavlin 286 $log->debug("position: $pos\n");
288 dpavlin 285
289 dpavlin 726 my $rec = $ll_db->fetch_rec($pos, sub {
290 dpavlin 619 my ($l,$f_nr) = @_;
291 dpavlin 624 # return unless defined($l);
292     # return $l unless ($rec_regex && $f_nr);
293 dpavlin 285
294 dpavlin 625 $log->debug("-=> $f_nr ## $l");
295    
296 dpavlin 624 # codepage conversion and recode_regex
297 dpavlin 626 from_to($l, $code_page, $self->{'encoding'});
298 dpavlin 624 $l =~ s/($recode_regex)/$recode_map->{$1}/g if ($recode_regex && $recode_map);
299    
300 dpavlin 619 # apply regexps
301     if ($rec_regex && defined($rec_regex->{$f_nr})) {
302     $log->logconfess("regexps->{$f_nr} must be ARRAY") if (ref($rec_regex->{$f_nr}) ne 'ARRAY');
303     my $c = 0;
304     foreach my $r (@{ $rec_regex->{$f_nr} }) {
305 dpavlin 626 my $old_l = $l;
306 dpavlin 619 eval '$l =~ ' . $r;
307 dpavlin 626 if ($old_l ne $l) {
308     $log->debug("REGEX on $f_nr eval \$l =~ $r\n## old l: [$old_l]\n## new l: [$l]");
309     }
310 dpavlin 619 $log->error("error applying regex: $r") if ($@);
311     }
312     }
313    
314 dpavlin 625 $log->debug("<=- $f_nr ## $l");
315 dpavlin 619 return $l;
316     });
317    
318 dpavlin 308 $log->debug(sub { Dumper($rec) });
319    
320 dpavlin 285 if (! $rec) {
321 dpavlin 286 $log->warn("record $pos empty? skipping...");
322 dpavlin 285 next;
323     }
324    
325     # store
326 dpavlin 761 if ($self->{save_row}) {
327     $self->{save_row}->({
328     id => $pos,
329     row => $rec,
330     });
331 dpavlin 285 } else {
332 dpavlin 286 $self->{data}->{$pos} = $rec;
333 dpavlin 285 }
334    
335     # create lookup
336 dpavlin 585 $arg->{'lookup_coderef'}->( $rec ) if ($rec && $arg->{'lookup_coderef'});
337 dpavlin 285
338 dpavlin 506 # update counters for statistics
339     if ($self->{stats}) {
340 dpavlin 593
341 dpavlin 619 # fetch clean record with regexpes applied for statistics
342 dpavlin 726 my $rec = $ll_db->fetch_rec($pos);
343 dpavlin 619
344 dpavlin 593 foreach my $fld (keys %{ $rec }) {
345 dpavlin 506 $self->{_stats}->{fld}->{ $fld }++;
346 dpavlin 593
347     $log->logdie("invalid record fild $fld, not ARRAY")
348     unless (ref($rec->{ $fld }) eq 'ARRAY');
349    
350     foreach my $row (@{ $rec->{$fld} }) {
351    
352     if (ref($row) eq 'HASH') {
353    
354     foreach my $sf (keys %{ $row }) {
355 dpavlin 619 next if ($sf eq 'subfields');
356 dpavlin 593 $self->{_stats}->{sf}->{ $fld }->{ $sf }->{count}++;
357     $self->{_stats}->{sf}->{ $fld }->{ $sf }->{repeatable}++
358     if (ref($row->{$sf}) eq 'ARRAY');
359 dpavlin 506 }
360 dpavlin 593
361     } else {
362     $self->{_stats}->{repeatable}->{ $fld }++;
363     }
364 dpavlin 506 }
365 dpavlin 593 }
366 dpavlin 506 }
367    
368 dpavlin 483 $self->progress_bar($pos,$to_rec) unless ($self->{no_progress_bar});
369 dpavlin 285
370     }
371    
372 dpavlin 286 $self->{pos} = -1;
373     $self->{last_pcnt} = 0;
374 dpavlin 285
375     # store max mfn and return it.
376 dpavlin 339 $self->{max_pos} = $to_rec;
377     $log->debug("max_pos: $to_rec");
378 dpavlin 285
379 dpavlin 757 # save for dump
380     $self->{ll_db} = $ll_db;
381    
382 dpavlin 285 return $size;
383     }
384    
385     =head2 fetch
386    
387     Fetch next record from database. It will also displays progress bar.
388    
389     my $rec = $isis->fetch;
390    
391     Record from this function should probably go to C<data_structure> for
392     normalisation.
393    
394     =cut
395    
396     sub fetch {
397     my $self = shift;
398    
399     my $log = $self->_get_logger();
400    
401 dpavlin 286 $log->logconfess("it seems that you didn't load database!") unless ($self->{pos});
402 dpavlin 285
403 dpavlin 286 if ($self->{pos} == -1) {
404     $self->{pos} = $self->{offset};
405 dpavlin 285 } else {
406 dpavlin 286 $self->{pos}++;
407 dpavlin 285 }
408    
409 dpavlin 286 my $mfn = $self->{pos};
410 dpavlin 285
411 dpavlin 286 if ($mfn > $self->{max_pos}) {
412     $self->{pos} = $self->{max_pos};
413 dpavlin 285 $log->debug("at EOF");
414     return;
415     }
416    
417 dpavlin 483 $self->progress_bar($mfn,$self->{max_pos}) unless ($self->{no_progress_bar});
418 dpavlin 285
419     my $rec;
420    
421 dpavlin 761 if ($self->{load_row}) {
422     $rec = $self->{load_row}->({ id => $mfn });
423 dpavlin 285 } else {
424 dpavlin 286 $rec = $self->{data}->{$mfn};
425 dpavlin 285 }
426    
427     $rec ||= 0E0;
428     }
429    
430     =head2 pos
431    
432     Returns current record number (MFN).
433    
434     print $isis->pos;
435    
436     First record in database has position 1.
437    
438     =cut
439    
440     sub pos {
441     my $self = shift;
442 dpavlin 286 return $self->{pos};
443 dpavlin 285 }
444    
445    
446     =head2 size
447    
448     Returns number of records in database
449    
450     print $isis->size;
451    
452     Result from this function can be used to loop through all records
453    
454     foreach my $mfn ( 1 ... $isis->size ) { ... }
455    
456 dpavlin 286 because it takes into account C<offset> and C<limit>.
457 dpavlin 285
458     =cut
459    
460     sub size {
461     my $self = shift;
462 dpavlin 286 return $self->{size};
463 dpavlin 285 }
464    
465     =head2 seek
466    
467     Seek to specified MFN in file.
468    
469     $isis->seek(42);
470    
471     First record in database has position 1.
472    
473     =cut
474    
475     sub seek {
476     my $self = shift;
477     my $pos = shift || return;
478    
479     my $log = $self->_get_logger();
480    
481     if ($pos < 1) {
482     $log->warn("seek before first record");
483     $pos = 1;
484 dpavlin 286 } elsif ($pos > $self->{max_pos}) {
485 dpavlin 285 $log->warn("seek beyond last record");
486 dpavlin 286 $pos = $self->{max_pos};
487 dpavlin 285 }
488    
489 dpavlin 286 return $self->{pos} = (($pos - 1) || -1);
490 dpavlin 285 }
491    
492 dpavlin 506 =head2 stats
493 dpavlin 285
494 dpavlin 506 Dump statistics about field and subfield usage
495    
496 dpavlin 507 print $input->stats;
497 dpavlin 506
498     =cut
499    
500     sub stats {
501     my $self = shift;
502 dpavlin 507
503     my $log = $self->_get_logger();
504    
505     my $s = $self->{_stats};
506     if (! $s) {
507     $log->warn("called stats, but there is no statistics collected");
508     return;
509     }
510    
511     my $max_fld = 0;
512    
513     my $out = join("\n",
514     map {
515     my $f = $_ || die "no field";
516     my $v = $s->{fld}->{$f} || die "no s->{fld}->{$f}";
517     $max_fld = $v if ($v > $max_fld);
518    
519 dpavlin 519 my $o = sprintf("%4s %d ~", $f, $v);
520 dpavlin 507
521     if (defined($s->{sf}->{$f})) {
522     map {
523 dpavlin 593 $o .= sprintf(" %s:%d%s", $_,
524     $s->{sf}->{$f}->{$_}->{count},
525     $s->{sf}->{$f}->{$_}->{repeatable} ? '*' : '',
526     );
527 dpavlin 507 } sort keys %{ $s->{sf}->{$f} };
528     }
529    
530     if (my $v_r = $s->{repeatable}->{$f}) {
531     $o .= " ($v_r)" if ($v_r != $v);
532     }
533    
534     $o;
535 dpavlin 519 } sort { $a cmp $b } keys %{ $s->{fld} }
536 dpavlin 507 );
537    
538     $log->debug( sub { Dumper($s) } );
539    
540     return $out;
541 dpavlin 506 }
542    
543 dpavlin 652 =head2 dump
544    
545     Display humanly readable dump of record
546    
547     =cut
548    
549     sub dump {
550     my $self = shift;
551    
552 dpavlin 757 return $self->{ll_db}->dump_rec( $self->{pos} );
553 dpavlin 652
554     }
555    
556 dpavlin 598 =head2 modify_record_regexps
557 dpavlin 597
558 dpavlin 636 Generate hash with regexpes to be applied using l<filter>.
559 dpavlin 597
560     my $regexpes = $input->modify_record_regexps(
561     900 => { '^a' => { ' : ' => '^b' } },
562     901 => { '*' => { '^b' => ' ; ' } },
563     );
564    
565     =cut
566    
567 dpavlin 636 sub _get_regex {
568     my ($sf,$from,$to) = @_;
569     if ($sf =~ /^\^/) {
570     return
571     's/\Q'. $sf .'\E([^\^]*?)\Q'. $from .'\E([^\^]*?)/'. $sf .'$1'. $to .'$2/';
572     } else {
573     return
574     's/\Q'. $from .'\E/'. $to .'/g';
575     }
576     }
577    
578 dpavlin 597 sub modify_record_regexps {
579     my $self = shift;
580     my $modify_record = {@_};
581    
582     my $regexpes;
583    
584 dpavlin 634 my $log = $self->_get_logger();
585    
586 dpavlin 597 foreach my $f (keys %$modify_record) {
587 dpavlin 634 $log->debug("field: $f");
588    
589 dpavlin 597 foreach my $sf (keys %{ $modify_record->{$f} }) {
590 dpavlin 634 $log->debug("subfield: $sf");
591    
592 dpavlin 597 foreach my $from (keys %{ $modify_record->{$f}->{$sf} }) {
593     my $to = $modify_record->{$f}->{$sf}->{$from};
594     #die "no field?" unless defined($to);
595 dpavlin 634 $log->debug("transform: |$from| -> |$to|");
596 dpavlin 597
597 dpavlin 636 my $regex = _get_regex($sf,$from,$to);
598     push @{ $regexpes->{$f} }, $regex;
599     $log->debug("regex: $regex");
600 dpavlin 597 }
601     }
602     }
603    
604     return $regexpes;
605     }
606    
607 dpavlin 636 =head2 modify_file_regexps
608    
609     Generate hash with regexpes to be applied using l<filter> from
610     pseudo hash/yaml format for regex mappings.
611    
612     It should be obvious:
613    
614     200
615     '^a'
616     ' : ' => '^e'
617     ' = ' => '^d'
618    
619     In field I<200> find C<'^a'> and then C<' : '>, and replace it with C<'^e'>.
620     In field I<200> find C<'^a'> and then C<' = '>, and replace it with C<'^d'>.
621    
622     my $regexpes = $input->modify_file_regexps( 'conf/modify/common.pl' );
623    
624     On undef path it will just return.
625    
626     =cut
627    
628     sub modify_file_regexps {
629     my $self = shift;
630    
631     my $modify_path = shift || return;
632    
633     my $log = $self->_get_logger();
634    
635     my $regexpes;
636    
637 dpavlin 697 CORE::open(my $fh, $modify_path) || $log->logdie("can't open modify file $modify_path: $!");
638 dpavlin 636
639     my ($f,$sf);
640    
641     while(<$fh>) {
642     chomp;
643     next if (/^#/ || /^\s*$/);
644    
645     if (/^\s*(\d+)\s*$/) {
646     $f = $1;
647     $log->debug("field: $f");
648     next;
649     } elsif (/^\s*'([^']*)'\s*$/) {
650     $sf = $1;
651     $log->die("can't define subfiled before field in: $_") unless ($f);
652     $log->debug("subfield: $sf");
653     } elsif (/^\s*'([^']*)'\s*=>\s*'([^']*)'\s*$/) {
654     my ($from,$to) = ($1, $2);
655    
656     $log->debug("transform: |$from| -> |$to|");
657    
658     my $regex = _get_regex($sf,$from,$to);
659     push @{ $regexpes->{$f} }, $regex;
660     $log->debug("regex: $regex");
661     }
662     }
663    
664     return $regexpes;
665     }
666    
667 dpavlin 3 =head1 AUTHOR
668    
669     Dobrica Pavlinusic, C<< <dpavlin@rot13.org> >>
670    
671 dpavlin 1 =head1 COPYRIGHT & LICENSE
672    
673 dpavlin 599 Copyright 2005-2006 Dobrica Pavlinusic, All Rights Reserved.
674 dpavlin 1
675     This program is free software; you can redistribute it and/or modify it
676     under the same terms as Perl itself.
677    
678     =cut
679    
680     1; # End of WebPAC::Input

  ViewVC Help
Powered by ViewVC 1.1.26