File Coverage

blib/lib/Chorus/Frame.pm
Criterion Covered Total %
statement 82 288 28.4
branch 9 152 5.9
condition 0 30 0.0
subroutine 22 55 40.0
pod 6 30 20.0
total 119 555 21.4


line stmt bran cond sub pod time code
1             package Chorus::Frame;
2              
3 1     1   18 use 5.006;
  1         3  
  1         54  
4 1     1   5 use strict;
  1         2  
  1         68  
5              
6             our $VERSION = '1.02';
7              
8             =head1 NAME
9              
10             Chorus::Frame - A short implementation of frames from knowledge representation.
11              
12             =head1 VERSION
13              
14             Version 1.02
15              
16             =cut
17              
18             =head1 SYNOPSIS
19              
20             use Chorus::Frame;
21            
22             my $f1 = Chorus::Frame->new(
23             b => {
24             _DEFAULT => 'inherited default for b'
25             }
26             );
27              
28             my $f2 = Chorus::Frame->new(
29             a => {
30             b1 => sub { $SELF->get('a b2') }, # procedural attachment using context $SELF
31             b2 => {
32             _ISA => $f1->{b},
33             _NEEDED => 'needed for b # needs mode Z to precede inherited _DEFAULT
34             }
35             }
36             );
37            
38             Chorus::Frame::setMode(GET => 'N');
39             print $f2->get('a b1') . "\n"; # print 'inherited default for b'
40              
41             Chorus::Frame::setMode(GET => 'Z');
42             print $f2->get('a b1') . "\n"; # print 'needed for b'
43            
44             =cut
45              
46             =head1 DESCRIPTION
47              
48             - A frame is a generic object structure described by slots (properties).
49             - A frame can inherit slots from other frames.
50             - A frame can have specific slots describing :
51            
52             * how it can be associated to a target information,
53             * how he reacts when its target information changes
54             * what it can try when a missing property is requested.
55            
56             - The slots _VALUE,_DEFAULT,_NEEDED are tested in this order to obtain the target information
57             of a given frame (can be inherited).
58             - Two other special slots _BEFORE & _AFTER can define what a frame has to do before or after
59             one of its properties changes.
60             - The slot _ISA is used to define the inheritance.
61              
62             Two modes 'N' (default) or 'Z' are used to define the priority between a frame and its inherited
63             frames in order to process its target information
64            
65             The globale variable $SELF returns the current CONTEXT which is the most recent frame called for the method get().
66             A slot defined by a function sub { .. } can refer to the current context $SELF in its body.
67            
68             All frames are automaticaly referenced in a repository used to optimise the selection of frames for a given action.
69             The function fmatch() can be used to quicly select all the frames responding to a given test on their properties.
70             =cut
71              
72             BEGIN {
73 1     1   5 use Exporter;
  1         2  
  1         55  
74 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         129  
75              
76 1     1   18 @ISA = qw(Exporter);
77 1         3 @EXPORT = qw($SELF &fmatch);
78 1         25 @EXPORT_OK = qw();
79              
80             # %EXPORT_TAGS = ( ); # eg: TAG => [ qw!name1 name2! ];
81             }
82              
83 1     1   6 use strict;
  1         2  
  1         37  
84 1     1   6 use Carp; # warn of errors (from perspective of caller)
  1         2  
  1         82  
85 1     1   7 use Digest::MD5;
  1         2  
  1         45  
86 1     1   6 use Scalar::Util qw(weaken);
  1         8  
  1         89  
87              
88 1     1   481938 use Data::Dumper;
  1         17531  
  1         108  
89              
90 1     1   11 use constant DEBUG_MEMORY => 0;
  1         2  
  1         67  
91              
92 1     1   5 use vars qw($AUTOLOAD);
  1         2  
  1         47  
93              
94 1     1   5 use constant SUCCESS => 1;
  1         2  
  1         43  
95 1     1   5 use constant FAILED => 0;
  1         2  
  1         43  
96              
97 1     1   5 use constant VALUATION_ORDER => ('_VALUE', '_DEFAULT', '_NEEDED');
  1         2  
  1         57  
98              
99 1     1   4 use constant MODE_N => 1;
  1         1  
  1         35  
100 1     1   11 use constant MODE_Z => 2;
  1         2  
  1         6579  
101              
102             my $getMode = MODE_N;
103              
104             my %REPOSITORY;
105             my %FMAP;
106             my %INSTANCES;
107              
108             our $SELF;
109             my @Heap = ();
110              
111             sub AUTOLOAD {
112 0   0 0   0 my $frame = shift || $SELF;
113 0         0 my $slotName = $AUTOLOAD;
114 0         0 $slotName =~ s/.*://; # strip fully-qualified portion
115 0         0 get($frame, $slotName, @_); # or getN or getZ !!
116             }
117              
118             sub _isa {
119 26     26   36 my ($ref, $str) = @_;
120 26         90 return (ref($ref) eq $str);
121             }
122              
123             =head1 SUBROUTINES
124             =cut
125              
126             =head2 setMode
127              
128             Defines the inheritance mode of methods get() for the special slots _VALUE,_DEFAULT,_NEEDED
129             the default mode is 'N'.
130            
131             'N' : ex. a single slot from the sequence _VALUE,_DEFAULT,_NEEDED will be tested in all inherited
132             frames before trying the next one.
133            
134             'Z' : the whole sequence _VALUE,_DEFAULT,_NEEDED will be tested from the frame before being
135             tested from the inherited frames
136            
137             ex. Chorus::Frame::setMode(GET => 'Z');
138              
139             =cut
140              
141             sub setMode {
142 0     0 1 0 my (%opt) = @_;
143 0 0 0     0 $getMode = MODE_N if defined($opt{GET}) and uc($opt{GET}) eq 'N';
144 0 0 0     0 $getMode = MODE_Z if defined($opt{GET}) and uc($opt{GET}) eq 'Z';
145             }
146              
147             =head1 METHODS
148             =cut
149              
150             =head2 _keys
151              
152             my @k = $f->_keys;
153             same as CORE::keys but excludes the special slot '_KEY' specific to all frames
154             =cut
155              
156             sub _keys {
157 0     0   0 my ($this) = @_;
158 0         0 grep { $_ ne '_KEY' } keys %{$this};
  0         0  
  0         0  
159             }
160              
161             sub pushself {
162 0 0   0 0 0 unshift(@Heap, $SELF) if $SELF;
163 0         0 $SELF = shift;
164             }
165              
166             sub popself {
167 0     0 0 0 $SELF = shift @Heap;
168             }
169              
170             sub expand {
171            
172 0     0 0 0 my ($info, @args) = @_;
173 0 0       0 return expand(&$info(@args)) if _isa($info, 'CODE');
174 0         0 return $info;
175             }
176              
177             =head2 _push
178              
179             push new elements to a given slot (becomes an array if necessary)
180             =cut
181              
182             sub _push {
183 0     0   0 my ($this, $slot, @elems) = @_;
184 0 0       0 return $this->{$slot} = [ @elems ] unless exists $this->{$slot};
185 0 0       0 $this->{$slot} = ref($this->{$slot}) eq 'ARRAY' ? [ @{$this->{$slot}}, @elems ] : [ $this->{$slot}, @elems ];
  0         0  
186             }
187              
188             sub _addInstance {
189 0     0   0 my ($this, $instance) = @_;
190 0         0 my $k = $instance->{_KEY};
191 0         0 $INSTANCES{$this->{_KEY}}->{$k} = $instance;
192 0         0 weaken($INSTANCES{$this->{_KEY}}->{$k}) ; # not counted in garbage collector !
193             }
194              
195             =head2 _inherits
196              
197             add inherited new frame(s) outside constructor
198             ex. $f->_inherits($F1,$F2);
199             =cut
200            
201             sub _inherits {
202 0     0   0 my ($this, @inherited) = @_;
203 0         0 $_->_addInstance($this) for @inherited;
204 0         0 $this->_push('_ISA', @inherited); # shoult test if already inherited !
205             }
206              
207             sub _removeInstance {
208 0     0   0 my ($this, $instance) = @_;
209 0         0 my $k = $instance->{_KEY};
210 0 0       0 (warn "Instance NOT FOUND !?", return) unless $INSTANCES{$this->{_KEY}}->{$k};
211 0         0 delete $INSTANCES{$this->{_KEY}}->{$k};
212             }
213              
214             sub blessToFrame {
215              
216             sub register {
217              
218 2     2 0 5 my ($this) = @_;
219              
220 2         4 my $k;
221 2         3 do {
222 2         91 $k = Digest::MD5::md5_base64( rand );
223             } while(exists($FMAP{$k}));
224            
225 2         17 foreach my $slot (keys(%$this)) { # register all slots
226 8 50       23 $REPOSITORY{$slot} = {} unless exists $REPOSITORY{$slot};
227 8         19 $REPOSITORY{$slot}->{$k} = 'Y';
228             }
229            
230 2         7 $this->{_KEY} = $k;
231 2         6 $FMAP{$k} = $this;
232 2         8 weaken($FMAP{$k}) ; # not counted in garbage collector !
233 2         4 return $this;
234             }
235              
236             sub blessToFrameRec {
237              
238 2     2 0 4 local $_ = shift;
239              
240 2 50       7 if (_isa($_,'Chorus::Frame')) {
241            
242 2         11 while(my ($k, $val) = each %$_) {
243 10 50       18 if (_isa($val,'HASH')) {
244 0 0       0 next if $val->{_NOFRAME};
245 0         0 bless($val, 'Chorus::Frame');
246 0         0 $val->register();
247 0         0 blessToFrameRec($val);
248             } else {
249 10 50       20 if (_isa($val,'ARRAY')) {
250 0         0 blessToFrameRec($_->{$k});
251             }
252             }
253 10 50       52 if ($k eq '_ISA') {
254 0 0       0 foreach my $inherited (_isa($val,'ARRAY') ? map \&expand, @{$val}
  0         0  
255             : (expand($val))) {
256 0 0       0 $inherited->_addInstance($_) if $inherited;
257             }
258             }
259             }
260              
261 2         6 return;
262             }
263              
264 0 0       0 if (_isa($_,'ARRAY')) { # à revoir (sans $idx)
265 0         0 foreach my $idx (0 .. scalar(@$_) - 1) {
266 0 0       0 if (_isa($_[$idx], 'HASH')) {
267 0 0       0 next if exists $_[$idx]->{_NOFRAME};
268 0         0 bless($_[$idx], 'Chorus::Frame');
269 0         0 $_[$idx]->register();
270 0         0 blessToFrameRec($_[$idx]);
271             } else {
272 0 0       0 if (_isa($_[$idx],'ARRAY')) {
273 0         0 blessToFrameRec($_[$idx]);
274             }
275             }
276             }
277             }
278             }
279              
280 2     2 0 5 my $res = shift;
281              
282 2 50       8 return $res if _isa($res, 'Chorus::Frame'); # already blessed
283              
284             SWITCH: {
285              
286 2 50       6 _isa($res, 'HASH') && do {
  2         8  
287 2 50       8 return $res if exists $res->{_NOFRAME};
288 2         10 bless($res, 'Chorus::Frame')->register();
289 2 50       14 blessToFrameRec $res if keys(%$res);
290 2         5 last SWITCH;;
291             };
292              
293 0 0       0 _isa($res, 'ARRAY') && do {
294 0 0       0 return $res unless scalar(@$res);
295 0         0 blessToFrameRec $res;
296 0         0 last SWITCH;
297             };
298              
299             }; # SWITCH
300              
301 2         8 return $res;
302             }
303              
304             =head2 new
305              
306             Constructor : Converts a hashtable definition into a Chorus::Frame object.
307            
308             Important - All nested hashtables are recursively converted to Chorus::Frame,
309             except those providing a slot _NO_FRAME
310            
311             All frames are associated to a unique key and registered in an internal repository (see fmatch)
312            
313             Ex. $f = Chorus::Frame->new(
314             slotA1 => {
315             _ISA => [ $f2->slotA, $f3->slotA ] # multiple inheritance
316             slotA2 => sub { $SELF }; # procedural attachements
317             slotA3 => 'value for A3'
318             },
319             slotB => {
320             _NEEDED => sub { .. }
321             }
322             );
323             =cut
324              
325             sub new {
326 2     2 1 7 my ($this, @desc) = @_;
327 2         15 return blessToFrame({@desc});
328             }
329              
330             sub DESTROY {
331 0     0     my ($this) = @_;
332              
333 0 0         my $k = $this->{_KEY} or warn "Undefined _KEY(1) for " . Dumper($this);
334              
335 0 0         delete $INSTANCES{$k} if exists $INSTANCES{$k};
336              
337 0 0         foreach my $inherited (_isa($this->{_ISA}, 'ARRAY') ? map \&expand, @{$this->{_ISA}} : (expand($this->{_ISA}))) {
  0            
338 0 0         my $ik = $inherited->{_KEY} or next;
339 0 0         delete $INSTANCES{$ik}->{$k} if exists $INSTANCES{$ik}->{$k};
340             }
341              
342 0           foreach my $slot (keys(%$this)) {
343 0 0 0       delete($REPOSITORY{$slot}->{$k}) if exists $REPOSITORY{$slot} and exists $REPOSITORY{$slot}->{$k};
344             }
345              
346 0           delete $FMAP{$k}; # is a weak reference (not counted by garbage collector)
347             }
348              
349             =head2 get
350              
351             This method provides the information associated to a sequence of slots.
352             This sequence is given in a string composed with slot names separated by spaces.
353             The last slot is tested for the target information with the sequence _VALUE,_DEFAULT,_NEEDED.
354             If a frame doesn't provide any of those slots, the target information is the frame itself.
355              
356             A frame called with the method get() becomes the current context wich can be referred with the variable $SELF.
357            
358             Note - The short form $f->SLOTNAME() can by used instead of $f->get('SLOTNAME');
359            
360             Ex. $f->foo; # equiv to $f->get('foo');
361             $f->foo(@args); # equiv to $f->get('foo')(@args);
362              
363             $f->get('foo bar'); # $SELF (context) is $f while processing 'bar'
364              
365             $f->get('foo')->get('bar') # $SELF (context) is $f->foo while processing 'bar'
366             $f->foo->bar; # short form
367            
368             =cut
369              
370             sub get {
371            
372             sub expandInherits {
373              
374             sub first { # uses expand
375 0     0 0   my ($this, $slots, @args) = @_;
376 0           for (@{$slots}) {
  0            
377 0 0         return { ret => SUCCESS, res => expand($this->{$_}, @args) } if exists $this->{$_};
378             }
379 0           return undef;
380             }
381              
382 0     0 0   my ($this,$tryValuations,@args) = @_;
383              
384 0           my $res = $this->first($tryValuations,@args);
385 0 0 0       return $res if defined($res) and $res->{ret};
386            
387 0 0         if (exists($this->{_ISA})) {
388 0 0         my @h = _isa($this->{_ISA}, 'ARRAY') ? map \&expand, @{$this->{_ISA}} : (expand($this->{_ISA}));
  0            
389 0           for (@h) { # upper level
390 0           $res = $_->expandInherits($tryValuations,@args);
391 0 0 0       return $res if defined($res) and $res->{ret};
392             }
393             }
394 0           return { ret => FAILED };
395             } # expandInherits
396            
397             sub inherited {
398 0     0 0   my ($this,$slot,@rest) = @_;
399              
400 0 0         return $this->{$slot} if exists($this->{$slot}); # first that match (better than buildtree) !!
401 0 0         $this->{_ISA} and push @rest, _isa($this->{_ISA}, 'ARRAY') ? @{$this->{_ISA}} : $this->{_ISA}; # see expand
  0 0          
402              
403 0           my $next = shift @rest;
404 0 0         return undef unless $next;
405 0           return $next->inherited($slot,@rest);
406             }
407            
408             sub getZ {
409            
410             sub value_Z {
411 0     0 0   my ($info, @args) = @_;
412 0 0         return expand($info,@args) unless _isa($info,'Chorus::Frame');
413 0           my $res = $info->expandInherits([VALUATION_ORDER], @args);
414 0 0 0       return $res->{res} if defined($res) and $res->{ret};
415 0           return $info;
416             }
417            
418 0     0 0   my ($this, $way, @args) = @_;
419              
420 0 0         return $this->value_Z(@args) unless $way;
421              
422 0 0         $way =~ /^\s*(\S*)\s*(.*?)\s*$/o or die "Unexpected way format : '$way'";
423 0           my ($nextStep, $followWay) = ($1,$2);
424              
425 0 0         return value_Z($this->inherited($nextStep), @args) unless $followWay;
426              
427 0           my $next = $this->inherited($nextStep);
428 0 0         return _isa($next,'Chorus::Frame') ? $next->getZ($followWay, @args) : undef;
429             }
430              
431             sub getN {
432            
433             sub value_N {
434 0     0 0   my ($info, @args) = @_;
435 0 0         return expand($info,@args) unless _isa($info,'Chorus::Frame');
436 0           for (VALUATION_ORDER) {
437 0           my $res = $info->expandInherits([$_], @args);
438 0 0 0       return $res->{res} if defined($res) and $res->{ret};
439             }
440 0           return $info;
441             }
442            
443 0     0 0   my ($this, $way, @args) = @_;
444              
445 0 0         return $this->value_N(@args) unless $way;
446              
447 0 0         $way =~ /^\s*(\S*)\s*(.*?)\s*$/o or die "Unexpected way format : '$way'";
448 0           my ($nextStep, $followWay) = ($1,$2);
449              
450 0 0         return value_N($this->inherited($nextStep), @args) unless $followWay;
451              
452 0           my $next = $this->inherited($nextStep);
453 0 0         return _isa($next,'Chorus::Frame') ? $next->getN($followWay, @args) : undef;
454             }
455              
456 0     0 1   pushself(shift);
457 0 0         my $res = $getMode == MODE_N ? getN($SELF,@_) : getZ($SELF,@_);
458 0           popself();
459 0           return $res;
460             }
461              
462             =head2 delete
463              
464             All Frames properties are registered in a single table, especially to optimize the method fmatch().
465             This why frames have to use the form $f->delete($slotname) instead of delete($f->{$slotname})
466             otherwise a frame will be considered by fmatch() as providing a slot even after this one have been removed.
467              
468             =cut
469            
470             sub delete {
471            
472             sub deleteSlot {
473              
474             sub unregisterSlot {
475 0     0 0   my ($this,$slot) = @_;
476 0 0         return unless exists $REPOSITORY{$slot};
477 0 0         delete $REPOSITORY{$slot}->{$this->{_KEY}} if exists $REPOSITORY{$slot}->{$this->{_KEY}};
478             }
479              
480 0     0 0   my ($this,$slot) = @_;
481              
482 0           $this->unregisterSlot($slot);
483 0 0         delete($this->{$slot}) if exists $this->{$slot};
484             }
485            
486             sub deleteN {
487              
488 0     0 0   my ($this, $way) = @_;
489              
490 0 0         return undef unless $way;
491              
492 0 0         $way =~ /^\s*(\S*)\s*(.*?)\s*$/o or die "Unexpected way format : '$way'";
493 0           my ($nextStep, $followWay) = ($1,$2);
494              
495 0 0         return $this->deleteSlot($nextStep) unless $followWay;
496              
497 0           my $next = $this->inherited($nextStep);
498 0 0         return _isa($next,'Chorus::Frame') ? $next->deleteN($followWay) : undef;
499             }
500            
501 0     0 1   pushself(shift);
502 0           my $res = $SELF->deleteN(@_);
503 0           popself();
504 0           return $res;
505             }
506              
507             =head2 set
508              
509             This method tells a frame to associated target information to a sequence of slots
510             A frame called for this method becomes the new context.
511              
512             Ex. $f1 = Chorus::Frame->new(
513             a => {
514             b => {
515             c => 'C'
516             }
517             }
518             );
519            
520             $f1->set('a b', 'B'); # 'B' becomes the target _VALUE for $f1->get('a b')
521             $f1->get('a b'); # returns 'B'
522              
523             $f1->get('a b c'); # still returns 'C'
524             $f1->delete('a b c');
525             $f1->get('a b c'); # undef
526              
527             $f2 = Chorus::Frame->new(
528             _ISA => $1,
529             );
530              
531             $f2->get('a b c'); # returns 'C'
532            
533             $f2->set('a b', 'AB'); # cancel inheritance for first slot 'a'
534             $f2->get('a b'); # returns 'AB'
535              
536             $f2->get('a b c'); # undefined
537            
538             =cut
539              
540             sub set {
541              
542             sub registerSlot {
543 0     0 0   my ($this,$slot) = @_;
544 0 0         $REPOSITORY{$slot} = {} unless exists $REPOSITORY{$slot};
545 0           $REPOSITORY{$slot}->{$this->{_KEY}} = 'Y';
546             }
547            
548             sub setValue {
549 0     0 0   my ($this, $val) = @_;
550              
551 0           $this->getN('_BEFORE', $val); # or return undef;
552              
553 0           blessToFrame($val);
554 0           $this->{'_VALUE'} = $val;
555 0           $this->registerSlot('_VALUE');
556              
557 0           $this->getN('_AFTER', $val); # or return undef;
558              
559 0           return $val;
560             }
561              
562             sub setSlot {
563 0     0 0   my ($this, $slot, $info) = @_;
564 0           blessToFrame($info);
565 0           $this->{$slot} = $info;
566 0           $this->registerSlot($slot);
567 0           return $info;
568             }
569            
570             sub setN {
571 0     0 0   my ($this, $way, $info) = @_;
572              
573 0 0         return $this->setValue($info) unless $way;
574              
575 0 0         $way =~ /^\s*(\S*)\s*(.*?)\s*$/o or die "Unexpected way format : '$way'";
576 0           my ($nextStep, $followWay) = ($1,$2);
577 0           my $crossedValue = $this->{$nextStep};
578              
579 0 0         return $crossedValue->setN($followWay, $info) if _isa($crossedValue,'Chorus::Frame');
580            
581 0 0         unless ($followWay) {
582 0 0         if ($nextStep eq '_VALUE') {
583 0           return $this->setValue($info);
584             } else {
585 0 0 0       if (_isa($this->{$nextStep}, 'Chorus::Frame') and exists($this->{$nextStep}->{_VALUE})) {
586 0           return $this->{$nextStep}->setValue($info)
587             } else {
588 0           return $this->setSlot($nextStep, $info);
589             }
590             }
591             }
592              
593 0 0         $this->{$nextStep} = (exists($this->{$nextStep})) ? new Chorus::Frame (_VALUE => $crossedValue)
594             : new Chorus::Frame;
595            
596 0           return $this->{$nextStep}->setN($followWay, $info); # (keep current context)
597            
598             } # setN
599            
600 0     0 1   pushself(shift);
601 0           my %desc = @_;
602              
603 0           my $res;
604            
605 0           while(my($k,$val) = each %desc) {
606 0           $res = $SELF->setN($k, $val);
607             }
608            
609 0           popself();
610 0           return $res; # wil return last set if multiple pairs (key=>val) !!
611             }
612              
613             =head2 fmatch
614              
615             This function returns the list of the frames providing all the slots given as argument.
616             The result can contains the frames providing these the slots by inheritance.
617             This function can be used to minimise the list of frames that should be candidate for a given process.
618            
619             An optional argument 'from' can provide a list of frames as search space
620            
621             ex. @l = grep { $_->score > 5 } fmatch(
622             slot => ['foo', 'score'],
623             from => \@framelist # optional : limit search scope
624             );
625             #
626             # all frames, optionnaly from @framelist, providing both slots 'foo' and 'score' (possible
627             # inheritance) and on which the method get('score') returns a value > 5
628              
629             =cut
630              
631             sub firstInheriting {
632 0     0 0   my ($this) = @_;
633 0           my $k = $this->{_KEY};
634 0 0         return () unless $INSTANCES{$k};
635 0           return(values(%{$INSTANCES{$k}}));
  0            
636            
637             } # firstInheriting
638              
639             sub fmatch {
640            
641             sub framesProvidingSlot { # inheritance ok
642            
643             sub hasSlot {
644 0     0 0   my ($slot) = @_;
645 0 0         return map { $FMAP{$_} || () } keys(%{$REPOSITORY{$slot}})
  0            
  0            
646             }
647              
648             sub wholeTree {
649            
650 0     0 0   my ($res, @dig) = @_;
651 0 0         return $res unless $dig[0];
652 0           my @inheriting = map { $_->firstInheriting } @dig;
  0            
653 0           push(@$res, @inheriting);
654 0           return wholeTree($res,@inheriting);
655            
656             } # wholeTree
657            
658 0     0 0   my ($slot) = @_;
659            
660 0           my @res = hasSlot($slot);
661 0           my @inheriting = map { $_->firstInheriting } @res;
  0            
662            
663 0           push @res, @inheriting;
664 0           return wholeTree(\@res, @inheriting);
665            
666             } # framesProvidingSlot
667              
668 0     0 1   my %opts = @_;
669 0 0 0       $opts{slot} = [ $opts{slot} || () ] unless _isa($opts{slot},'ARRAY');
670 0 0         my ($firstslot,@otherslots) = @{$opts{slot} || []};
  0            
671              
672 0 0         return () unless $firstslot;
673            
674 0           my %filter = map { $_->{_KEY} => 'Y' } @{framesProvidingSlot($firstslot)};
  0            
  0            
675            
676 0           for(@otherslots) {
677 0 0         %filter = map { $filter{$_->{_KEY}} ? ($_->{_KEY} => 'Y') : () } @{framesProvidingSlot($_)};
  0            
  0            
678             }
679            
680 0 0         if ($opts{from}) {
681 0           return grep { $filter{$_->{_KEY}} } @{$opts{from}};
  0            
  0            
682             }
683            
684 0           return map { $FMAP{$_} } keys(%filter);
  0            
685            
686             } # fmatch
687              
688             =head1 AUTHOR
689              
690             Christophe Ivorra, C<< >>
691              
692             =head1 BUGS
693              
694             Please report any bugs or feature requests to C, or through
695             the web interface at L. I will be notified, and then you'll
696             automatically be notified of progress on your bug as I make changes.
697              
698             =head1 SUPPORT
699              
700             You can find documentation for this module with the perldoc command.
701              
702             perldoc Chorus::Frame
703              
704              
705             You can also look for information at:
706              
707             =over 4
708              
709             =item * RT: CPAN's request tracker (report bugs here)
710              
711             L
712              
713             =item * AnnoCPAN: Annotated CPAN documentation
714              
715             L
716              
717             =item * CPAN Ratings
718              
719             L
720              
721             =item * Search CPAN
722              
723             L
724              
725             =back
726              
727              
728             =head1 ACKNOWLEDGEMENTS
729              
730              
731             =head1 LICENSE AND COPYRIGHT
732              
733             Copyright 2013 Christophe Ivorra.
734              
735             This program is free software; you can redistribute it and/or modify it
736             under the terms of either: the GNU General Public License as published
737             by the Free Software Foundation; or the Artistic License.
738              
739             See http://dev.perl.org/licenses/ for more information.
740              
741              
742             =cut
743              
744             1; # End of Chorus::Frame