X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2FID3FS%2FPath.pm;h=5c7eacce84a99d4a77211a43473290a670fe42ad;hb=383168447067bcf6a257ca77eb5c6fd4a95821bc;hp=3335e445517a688634e97b0e990373aadf8337ea;hpb=f47bb604b3d6f4a1757656f1b8df10a30ebe3de2;p=id3fs.git diff --git a/lib/ID3FS/Path.pm b/lib/ID3FS/Path.pm index 3335e44..5c7eacc 100644 --- a/lib/ID3FS/Path.pm +++ b/lib/ID3FS/Path.pm @@ -8,11 +8,14 @@ use ID3FS::PathElement::Boolean; use ID3FS::PathElement::File; use ID3FS::PathElement::Tag; use ID3FS::PathElement::Tagval; +use ID3FS::Path::Node; our ($STATE_INVALID, $STATE_ROOT, $STATE_TAG, $STATE_TAGVAL, $STATE_BOOLEAN, $STATE_ALBUMS, $STATE_TRACKLIST, $STATE_FILE)=(0..7); +our %priorities=( "OR" => 0, "AND" => 1, "NOT" => 2 ); + sub new { my $proto=shift; @@ -20,6 +23,7 @@ sub new my $self={}; bless($self,$class); + $self->{elements}=[]; $self->{db}=shift; $self->{path}=shift; $self->parse(); @@ -53,10 +57,10 @@ sub isvalid sub dest { - my($self)=@_; + my($self, $mountpoint)=@_; if($self->state() == $STATE_FILE) { - return $self->{db}->filename(@{$self->{elements}}); + return $self->{db}->filename($mountpoint, $self); } return "ERROR"; #should never happen? } @@ -67,6 +71,7 @@ sub dirents my @dents=(); my $state=$self->state(); # print "DIRENTS: STATE: $state\n"; +# print "DIRENTS: FILE: $self->{path}\n"; if($state==$STATE_TAG || $state==$STATE_TAGVAL) { my $tag=$self->{elements}->[$#{$self->{elements}}]; @@ -75,29 +80,36 @@ sub dirents ref($tag) eq "ID3FS::PathElement::Tag" && $self->{db}->tag_has_values($tag->{id})) { - @dents=$self->{db}->tag_values($tag->{id}); + @dents=$self->{db}->tags($self); } else { @dents=(qw(AND OR TRACKS NOARTIST), - $self->{db}->artists(@{$self->{elements}})); + $self->{db}->artists($self)); } } elsif($state==$STATE_BOOLEAN) { - @dents=("NOT", $self->{db}->tags(@{$self->{elements}})); + my $parent=$self->{elements}->[$#{$self->{elements}}]; + unless(defined($parent) && + ref($parent) eq "ID3FS::PathElement::Boolean" && + $parent->{name} eq "NOT") + { + push(@dents, "NOT"); + } + push(@dents, $self->{db}->tags($self)); } elsif($state==$STATE_ROOT) { - @dents=(qw(ALL NOT), $self->{db}->tags(@{$self->{elements}})); + @dents=(qw(ALL NOT), $self->{db}->tags($self)); } elsif($state==$STATE_ALBUMS) { - @dents=(qw(TRACKS NOALBUM),$self->{db}->albums(@{$self->{elements}})); + @dents=(qw(TRACKS NOALBUM),$self->{db}->albums($self)); } elsif($state==$STATE_TRACKLIST) { - @dents=$self->{db}->tracks(@{$self->{elements}}); + @dents=$self->{db}->tracks($self); } else { @@ -135,6 +147,7 @@ sub parse } elsif($name eq "NOT") { + push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($self->{db}, $name)); $self->state($STATE_BOOLEAN); } else @@ -160,7 +173,8 @@ sub parse ref($tag) eq "ID3FS::PathElement::Tag" && $self->{db}->tag_has_values($tag->{id})) { - my $tagval=ID3FS::PathElement::Tagval->new($name); +# print "Parsing: parent: $tag->{id}\n"; + my $tagval=ID3FS::PathElement::Tag->new($self->{db}, $name, $tag->{id}); if(defined($tagval)) { $self->state($STATE_TAGVAL); @@ -183,12 +197,12 @@ sub parse elsif($name eq "AND") { $self->state($STATE_BOOLEAN); -# push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($name)); + push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($self->{db}, $name)); } elsif($name eq "OR") { $self->state($STATE_BOOLEAN); -# push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($name)); + push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($self->{db}, $name)); } else { @@ -207,9 +221,18 @@ sub parse elsif($state==$STATE_BOOLEAN) { # print "SM: BOOLEAN: $name\n"; - if($name eq "NOT") + my $parent=$self->{elements}->[$#{$self->{elements}}]; + my $allownot=1; + if(defined($parent) && + ref($parent) eq "ID3FS::PathElement::Boolean" && + $parent->{name} eq "NOT") + { + $allownot=0; + } + if($allownot && $name eq "NOT") { $self->state($STATE_BOOLEAN); + push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($self->{db}, $name)); } else { @@ -254,7 +277,6 @@ sub parse { # print "SM: TRACKLIST: $name\n"; my $track=ID3FS::PathElement::File->new($self->{db}, $name); - push(@{$self->{elements}}, $track); if($track) { push(@{$self->{elements}}, $track); @@ -277,6 +299,24 @@ sub parse $self->state($STATE_INVALID); } } + # remove trailing boolean + if(@{$self->{elements}} && + ref($self->{elements}->[$#{$self->{elements}}]) eq "ID3FS::PathElement::Boolean") + { + $self->{lastop}=pop @{$self->{elements}}; + } + # sort elements by precedence + @{$self->{elements}}=$self->sort_elements(@{$self->{elements}}); + $self->{tagtree}=$self->elements_to_tree([ @{$self->{elements}} ]); + if($self->{tagtree}) + { + ($self->{sqlconditions}, + $self->{andsneeded}) = $self->{tagtree}->to_sql(); +# print("SQL CONDITION(", $self->{andsneeded}, "): ", +# $self->{sqlconditions}, "\n"); +# use Data::Dumper; +# print Dumper $self->{tagtree}; + } } sub state @@ -286,4 +326,103 @@ sub state return $self->{state}; } +sub elements_to_tree +{ + my($self, $elements)=@_; + return undef unless(@$elements); + my ($left, $right, $op)=(undef, undef, undef); + my $thing=pop @$elements; + if(ref($thing) eq "ID3FS::PathElement::Boolean") + { + my $op=$thing; + $right=$self->elements_to_tree($elements); + if($op->{name} ne "NOT") + { + $left=$self->elements_to_tree($elements); + } + return ID3FS::Path::Node->new($left, $op, $right); + } + else + { + return ID3FS::Path::Node->new($thing); + } +} + +# Dijkstra's shunting-yard algorithm +sub sort_elements +{ + my ($self, @input)=@_; + my @opstack=(); + my @output=(); +# print "INPUT: ", join(', ', map { $_->{name}; } @input), "\n"; + while(my $thing = shift @input) + { + if(ref($thing) eq "ID3FS::PathElement::Tag") + { +# print "Pushing $thing->{name} to output\n"; + push(@output, $thing); +# print "OPSTACK: ", join(', ', map { $_->{name}; } @opstack), "\n"; +# print "OUTPUT: ", join(', ', map { $_->{name}; } @output), "\n"; + } + elsif(ref($thing) eq "ID3FS::PathElement::Boolean") + { +# print "BOOL: $thing->{name}\n"; + # bool +# print "thing: $thing->{name}: $priorities{$thing->{name}} "; + if(@opstack) + { +# print("topop: ", $opstack[$#opstack]->{name}, +# ": ", $priorities{$opstack[$#opstack]->{name}}, "\n"); + } + while(@opstack && + ($priorities{$thing->{name}} <= $priorities{$opstack[$#opstack]->{name}})) + { +# print "Pushing ", $opstack[$#opstack]->{name}, " from opstack to output\n"; + push(@output, pop(@opstack)); +# print "OPSTACK: ", join(', ', map { $_->{name}; } @opstack), "\n"; +# print "OUTPUT: ", join(', ', map { $_->{name}; } @output), "\n"; + } +# print "Pushing $thing->{name} to opstack\n"; + push(@opstack, $thing); +# print "OPSTACK: ", join(', ', map { $_->{name}; } @opstack), "\n"; +# print "OUTPUT: ", join(', ', map { $_->{name}; } @output), "\n"; + } + } + while(@opstack) + { + push(@output, pop(@opstack)); + } +# print "STACK: ", join(', ', map { $_->{name}; } @output), "\n"; + return @output; +} + +sub used_tags +{ + my($self)=@_; + print "TAGTREE UNDEF\n" unless(defined($self->{tagtree})); + return undef unless(defined($self->{tagtree})); + return($self->{tagtree}->used_tags()); +} + +sub tag_has_values +{ + my($self)=@_; + my $tail=$self->{elements}->[$#{$self->{elements}}]; + if($tail && ref($tail) eq "ID3FS::PathElement::Tag") + { + return($self->{db}->tag_has_values($tail->{id})); + } +} + +sub trailing_tag_id +{ + my($self)=@_; + my $tail=$self->{elements}->[$#{$self->{elements}}]; + if($tail && ref($tail) eq "ID3FS::PathElement::Tag") + { + return($tail->{id}); + } + return undef; +} + 1;