| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Bio::ConnectDots::DotQuery; | 
| 2 | 1 |  |  | 1 |  | 32265 | use vars qw(@ISA @AUTO_ATTRIBUTES @OTHER_ATTRIBUTES %SYNONYMS %DEFAULTS); | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 191 |  | 
| 3 | 1 |  |  | 1 |  | 8 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 43 |  | 
| 4 |  |  |  |  |  |  | #use lib "/users/ywang/temp"; | 
| 5 | 1 |  |  | 1 |  | 768 | use Bio::ConnectDots::Connector; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 38 |  | 
| 6 | 1 |  |  | 1 |  | 875 | use Bio::ConnectDots::Dot; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 41 |  | 
| 7 | 1 |  |  | 1 |  | 30859 | use Bio::ConnectDots::DotQuery::Output; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 40 |  | 
| 8 | 1 |  |  | 1 |  | 1684 | use Bio::ConnectDots::DotQuery::Constraint; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 131 |  | 
| 9 | 1 |  |  | 1 |  | 9 | use Class::AutoClass; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1591 |  | 
| 10 |  |  |  |  |  |  | @ISA = qw(Class::AutoClass); # AutoClass must be first!! | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | @AUTO_ATTRIBUTES=qw(input dottable outputs constraints name2output); | 
| 13 |  |  |  |  |  |  | @OTHER_ATTRIBUTES=qw(); | 
| 14 |  |  |  |  |  |  | %SYNONYMS=(); | 
| 15 |  |  |  |  |  |  | %DEFAULTS=(name2output=>{}); | 
| 16 |  |  |  |  |  |  | Class::AutoClass::declare(__PACKAGE__); | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | sub _init_self { | 
| 19 | 1 |  |  | 1 |  | 969 | my($self,$class,$args)=@_; | 
| 20 | 1 | 50 |  |  |  | 5 | return unless $class eq __PACKAGE__; # to prevent subclasses from re-running this | 
| 21 | 1 | 50 |  |  |  | 26 | $self->throw("Required parameter -input missing") unless $self->input; | 
| 22 | 1 | 50 |  |  |  | 30 | $self->throw("Required parameter -outputs missing") unless $self->outputs; | 
| 23 |  |  |  |  |  |  | } | 
| 24 |  |  |  |  |  |  |  | 
| 25 | 0 |  |  | 0 | 0 |  | sub connectdots {$_[0]->dottable->connectdots;} | 
| 26 | 0 |  |  | 0 | 0 |  | sub name {$_[0]->dottable->name;} | 
| 27 | 0 |  |  | 0 | 0 |  | sub db {$_[0]->dottable->db;} | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | sub execute { | 
| 30 | 0 |  |  | 0 | 0 |  | my($self)=@_; | 
| 31 | 0 |  |  |  |  |  | $self->parse;			# parse syntax | 
| 32 | 0 |  |  |  |  |  | $self->normalize;		# normalize syntax | 
| 33 | 0 |  |  |  |  |  | $self->validate;		# do semantic checks | 
| 34 | 0 |  |  |  |  |  | $self->db_execute;		# really execute -- implemented in subclasses | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  | sub parse { | 
| 37 | 0 |  |  | 0 | 0 |  | my($self)=@_; | 
| 38 | 0 |  |  |  |  |  | $self->parse_outputs; | 
| 39 | 0 |  |  |  |  |  | $self->parse_constraints; | 
| 40 |  |  |  |  |  |  | } | 
| 41 |  |  |  |  |  |  | sub normalize { | 
| 42 | 0 |  |  | 0 | 0 |  | my($self)=@_; | 
| 43 | 0 |  |  |  |  |  | $self->normalize_outputs; | 
| 44 | 0 |  |  |  |  |  | $self->normalize_constraints; | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  | sub validate { | 
| 47 | 0 |  |  | 0 | 0 |  | my($self)=@_; | 
| 48 | 0 |  |  |  |  |  | $self->validate_outputs;	# implemented in subclass mixins | 
| 49 | 0 |  |  |  |  |  | $self->validate_constraints;	# implemented in subclass mixins | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  | sub parse_outputs { | 
| 52 | 0 |  |  | 0 | 0 |  | my($self)=@_; | 
| 53 | 0 |  |  |  |  |  | my $outputs=parse Bio::ConnectDots::DotQuery::Output($self->outputs); | 
| 54 | 0 |  |  |  |  |  | $self->outputs($outputs); | 
| 55 |  |  |  |  |  |  | } | 
| 56 |  |  |  |  |  |  | sub normalize_outputs { | 
| 57 | 0 |  |  | 0 | 0 |  | my($self)=@_; | 
| 58 | 0 |  |  |  |  |  | my $outputs=$self->outputs; | 
| 59 | 0 |  |  |  |  |  | my $normalized=[]; | 
| 60 | 0 |  |  |  |  |  | @$normalized=map {$_->normalize} @$outputs; | 
|  | 0 |  |  |  |  |  |  | 
| 61 | 0 |  |  |  |  |  | $self->outputs($normalized); | 
| 62 | 0 |  |  |  |  |  | my $name2output=$self->name2output; | 
| 63 | 0 |  |  |  |  |  | for my $output (@$normalized) { | 
| 64 | 0 |  |  |  |  |  | my $output_name=$output->output_name; | 
| 65 | 0 | 0 |  |  |  |  | $self->throw("Duplicate output: $output") if $name2output->{$output_name}; | 
| 66 | 0 |  |  |  |  |  | $name2output->{$output_name}=$output; | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  | sub parse_constraints { | 
| 70 | 0 |  |  | 0 | 0 |  | my($self)=@_; | 
| 71 | 0 |  |  |  |  |  | my $constraints=parse Bio::ConnectDots::DotQuery::Constraint($self->constraints); | 
| 72 | 0 |  |  |  |  |  | $self->constraints($constraints); | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  | sub normalize_constraints { | 
| 75 | 0 |  |  | 0 | 0 |  | my($self)=@_; | 
| 76 | 0 |  |  |  |  |  | my $constraints=$self->constraints; | 
| 77 | 0 |  |  |  |  |  | my $normalized=[]; | 
| 78 | 0 |  |  |  |  |  | @$normalized=map {$_->normalize} @$constraints; | 
|  | 0 |  |  |  |  |  |  | 
| 79 | 0 |  |  |  |  |  | $self->constraints($normalized); | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | # 'utility' method used in all subclasses | 
| 83 |  |  |  |  |  |  | # generate core where classes for constraint | 
| 84 |  |  |  |  |  |  | sub constraint_where { | 
| 85 | 0 |  |  | 0 | 0 |  | my($self,$constraint,$cs_id,$cd)=@_; | 
| 86 | 0 |  |  |  |  |  | my @where; | 
| 87 | 0 |  |  |  |  |  | push(@where,"$cd.connectorset_id=$cs_id"); | 
| 88 | 0 |  |  |  |  |  | my $label_ids=$constraint->label_ids; | 
| 89 |  |  |  |  |  |  | # if $label_ids is empty, the label was '*' -- matches all ids | 
| 90 | 0 | 0 |  |  |  |  | if (@$label_ids==1) { | 
|  |  | 0 |  |  |  |  |  | 
| 91 | 0 |  |  |  |  |  | push(@where,"$cd.label_id=".$label_ids->[0]); | 
| 92 |  |  |  |  |  |  | } elsif (@$label_ids>1) { | 
| 93 | 0 |  |  |  |  |  | push(@where,"$cd.label_id IN (".join(",",@$label_ids).")"); | 
| 94 |  |  |  |  |  |  | } | 
| 95 | 0 |  |  |  |  |  | my($op,$constants)=($constraint->op,$constraint->constants); | 
| 96 | 0 |  |  |  |  |  | my $db=$self->db; | 
| 97 | 0 |  |  |  |  |  | my @constants=map {$db->quote_dot($_)} @$constants; | 
|  | 0 |  |  |  |  |  |  | 
| 98 | 0 | 0 |  |  |  |  | if ($op=~/IN/) {		# IN or NOT IN | 
|  |  | 0 |  |  |  |  |  | 
| 99 | 0 |  |  |  |  |  | push(@where,"$cd.id $op (".join(",",@constants).")"); | 
| 100 |  |  |  |  |  |  | } elsif ($op ne 'EXISTS') {	# EXISTS has no constants -- needs no SQL condition | 
| 101 |  |  |  |  |  |  | # should only be 1 constant by now -- see Constraint::normalize | 
| 102 | 0 |  |  |  |  |  | push(@where,"$cd.id $op ".$db->quote($constants->[0])); | 
| 103 |  |  |  |  |  |  | } | 
| 104 | 0 | 0 |  |  |  |  | wantarray? @where: \@where; | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | # Removes entries from a table that are subsets of other rows on one identifier | 
| 108 |  |  |  |  |  |  | # usage: remove_subsets( ,  ) 
| 109 |  |  |  |  |  |  | sub remove_subsets { |  
| 110 | 0 |  |  | 0 | 0 |  | my ($self, $dbh, $TABLE, $key_name, $output_cols) = @_; |  
| 111 |  |  |  |  |  |  |  |  
| 112 |  |  |  |  |  |  | # setup translation hash and assign key index |  
| 113 | 0 |  |  |  |  |  | my $key_index; |  
| 114 | 0 |  |  |  |  |  | for(my $i=0; $i<@$output_cols; $i++) { |  
| 115 | 0 | 0 |  |  |  |  | $key_index = $i if $key_name eq $output_cols->[$i]; |  
| 116 |  |  |  |  |  |  | } |  
| 117 |  |  |  |  |  |  |  |  
| 118 | 0 |  |  |  |  |  | my $iterator = $dbh->prepare("SELECT DISTINCT * FROM $TABLE ORDER BY $key_name"); |  
| 119 | 0 |  |  |  |  |  | $iterator->execute(); |  
| 120 | 0 |  |  |  |  |  | my @list; |  
| 121 |  |  |  |  |  |  | my @delete; |  
| 122 | 0 |  |  |  |  |  | my $old_key; |  
| 123 | 0 |  |  |  |  |  | my $key_index=0; |  
| 124 | 0 |  |  |  |  |  | while (my @cols = $iterator->fetchrow_array()) { |  
| 125 | 0 |  |  |  |  |  | my $key = $cols[$key_index]; |  
| 126 | 0 | 0 |  |  |  |  | if($key ne $old_key) { # reset lists |  
| 127 | 0 |  |  |  |  |  | @list = undef; |  
| 128 | 0 |  |  |  |  |  | $old_key = $key; |  
| 129 |  |  |  |  |  |  | } |  
| 130 |  |  |  |  |  |  | # remove subset entries on image_id |  
| 131 |  |  |  |  |  |  |  |  
| 132 | 0 | 0 |  |  |  |  | if (@list) { # update list to exclude subsets |  
| 133 | 0 |  |  |  |  |  | my $add_it = 1; |  
| 134 | 0 |  |  |  |  |  | for(my $i=0; $i<=$#list; $i++) { |  
| 135 | 0 | 0 |  |  |  |  | next unless $list[$i]; |  
| 136 | 0 | 0 |  |  |  |  | if ($self->subset(\@cols, $list[$i]) ) { # skip this row if it's a subset |  
| 137 | 0 |  |  |  |  |  | $add_it = 0; |  
| 138 | 0 |  |  |  |  |  | push @delete, \@cols; |  
| 139 | 0 |  |  |  |  |  | last; |  
| 140 |  |  |  |  |  |  | } |  
| 141 | 0 | 0 |  |  |  |  | if ($self->subset($list[$i], \@cols)) { # remove entries that are subset of present |  
| 142 | 0 |  |  |  |  |  | push @delete, $list[$i]; |  
| 143 | 0 |  |  |  |  |  | $list[$i] = ''; |  
| 144 |  |  |  |  |  |  | } |  
| 145 |  |  |  |  |  |  | } |  
| 146 | 0 | 0 |  |  |  |  | push @list, \@cols if $add_it; # add non subset rows |  
| 147 |  |  |  |  |  |  | } |  
| 148 | 0 |  |  |  |  |  | else { push @list, \@cols; } |  
| 149 |  |  |  |  |  |  | } |  
| 150 |  |  |  |  |  |  |  |  
| 151 |  |  |  |  |  |  | ### delete rows from table |  
| 152 | 0 |  |  |  |  |  | foreach my $cols (@delete) { |  
| 153 | 0 | 0 |  |  |  |  | next unless $cols; # ignore empty rows in the list |  
| 154 | 0 |  |  |  |  |  | my $sql = "DELETE FROM $TABLE WHERE"; |  
| 155 | 0 |  |  |  |  |  | for(my $i=0; $i<@$output_cols; $i++) { |  
| 156 | 0 | 0 |  |  |  |  | $sql .= " AND" if $i>0; |  
| 157 | 0 | 0 |  |  |  |  | if($cols->[$i]) { |  
| 158 | 0 |  |  |  |  |  | $sql .=  " $output_cols->[$i]='$cols->[$i]'"; |  
| 159 |  |  |  |  |  |  | } |  
| 160 |  |  |  |  |  |  | else { |  
| 161 | 0 |  |  |  |  |  | $sql .=  " $output_cols->[$i] IS NULL"; |  
| 162 |  |  |  |  |  |  | } |  
| 163 |  |  |  |  |  |  | } |  
| 164 | 0 |  |  |  |  |  | $dbh->do($sql); |  
| 165 |  |  |  |  |  |  | } |  
| 166 |  |  |  |  |  |  | } |  
| 167 |  |  |  |  |  |  |  |  
| 168 |  |  |  |  |  |  | ### returns true if first is a subset of second, false otherwise |  
| 169 |  |  |  |  |  |  | sub subset { |  
| 170 | 0 |  |  | 0 | 0 |  | my ($self, $first, $second) = @_; # pointers to the two lists to compare |  
| 171 | 0 | 0 |  |  |  |  | return 0 if @{$first} > @{$second}; |  
|  | 0 |  |  |  |  |  |  |  
|  | 0 |  |  |  |  |  |  |  
| 172 | 0 |  |  |  |  |  | for (my $i=0; $i<@{$second}; $i++) { |  
|  | 0 |  |  |  |  |  |  |  
| 173 | 0 | 0 | 0 |  |  |  | return 0 if !$second->[$i] && $first->[$i];  # 0 1 |  
| 174 | 0 | 0 | 0 |  |  |  | return 0 if $first->[$i] && $second->[$i] && $first->[$i] ne $second->[$i];  # 1 != 1 |  
|  |  |  | 0 |  |  |  |  |  
| 175 |  |  |  |  |  |  | } |  
| 176 | 0 |  |  |  |  |  | return 1; |  
| 177 |  |  |  |  |  |  | } |  
| 178 |  |  |  |  |  |  |  |  
| 179 |  |  |  |  |  |  |  |  
| 180 |  |  |  |  |  |  |  |  
| 181 |  |  |  |  |  |  |  |  
| 182 |  |  |  |  |  |  | 1; |  |