File Coverage

blib/lib/Chess/PGN/Filter.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Parms;
2            
3 1     1   17548 use strict;
  1         2  
  1         34  
4 1     1   5 use warnings;
  1         2  
  1         212  
5 1     1   2436 use Chess::PGN::EPD;
  0            
  0            
6             use vars '$AUTOLOAD';
7             use Carp;
8            
9             sub new {
10             my ($class,%arg) = @_;
11             bless {
12             _verbose => defined $arg{verbose} ? $arg{verbose} : 1, #default legacy verbose
13             _filtertype => $arg{filtertype},
14             _source => $arg{source},
15             _fen => $arg{fen} || 'no',
16             _position => $arg{position} || 'yes',
17             _type => $arg{type} || ($arg{font} || 'marroquin'),
18             _border => $arg{single} || 'single',
19             _corner => $arg{square} || 'square',
20             _legend => $arg{legend} || 'no',
21             _size => $arg{size} || '5',
22             _font => $arg{font} || 'Chess Kingdom',
23             _ECO => $arg{ECO} || 'yes',
24             _NIC => $arg{NIC} || 'no',
25             _Opening => $arg{Opening} || 'yes',
26             _substitutions => $arg{substitutions},
27             _exclude => $arg{exclude},
28             _comments => $arg{comments} || 'yes',
29             _nags => $arg{nags} || 'yes',
30             _ravs => $arg{ravs} || 'yes',
31             _sticky => $arg{sticky} || 'yes',
32             _autoround => $arg{autoround} || 'yes',
33             _event => '',
34             _site => '',
35             _date => '',
36             _round => '',
37             }, $class;
38             }
39            
40             sub AUTOLOAD {
41             my ($self,$newval) = @_;
42            
43             $AUTOLOAD =~ /.*::get(_\w+)/ and return $self->{$1};
44             $AUTOLOAD =~ /.*::set(_\w+)/ and do { $self->{$1} = $newval; return };
45             $AUTOLOAD =~ /.*::if(_\w+)/ and return ($self->{$1} eq 'yes');
46             croak "No such method: $AUTOLOAD\n";
47             }
48            
49             sub DESTROY {
50             }
51            
52             package Chess::PGN::Filter;
53            
54             use 5.006;
55             use strict;
56             use warnings;
57             use Chess::PGN::Parse;
58             use Chess::PGN::EPD;
59             use Text::DelimMatch;
60             use Data::Dumper;
61            
62             require Exporter;
63            
64             our @ISA = qw(Exporter);
65            
66             our @EXPORT = qw(
67             &filter
68             );
69             our $VERSION = '0.15';
70            
71            
72             sub filter {
73             my $parameters = new Parms(@_);
74             my $filtered;
75             if ($parameters->get_filtertype() eq 'XML') {
76             $filtered = filterXML(@_);
77             }
78             elsif ($parameters->get_filtertype() eq 'TEXT') {
79             $filtered = filterTEXT(@_);
80             }
81             elsif ($parameters->get_filtertype() eq 'DOM') {
82             $filtered = [ filterDOM(@_) ];
83             }
84             else {
85             die "Unknown filtertype: '$parameters->get_filtertype()' not supported.\n";
86             }
87             if ($parameters->get_verbose()) {
88             print $parameters->get_filtertype() eq 'DOM' ? Dumper(@$filtered) : $filtered;
89             }
90             else {
91             return $filtered;
92             }
93             }
94            
95             sub filterDOM {
96             my $parms = new Parms(@_);
97             my $file = $parms->get_source();
98             my $filetext;
99            
100             {
101             local $/ = undef;
102             open(FILE,$file) or die "Couldn't open file:$file $!\n";
103             $filetext = ;
104             close(FILE);
105             }
106             return getDOM($filetext);
107             }
108            
109             sub filterTEXT {
110             my $parms = new Parms(@_);
111             my $file = $parms->get_source();
112             my @DOM;
113             my $filetext;
114             my $text = '';
115            
116             {
117             local $/ = undef;
118             open(FILE,$file) or die "Couldn't open file:$file $!\n";
119             $filetext = ;
120             close(FILE);
121             }
122             @DOM = getDOM($filetext);
123             foreach (@DOM) {
124             my $termination = ($_->{'Tags'}->{'Result'} =~ /^1-0|0-1|1\/2-1\/2|\*$/ ? $_->{'Tags'}->{'Result'} : '*');
125             my $movetext;
126             my $move = 1;
127            
128             domExTags($parms,$_->{'Tags'}) if $parms->get_exclude();
129             domSticky($parms,$_->{'Tags'}) if $parms->if_sticky();
130             domAutoround($parms,$_->{'Tags'}) if $parms->if_autoround();
131             domTaxonomy($parms,$_->{'Gametext'},$_->{'Tags'}) if doTax($parms);
132             foreach my $key ('Event','Site','Date','Round','White','Black','Result') {
133             if ($_->{'Tags'}->{$key}) {
134             if ($parms->get_substitutions()) {
135             while (my ($one,$another) = each(%{$parms->get_substitutions()})) {
136             if ($_->{'Tags'}->{$key} =~ /$one/) {
137             $_->{'Tags'}->{$key} =~ s/$one/$another/;
138             }
139             }
140             }
141             $text .= "[$key \"$_->{'Tags'}->{$key}\"]\n";
142             delete($_->{'Tags'}->{$key});
143             }
144             }
145             foreach my $key (sort keys %{$_->{'Tags'}}) {
146             $text .= "[$key \"$_->{'Tags'}->{$key}\"]\n";
147             }
148             $text .= "\n";
149             $movetext = domTEXTGametext($parms,$move,$_->{'Gametext'});
150             $text .= join("\n",paragraph($movetext . $termination,78))."\n\n";
151             }
152             return $text;
153             }
154            
155             sub doTax {
156             my $parms = shift;
157            
158             return ($parms->if_ECO() or $parms->if_NIC() or $parms->if_Opening());
159             }
160            
161             sub domExTags {
162             my $parms = shift;
163             my $tag = shift;
164             my $array = $parms->get_exclude();
165            
166             foreach (@$array) {
167             if (exists($tag->{$_})) {
168             delete($tag->{$_});
169             }
170             }
171            
172             }
173            
174             sub domAutoround {
175             my $parms = shift;
176             my $tag = shift;
177            
178             if (exists($tag->{'Round'})) {
179             my $value = $tag->{'Round'};
180            
181             if ($value eq '' or $value eq '?') {
182             my $round = $parms->get_round();
183            
184             $tag->{'Round'} = ++$round;
185             $parms->set_round($round);
186             }
187             else {
188             $parms->set_round($value);
189             }
190             }
191             }
192            
193             sub domSticky {
194             my $parms = shift;
195             my $tag = shift;
196            
197             if (exists($tag->{'Event'})) {
198             my $value = $tag->{'Event'};
199            
200             if ($value eq '' or $value eq '?') {
201             $tag->{'Event'} = $parms->get_event();
202             }
203             else {
204             $parms->set_event($value);
205             }
206             }
207             if (exists($tag->{'Site'})) {
208             my $value = $tag->{'Site'};
209            
210             if ($value eq '' or $value eq '?') {
211             $tag->{'Site'} = $parms->get_site();
212             }
213             else {
214             $parms->set_site($value);
215             }
216             }
217             if (exists($tag->{'Date'})) {
218             my $value = $tag->{'Date'};
219            
220             if ($value eq '' or $value eq '??.??.??') {
221             $tag->{'Date'} = $parms->get_date();
222             }
223             else {
224             $parms->set_date($value);
225             }
226             }
227             }
228            
229             sub domTaxonomy {
230             my $parms = shift;
231             my $gametext = shift;
232             my $tag = shift;
233             my @epd = reverse domEPD($gametext);
234            
235             if (exists($tag->{'ECO'})) {
236             my $tax = $tag->{'ECO'};
237            
238             if ($tax eq '?' or $tax eq '') {
239             $tag->{'ECO'} = epdcode('ECO',\@epd)
240             }
241             }
242             else {
243             $tag->{'ECO'} = epdcode('ECO',\@epd) if $parms->if_ECO();
244             }
245             if (exists($tag->{'NIC'})) {
246             my $tax = $tag->{'NIC'};
247            
248             if ($tax eq '?' or $tax eq '') {
249             $tag->{'NIC'} = epdcode('NIC',\@epd)
250             }
251             }
252             else {
253             $tag->{'NIC'} = epdcode('NIC',\@epd) if $parms->if_NIC();
254             }
255             if (exists($tag->{'Opening'})) {
256             my $tax = $tag->{'Opening'};
257            
258             if ($tax eq '?' or $tax eq '') {
259             $tag->{'Opening'} = epdcode('Opening',\@epd)
260             }
261             }
262             else {
263             $tag->{'Opening'} = epdcode('Opening',\@epd) if $parms->if_Opening();
264             }
265             }
266            
267             sub domTEXTGametext {
268             my $parms = shift;
269             my $move = shift;
270             my $Gametext = shift;
271             my $movetext = '';
272            
273             foreach my $element (@{$Gametext}) {
274             if ($element->{'Movenumber'} % 2) {
275             $movetext .= "$move. ";
276             }
277             else {
278             $move++;
279             }
280             $movetext .= $element->{'Movetext'} . " ";
281             if ($element->{'Comment'}) {
282             $movetext =~ s/\s$//;
283             $movetext .= '(' . $element->{'Comment'} . ')' if $parms->if_comments();
284             }
285             if ($element->{'Nag'}) {
286             my $s = $element->{'Nag'};
287            
288             if ($s) {
289             if ($s < 6) {
290             $movetext =~ s/\s$//;
291             $movetext .= NAG($s);
292             }
293             else {
294             $movetext =~ s/\s$//;
295             $movetext .= "\$$s" if $parms->if_nags();
296             }
297             }
298             }
299             if ($element->{'Rav'}) {
300             my $ravtext = domTEXTGametext($parms,$move,$element->{'Rav'});
301            
302             $ravtext =~ s/\s$//;
303             $movetext =~ s/\s$//;
304             $movetext .= "{$ravtext}";
305             }
306             }
307             return $movetext;
308             }
309            
310             sub domEPD {
311             my $Gametext = shift;
312             my @epd;
313            
314             foreach my $element (@{$Gametext}) {
315             push(@epd,$element->{'Epd'});
316             }
317             return @epd;
318             }
319            
320             sub paragraph {
321             my $s = shift;
322             my $n = shift;
323             my $m = $n;
324            
325             while ($m < length($s)) {
326             while(substr($s,$m,1) ne ' ') {
327             $m--;
328             }
329             if (substr($s,$m - 1,1) eq '.') {
330             $m--;
331             next;
332             }
333             substr($s,$m,1) = '|';
334             $m += $n;
335             }
336             return split(/\|/,$s);
337             }
338            
339             sub deLIMIT {
340             my $t = shift;
341             my $startdelim = shift;
342             my $enddelim = shift;
343             my $escape = shift;
344             my $mc = new Text::DelimMatch($startdelim,$enddelim,$escape);
345             my ($prefix,$match,$remainder) = $mc->match(' ' . $t . ' ');
346            
347             if ($match) {
348             $match =~ s/^$startdelim//;
349             $match =~ s/$enddelim$//;
350             return ($prefix or '') . ($remainder or ''),$match;
351             }
352             else {
353             return $t,'';
354             }
355             }
356            
357             sub parseComments {
358             my $rcomments = shift;
359             my $rravs = shift;
360             my $rnags = shift;
361            
362             foreach (keys %$rcomments) {
363             my $t = $rcomments->{$_};
364             my $NAG;
365             my $RAV;
366             my $COMMENT;
367            
368             ($t,$COMMENT) = deLIMIT($t,'\{','\}');
369             ($t,$RAV) = deLIMIT($t,'\(','\)','{}');
370             ($t,$NAG) = deLIMIT($t,'\$','\D');
371             if ($RAV) {
372             $rravs->{$_} = $RAV;
373             }
374             if ($NAG and $NAG ne '') {
375             $rnags->{$_} = $NAG;
376             }
377             if ($COMMENT) {
378             $rcomments->{$_} = $COMMENT;
379             }
380             else {
381             delete($rcomments->{$_});
382             }
383             }
384             }
385            
386             sub filterXML {
387             my $parms = new Parms(@_);
388             my $file = $parms->get_source();
389             my @DOM;
390             my $filetext;
391             my $text = '';
392            
393             {
394             local $/ = undef;
395             open(FILE,$file) or die "Couldn't open file:$file $!\n";
396             $filetext = ;
397             close(FILE);
398             }
399             @DOM = getDOM($filetext);
400             $file =~ s/.pgn//;
401             $file = uc($file);
402             #-----------------------------------------------------------------------------
403             $text .= <<"HEADER";
404            
405            
406            
407            
408             HEADER
409             #-----------------------------------------------------------------------------
410             $text .= dom2XML($parms,@DOM);
411             $text .= "\n";
412             return $text;
413             }
414            
415             sub dom2XML {
416             my $parms = shift;
417             my @DOM = @_;
418             my $level = 0;
419             my $result;
420             my $text = '';
421            
422             foreach (@DOM) {
423             $text .= "\t\n";
424             $text .= "\t\t\n";
425             foreach my $key ('Event','Site','Date','Round','White','Black','Result') {
426             if ($_->{'Tags'}->{$key}) {
427             if ($key eq 'Result') {
428             $result = $_->{'Tags'}->{$key};
429            
430             if ($result eq '1-0') {
431             $result = 'WHITEWIN';
432             }
433             elsif ($result eq '0-1') {
434             $result = 'BLACKWIN';
435             }
436             elsif ($result eq '1/2-1/2') {
437             $result = 'DRAW';
438             }
439             else {
440             $result = 'UNKNOWN';
441             }
442             $text .= "\t\t\t\n";
443             }
444             elsif ($key eq 'Date') {
445             my @date = split(/\./,$_->{'Tags'}->{$key});
446            
447             $text .= "\t\t\t\n";
448             }
449             else {
450             $text .= "\t\t\t<$key>$_->{'Tags'}->{$key}\n";
451             }
452             delete($_->{'Tags'}->{$key});
453             }
454             }
455             foreach my $key (sort keys %{$_->{'Tags'}}) {
456             $text .= "\t\t\t<$key>$_->{Tags}->{$key}\n";
457             }
458             $text .= "\t\t\n";
459             $text .= dom2XMLGametext($parms,$level,$result,$_->{'Gametext'});
460             $text .= "\t\n";
461             }
462             return $text;
463             }
464            
465             sub dom2XMLGametext {
466             my $parms = shift;
467             my $level = shift;
468             my $result = shift;
469             my $Gametext = shift;
470             my $tabs = "\t" x 2 . "\t" x $level;
471             my $text = '';
472             my $diagram = sub {
473             my $element = shift;
474             my @rows = epdstr(
475             epd => $element,
476             type => $parms->get_type(),
477             border => $parms->get_border(),
478             corner => $parms->get_corner(),
479             legend => $parms->get_legend()
480             );
481             my $text = '';
482            
483             $text .= qq|$tabs\t\n|;
484             foreach my $row (@rows) {
485             $text .= "$tabs\t\t$row\n";
486             }
487             $text .= "$tabs\t\n";
488             return $text;
489             };
490            
491             $text .= qq|$tabs\n|;
492             foreach my $element (@{$Gametext}) {
493             $text .= "$tabs\t$element->{'Movenumber'}\n";
494             $text .= "$tabs\t$element->{'Movetext'}\n";
495             if ($element->{'Rav'}) {
496             $text .= dom2XMLGametext($parms,$level + 1,'UNKNOWN',$element->{'Rav'});
497             }
498             $text .= "$tabs\t$element->{'Comment'}\n" if $element->{'Comment'};
499             if ($element->{'Nag'}) {
500             my $s = $element->{'Nag'};
501             if ($s eq '0' and ($parms->if_position() or $parms->get_position() eq 'nag')) {
502             $text .= &$diagram($element->{'Epd'});
503             }
504             else {
505             $text .= "$tabs\t$element->{'Nag'}\n";
506             }
507             }
508             $text .= "$tabs\t$element->{'Epd'}\n" if $parms->if_fen();
509             }
510             $text .= qq|$tabs\t\n|;
511             $text .= "$tabs\n";
512             if ($level == 0 and ($parms->if_position() or $parms->get_position() eq 'end')) {
513             chop($tabs);
514             $text .= &$diagram(@{$Gametext}[-1]->{'Epd'});
515             }
516             return $text;
517             }
518            
519             sub getDOM {
520             my $s = shift;
521             my $pgn = new Chess::PGN::Parse undef, $s;
522             my $games_ref = $pgn->read_all({save_comments => 'yes'});
523             my @DOM;
524            
525             foreach (@{$games_ref}) {
526             my %comments;
527             my %ravs;
528             my %nags;
529             my @moves;
530             my @epd;
531             my %tags;
532             my %game;
533             my @movelist;
534             my @movesmade;
535             my $position = 0;
536             my $movenumber = 1;
537            
538             push(@DOM,\%game);
539             foreach my $key (keys %{$_}) {
540             my $ref = $_->{$key};
541             if (ref($ref)) {
542             if ($key eq 'GameMoves') {
543             @moves = @{$ref};
544             @epd = epdlist(@moves);
545             }
546             elsif ($key eq 'GameComments') {
547             %comments = %{$ref};
548             parseComments(\%comments,\%ravs,\%nags);
549             }
550             }
551             elsif ($ref) {
552             if ($key ne 'Game') {
553             $tags{$key} = $ref;
554             }
555             }
556             }
557             $game{'Tags'} = \%tags;
558             $game{'Gametext'} = \@movelist;
559             foreach (@moves) {
560             my %move;
561             my $ckey;
562            
563             $move{'Movetext'} = $_;
564             $move{'Movenumber'} = $position;
565             push(@movesmade,$_);
566             if ($position % 2) {
567             $ckey = "${movenumber}b";
568             $movenumber++;
569             }
570             else {
571             $ckey = "${movenumber}w";
572             }
573             $move{'Comment'} = $comments{$ckey} if (%comments and exists($comments{$ckey}));
574             $move{'Nag'} = $nags{$ckey} if (%nags and exists($nags{$ckey}));
575             if (%ravs and exists($ravs{$ckey})) {
576             my $n = scalar(@movesmade) - 2;
577             my @ravDOM = getDOM("[Result \"*\"]\n\n" . join(' ',@movesmade[0..$n++]) . " $ravs{$ckey}");
578            
579             delete($ravDOM[0]->{'Tags'});
580             splice(@{$ravDOM[0]->{'Gametext'}},0,$n);
581             $move{'Rav'} = $ravDOM[0]->{'Gametext'};
582             }
583             $move{'Epd'} = $epd[$position++];
584             $move{'Movenumber'} = $position;
585             push(@movelist,\%move);
586             }
587             }
588             return @DOM;
589             }
590            
591             1;
592             __END__