bdc403fda57f238972315a9b5ba1c5ae33beb73a
[id3fs.git] / lib / ID3FS / Path / Node.pm
1 # id3fs - a FUSE-based filesystem for browsing audio metadata
2 # Copyright (C) 2010  Ian Beckwith <ianb@erislabs.net>
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 package ID3FS::Path::Node;
18
19 use strict;
20 use warnings;
21
22 sub new
23 {
24     my $proto=shift;
25     my $class=ref($proto) || $proto;
26     my $self={};
27     bless($self,$class);
28
29     my $db=shift;
30     $self->{type}=shift;
31     $self->{name}=shift;
32     $self->{parents_id}=shift;
33     if($self->{type} ne "boolean")
34     {
35         my $table=ucfirst($self->{type});
36         $table .= "s" unless($table=~/s$/);
37         $self->{id}=$db->lookup_id($table, $self->{name}, $self->{parents_id});
38         return undef unless(defined($self->{id}));
39     }
40     return $self;
41 }
42
43 sub set
44 {
45     my($self, $name, $val)=@_;
46     if(defined($val))
47     {
48         $self->{$name}=$val;
49     }
50     return $self->{$name};
51 }
52
53 sub left       { return shift->set("left",       shift); }
54 sub right      { return shift->set("right",      shift); }
55 sub name       { return shift->set("name",       shift); }
56 sub type       { return shift->set("type",       shift); }
57 sub id         { return shift->set("id",         shift); }
58 sub parents_id { return shift->set("parents_id", shift); }
59
60 sub print
61 {
62     my($self)=@_;
63     my $op=$self->name();
64     my $left=$self->left();
65     my $right=$self->right();
66     return "" unless($left || $right);
67     my $str .= $self->print_node($left);
68     $str .= (" " . $op . " ") if($op);
69     $str .= $self->print_node($right);
70     if($op || ($left && $right))
71     {
72         $str="(" . $str . ")";
73     }
74     return $str;
75 }
76
77 sub print_node
78 {
79     my($self, $node)=@_;
80     return "" unless(defined($node));
81     return $node->print() if(ref($node) eq "ID3FS::Path::Node");
82     return $node->{name};
83 }
84
85 sub to_sql
86 {
87     my($self, $hasvals, $not, @joins)=@_;
88     $not=0 unless(defined($not));
89     my @outjoins=();
90     my $str='';
91     # init
92     unless(@joins)
93     {
94         @outjoins = @joins = ("INNER");
95     }
96
97     if($self->type() ne "boolean")
98     {
99         my $cnt=scalar(@joins)+1;
100         $str .= "t" . scalar(@joins) . ".id='" . $self->{id} . "'";
101         if($not && !$hasvals)
102         {
103             $str .= " AND fxt" . scalar(@joins) . ".files_id IS NULL";
104         }
105         return ($str, @outjoins);
106     }
107
108     my $left=$self->left();
109     my $right=$self->right();
110     return ("", @outjoins) unless($left || $right);
111     my ($leftstr, @leftjoins) = $left->to_sql($hasvals, $not, @joins) if($left);
112     push(@joins, @leftjoins);
113     push(@outjoins, @leftjoins);
114     my $op=$self->name();
115     print "op: $op type: ", $self->type(), " not: $not\n";
116     if(defined($op))
117     {
118         # if we are ANDing, add an inner join
119         # also if we are NOTing, but we are looking for a tag *value*
120         if($op eq "AND")
121         {
122             print "AND\n";
123             # hack - if right child is a NOT, we don't need extra join/brackets
124             # NOT will do the same and we will end up with an extra one
125             unless($right && $right->name() && $right->name() eq "NOT")
126             {
127                 push(@joins, "INNER");
128                 push(@outjoins, "INNER");
129             }
130         }
131         elsif($op eq "NOT")
132         {
133             print "NOT (was $not)\n";
134             $not=1;
135             # as above - if right child is a NOT, we don't need extra join/brackets
136             # NOT will do the same and we will end up with an extra one
137             unless($right && $right->name() && $right->name() eq "NOT")
138             {
139                 if($hasvals)
140                 {
141                     push(@joins, "INNER");
142                     push(@outjoins, "INNER");
143                 }
144                 else
145                 {
146                     push(@joins, "LEFT");
147                     push(@outjoins, "LEFT");
148                 }
149             }
150 #           print("LEFT: ", $left->print(), "\n") if ($left);
151 #           print("RIGHT: ", $right->print(), "\n") if($right);
152         }
153         elsif($op eq "OR")
154         {
155             print "OR\n";
156             # if left child is a NOT, we need an extra (inner) join
157             # unless right child is also a NOT
158             if(($left && $left->name() && $left->name() eq "NOT") &&
159                !($right && $right->name() && $right->name() eq "NOT"))
160             {
161                 push(@joins, "INNER");
162                 push(@outjoins, "INNER");
163             }
164         }
165     }
166     my ($rightstr, @rightjoins) = $right->to_sql($hasvals, $not, @joins) if($right);
167     push(@outjoins, @rightjoins);
168 #    print "LEFT (", scalar(@leftjoins), "): $leftstr\n";
169 #    print "RIGHT (", scalar(@rightjoins), "): $rightstr\n";
170     $str=$leftstr;
171     $str .= " $op " if($op && !$not);
172     $str .= $rightstr;
173     if($op || ($left && $right))
174     {
175         $str="(" . $str . ")";
176     }
177 #    print "STR: $str\n";
178 #    my @all=(@joins, @rightjoins);
179 #    print "JOINS: RETURN ", scalar(@outjoins), " ALL ", scalar(@all), "\n";
180     return($str, @outjoins);
181 }
182
183 sub used_tags
184 {
185     my($self)=@_;
186     if($self->type() eq "boolean")
187     {
188         my @used=();
189         push(@used, $self->left()->used_tags())  if($self->left());
190         push(@used, $self->right()->used_tags()) if($self->right());
191         return(grep { defined; } @used);
192     }
193     return $self->id();
194 }
195
196 1;