File Coverage

blib/lib/FrameNet/QueryData.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package FrameNet::QueryData;
2              
3             require Exporter;
4             our @ISA = qw(Exporter);
5             our $VERSION = '0.07';
6              
7 3     3   81482 use Carp;
  3         15  
  3         255  
8 3     3   19 use warnings;
  3         7  
  3         88  
9 3     3   18 use strict;
  3         9  
  3         128  
10 3     3   3456 use Storable;
  3         16774  
  3         243  
11 3     3   4925 use XML::TreeBuilder;
  0            
  0            
12             use XML::XPath;
13             use File::Spec;
14             use Data::Dumper;
15              
16             my $CACHE_VERSION = '0.03.2';
17              
18             sub new {
19             my $class = shift;
20             my $self = {};
21              
22             $class = ref $class || $class;
23              
24             bless $self, $class;
25            
26             my %params = @_;
27              
28             ##############
29             ### FNHOME ###
30             ##############
31             # precedence: parameter, environment variable
32             if (defined $params{-fnhome}) {
33             $self->fnhome($params{-fnhome});
34             } elsif (defined $ENV{FNHOME}) {
35             $self->fnhome($ENV{FNHOME});
36             } else {
37             carp "FrameNet could not be found. Did you set \$FNHOME?\n";
38            
39             }
40              
41             ###############
42             ### VERBOSE ###
43             ###############
44             if (defined $params{-verbose}) {
45             $self->verbose($params{-verbose})
46             } else {
47             # Default: No output
48             $self->verbose(0);
49             };
50              
51             ###############
52             ### CACHE #####
53             ###############
54             if (defined $params{-cache}) {
55             $self->cache(1);
56             } else {
57             $self->cache(0);
58             }
59              
60             # Currently no cache system available
61             # $self->cache(0);
62             # $self->{VCACHE} = 0.01;
63            
64             my $infix = "xml";
65             $infix = "frXML" if (-e File::Spec->catfile(($self->fnhome,"frXML"),
66             "frames.xml"));
67             $self->file_frames_xml(File::Spec->catfile(($self->fnhome,$infix),
68             "frames.xml"));
69             $infix = "xml" if (-e File::Spec->catfile(($self->fnhome,"xml"),
70             "frRelation.xml"));
71             $self->file_frrelation_xml(File::Spec->catfile(($self->fnhome,$infix),
72             "frRelation.xml"));
73            
74             # no cache in this version
75              
76              
77             return $self;
78             }
79              
80             sub _init_cache {
81             my $self = shift;
82              
83              
84             # Used for untainting
85             my $u = $ENV{'USER'};
86            
87             if ($u =~ /^([\w\.\-]+)$/) {
88             $u = $1;
89             } else {
90             $u = 'user';
91             }
92              
93             $self->{'cachefilename'} = File::Spec->catfile((File::Spec->tmpdir),$u."-FrameNet-QueryData-".$CACHE_VERSION.".dat");
94              
95             if ($self->cache) {
96             if (! -e $self->{'cachefilename'}) {
97             store({}, $self->{'cachefilename'});
98             }
99             $self->{'cache'} = retrieve($self->{'cachefilename'});
100             }
101              
102             return $self->cache;
103             }
104              
105             sub _store_cache {
106             my $self = shift;
107             if ($self->cache) {
108             store($self->{'cache'}, $self->{'cachefilename'});
109             }
110             }
111              
112             sub fnhome {
113             my ($self, $fnhome) = @_;
114             $self->{'fnhome'} = $fnhome if (defined $fnhome);
115             return $self->{'fnhome'};
116             }
117              
118             sub verbose {
119             my ($self, $verbose) = @_;
120             $self->{'verbose'} = $verbose if (defined $verbose);
121             return $self->{'verbose'};
122             }
123              
124             sub cache {
125             my ($self, $cache) = @_;
126             $self->{'cache_enabled'} = $cache if (defined $cache);
127             return $self->{'cache_enabled'};
128             }
129              
130             sub frame {
131             my ($self, $framename) = @_;
132             return {} if (not defined $framename);
133             if ($self->cache) {
134             $self->_init_cache;
135             if (exists($self->{'cache'}{'frames'}{$framename})) {
136             return $self->{'cache'}{'frames'}{$framename};
137             }
138             }
139             my $ret = {};
140             $ret->{'name'} = $framename;
141             $self->parse;
142             $ret->{'lus'} = $self->_lu_part_of_frame($framename);
143             $ret->{'fes'} = $self->_fe_part_of_frame($framename);
144              
145             if ($self->cache) {
146             $self->_init_cache;
147             $self->{'cache'}{'frames'}{$framename} = $ret;
148             $self->_store_cache;
149             }
150              
151             return $ret;
152             };
153              
154             sub related_frames {
155             my ($self, $framename, $relation) = @_;
156             $self->xparse;
157             return $self->{'rels'}->{$relation}->{$framename};
158             };
159              
160             sub related_inv_frames {
161             my ($self, $framename, $relation) = @_;
162             $self->xparse;
163             return $self->{rels}->{$relation}->{'inverse'}->{$framename};
164             };
165              
166             sub _fe_part_of_frame {
167             my ($self, $framename) = @_;
168             my $partnodes = $self->_part_of_frame($framename, 'fe');
169             my $ret = [];
170             foreach my $pa (@$partnodes) {
171             push(@$ret, { 'name' => $pa->find('@name')->string_value,
172             'ID' => $pa->find('@ID')->string_value,
173             'abbrev' => $pa->find('@abbrev')->string_value,
174             'coreType' => $pa->find('@coreType')->string_value });
175             }
176             return $ret;
177             };
178              
179              
180             sub _lu_part_of_frame {
181             my ($self, $framename) = @_;
182             my $partnodes = $self->_part_of_frame($framename, 'lexunit');
183             my $ret = [];
184             foreach my $pa (@$partnodes) {
185             my $name = $pa->find('@name')->string_value;
186             chop $name;
187             chop $name;
188              
189              
190             push(@$ret, { 'name' => $name,
191             'ID' => $pa->find('@ID')->string_value,
192             'pos' => $pa->find('@pos')->string_value,
193             'status' => $pa->find('@status')->string_value,
194             'lemmaId' => $pa->find('@lemmaId')->string_value });
195             }
196             return $ret;
197             };
198              
199             sub _part_of_frame {
200             my ($self, $framename, $part) = @_;
201             $self->parse;
202             my @parts = $self->{xtree}->
203             find('//frames/frame[@name="'.$framename.'"]/'.$part.'s/'.$part)->
204             get_nodelist;
205             return \@parts;
206             }
207              
208             sub related {
209             my $self = shift;
210             my ($f1,$f2) = @_;
211              
212             $self->xparse;
213              
214             foreach my $relname (keys %{$self->{'rels'}}) {
215             #print STDERR "Checking ".$relname."\n";
216             #print STDERR Dumper($self->{'rels'}{'inverse'}{$relname}{$f2});
217             return $relname if (grep(/$f2/, @{$self->{'rels'}{$relname}{$f1}}) or
218             grep(/$f1/, @{$self->{'rels'}{$relname}{$f2}}));
219             };
220             return 0;
221            
222             };
223              
224             sub transitive_related {
225             my $self = shift;
226             my ($frame1, $frame2) = @_;
227              
228             $self->xparse;
229              
230             foreach my $relname (keys %{$self->{'rels'}}) {
231             if (grep(/$frame2/, @{$self->{'rels'}{$relname}{$frame1}}) or
232             grep(/$frame1/, @{$self->{'rels'}{$relname}{$frame2}})) {
233             #print STDERR $relname;
234              
235             return 1;
236             }
237             foreach my $f (@{$self->{'rels'}{$relname}{$frame1}},
238             @{$self->{'rels'}{$relname}{$frame2}}) {
239             if ($self->transitive_related($frame1, $f)) {
240             #print STDERR $f."\n";
241             return 1;
242             }
243             }
244             }
245             return 0;
246             }
247              
248             sub path_related {
249             my $self = shift;
250             my $frame1 = shift;
251             my $frame2 = shift;
252             my @path = @_;
253              
254             $self->xparse;
255             #print STDERR "$frame1 vs. $frame2 ".join(', ', @path)."\n";
256             if (@path == 0) {
257             return ($frame1 eq $frame2);
258             }
259              
260             my $rel = shift(@path);
261              
262             foreach my $f (@{$self->{'rels'}{$rel}{$frame1}}) {
263             return 1 if ($f eq $frame2);
264             return 1 if ($self->path_related($f, $frame2, @path));
265             };
266            
267             foreach my $f (@{$self->{'rels'}{$rel}{'inverse'}{$frame1}}) {
268             return 1 if ($f eq $frame2);
269             return 1 if ($self->path_related($f, $frame2, @path));
270             };
271              
272             return 0;
273             }
274              
275             sub dumpout {
276             my $self = shift;
277             $self->xparse;
278             print Dumper($self->{rels});
279             };
280              
281             sub xparse {
282             my $self = shift;
283             if (! defined $self->{'xp'}) {
284             if ($self->_init_cache and exists($self->{'cache'}{'rels'})) {
285             $self->{'rels'} = $self->{'cache'}->{'rels'};
286             } else {
287              
288             print STDERR "Parsing XML file (frRelation.xml)\n" if ($self->verbose > 0);
289             $self->{'xp'} = XML::XPath->new(filename => $self->file_frrelation_xml);
290            
291             foreach my $frame_relation ($self->{'xp'}->find("//frame-relation-type/frame-relations/frame-relation")->get_nodelist) {
292            
293             my $relation_type = $frame_relation->find('../../@name')->string_value;
294            
295             my $super = $frame_relation->find('@superFrameName')->string_value;
296             my $sub = $frame_relation->find('@subFrameName')->string_value;
297            
298             push(@{$self->{rels}->{$relation_type}->{$sub}},$super);
299             # if (! grep(/$super/,@{$self->{rels}->{$relation_type}->{$sub}}));
300             push(@{$self->{rels}->{$relation_type}->{'inverse'}->{$super}},$sub);
301             # if (! grep(/$super/,@{$self->{rels}->{$relation_type}->{'inverse'}->{$super}}));
302             };
303             if ($self->_init_cache) {
304             $self->{'cache'}->{'rels'} = $self->{'rels'};
305             $self->_store_cache;
306             }
307             };
308             };
309             };
310              
311             sub file_frames_xml {
312             my ($self, $fname) = @_;
313             $self->{'file_frames_xml'} = $fname if (defined $fname);
314             return $self->{'file_frames_xml'};
315             }
316              
317             sub file_frrelation_xml {
318             my ($self, $fname) = @_;
319             $self->{'file_frrelation_xml'} = $fname if (defined $fname);
320             return $self->{'file_frrelation_xml'};
321             }
322              
323             sub parse {
324             my $self = shift;
325             if (not (defined $self->{xtree})) {
326             print STDERR "Parsing XML file (frames.xml)\n" if ($self->verbose > 0);
327             $self->{xtree} = XML::XPath->new(filename => $self->file_frames_xml);
328             };
329             };
330              
331             sub frames {
332             my $self = shift;
333            
334             if ($self->cache) {
335             $self->_init_cache;
336             if (exists($self->{'cache'}{'all_frames'})) {
337             return @{$self->{'cache'}{'all_frames'}};
338             }
339             }
340              
341             $self->parse;
342              
343             my $frames;
344             foreach my $frame ($self->{xtree}->find("//frames/frame")->get_nodelist) {
345             $frames->{$frame->find('@name')->string_value} = 1;
346             };
347              
348             my @all_frames = keys %$frames;
349            
350             if ($self->cache) {
351             $self->_init_cache;
352             $self->{'cache'}{'all_frames'} = \@all_frames;
353             $self->_store_cache;
354             }
355              
356             return @all_frames;
357             }
358              
359              
360             =head1 NAME
361              
362             FrameNet::QueryData - A module for accessing the FrameNet data.
363              
364             =head1 VERSION
365              
366             Version 0.03
367              
368             =head1 SYNOPSIS
369              
370             use FrameNet::QueryData;
371              
372             # The name of the frame
373             my $framename = "Getting";
374              
375             my $qd = FrameNet::QueryData->new(-fnhome => $ENV{'FNHOME'},
376             -verbose => 0,
377             -cache => 1);
378            
379             my $frame = $qd->frame($framename);
380             # Getting the lexical units
381             my $lus = $frame->{'lus'};
382             # Getting the frame elements
383             my $fes = $frame->{'fes'}
384              
385             # Listing the names of all lexical units
386             print join(', ', map { $_->{'name'} } @$lus);
387              
388             # Listing all frames that are used by Getting
389             print $qd->related_frames('Getting', 'Using');
390              
391             # List all frames that use Getting
392             print $qd->related_inv_frames('Getting', 'Using');
393              
394             # Find out if two frames are directly related
395             print "They are!" if ($qd->related("Getting", "Intentionally_create"));
396              
397             # Find out, if two frames are related through a Using relation
398             print "They are!" if ($qd->path_related("Getting", "Intentionally_create", "Using"));
399              
400             # Find out if two frames are related through some relations and other frames, i.e. indirectly related
401             print "They are!" if ($qd->transitive_related("Getting", "Intentionally_create"));
402              
403             # Printing a list of all frames
404             print join(', ', $qd->frames);
405              
406             =head1 DESCRIPTION
407              
408             The purpose of this module is to provide an easy access to FrameNet. Its database is organized in large XML files, which are parsed by this module. The module has been tested with FrameNet 1.2 and 1.3. Other versions may work, but it is not guaranteed.
409              
410             =head1 METHODS
411              
412             =over 4
413              
414             =item new ( -fnhome, -verbose, -cache)
415              
416             The constructor for this class. It can take two arguments: The path to the FrameNet directory and a verbosity level. Both are not mandatory. -fnhome defaults to the environment variable $FNHOME, -verbose defaults to 0 (zero), which means no output.
417              
418             -cache (available since 0.03) controls, if the parsed data is kept in a file for later use. This increases performance significantly. The cache itself is located in the temporary directory of your system.
419              
420             =item fnhome ($FNHOME)
421              
422             Sets and returns the FrameNet home directory. If the argument is given, it will be set to the new value. If the argument is omitted, the value will be returned.
423              
424             =item verbose ($VERBOSE)
425              
426             Sets and returns the verbosity level. If the argument is given, the verbosity level will be set to this new value. If not, the value is returned.
427              
428             =item frame ($FRAMENAME)
429              
430             This method returns a hash containing information for the frame $FRAMENAME. The hash has three elements:
431              
432             =over 8
433              
434             =item name
435              
436             The name of the frame
437              
438             =item lus
439              
440             A list containing all the lexical units of the frame. The lexical units are represented by another hash containing the keys 'name', 'ID', 'pos', 'status' and 'lemmaId'.
441              
442             =item fes
443              
444             A list containg all the frame elements for this frame. The frame elements are represented by a hash containing the keys 'name', 'ID', 'abbrev' and 'coreType'.
445              
446             =back
447              
448             =item related_frames ($FRAMENAME, $RELATIONNAME)
449              
450             This method returns a list of frame names, that are related to $FRAMENAME via the relation $RELATIONNAME.
451              
452             =item related_inv_frames ($FRAMENAME, $RELATIONNAME)
453              
454             Does the same as L, but in the other direction of the relation. Using the relation "Inheritance", you can ask for the superordinated frames for example.
455              
456             =item related ( $FRAME1, $FRAME2 )
457              
458             Checks, if $FRAME1 and $FRAME2 are somehow related. If they are related, the exact name of the relation is returned. Otherwise, a 0 (zero) is returned. Note, that this method is not transitive.
459              
460             =item transitive_related ( $FRAME1, $FRAME2 )
461              
462             Checks, if $FRAME1 and $FRAME2 are somehow related. There is no limit on the maximum number of steps, so this method can be slow. And it will probably run forever, if a frame is related to itself.
463              
464             =item path_related ( $FRAME1, $FRAME2, @RELATIONS )
465              
466             With this method, one can check if $FRAME1 and $FRAME2 are related through the given path. The path itself is a list of relations. The method tries to explore all the possiblities along the path, so it is also slow.
467              
468             =item frames ( )
469              
470             Returns a list (NOT a reference to a list) of all frames that are defined in FrameNet.
471              
472             =item file_frames_xml ( $PATH )
473              
474             Can be used to get and set the path to the file frames.xml. To get it, just use it without argument.
475              
476             =item file_frrelation_xml ( $PATH )
477              
478             Can be used to get and set the path to the file frrelation.xml. To get, use it without argument.
479              
480             =item cache ( $cache )
481              
482             En- or disables the cache. If $cache is defined, it is enabled.
483             This method is experimental!
484              
485             =item dumpout ( )
486              
487             This method prints the entire object using Data::Dumper. Can be used to debug the class.
488              
489             =item parse ( )
490              
491             Internal method.
492              
493             =item xparse ( )
494              
495             Internal method.
496              
497              
498              
499             =back
500              
501             =head1 AUTHOR
502              
503             Nils Reiter, C<< >>
504              
505             =head1 BUGS
506              
507             Please report any bugs or feature requests to C.
508              
509             =head1 COPYRIGHT & LICENSE
510              
511             Copyright 2005 Nils Reiter and Aljoscha Burchardt, all rights reserved.
512              
513             This program is free software; you can redistribute it and/or modify it
514             under the same terms as Perl itself.
515              
516             =cut
517              
518             1; # End of FrameNet::QueryData