line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Alzabo::Display::SWF::Schema; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
2325
|
use SWF qw(:ALL); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use Alzabo::Display::SWF::Util qw/rgb2ary button_shape get_coordinates/; |
8
|
|
|
|
|
|
|
use Alzabo::Display::SWF::Table; |
9
|
|
|
|
|
|
|
use Alzabo::Display::SWF::Column; |
10
|
|
|
|
|
|
|
use Alzabo::Display::SWF::Key; |
11
|
|
|
|
|
|
|
use GraphViz; |
12
|
|
|
|
|
|
|
use Alzabo::Runtime; |
13
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
SWF::setVersion(5); |
16
|
|
|
|
|
|
|
my $scale = 5; |
17
|
|
|
|
|
|
|
SWF::setScale($scale); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub new { |
20
|
|
|
|
|
|
|
my $pkg = shift; |
21
|
|
|
|
|
|
|
my %p = @_; |
22
|
|
|
|
|
|
|
my $self = {}; |
23
|
|
|
|
|
|
|
bless $self, $pkg; |
24
|
|
|
|
|
|
|
$self->{mov} = new SWF::Movie; |
25
|
|
|
|
|
|
|
$self->{ars} = Alzabo::Runtime::Schema->load_from_file(name => $p{name}); |
26
|
|
|
|
|
|
|
$self->{cfg} = $p{cfg}; |
27
|
|
|
|
|
|
|
$self->{gvz} = GraphViz->new( qw/ |
28
|
|
|
|
|
|
|
layout neato |
29
|
|
|
|
|
|
|
no_overlap 1 |
30
|
|
|
|
|
|
|
/, |
31
|
|
|
|
|
|
|
node => {qw/ shape box fontname Courier /}, |
32
|
|
|
|
|
|
|
graph => {qw/ splines false /, label => $p{name} }, |
33
|
|
|
|
|
|
|
); |
34
|
|
|
|
|
|
|
return $self; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub create_graph { |
38
|
|
|
|
|
|
|
my $self = shift; |
39
|
|
|
|
|
|
|
my $g = $self->{gvz}; |
40
|
|
|
|
|
|
|
my (%fk, %tm); |
41
|
|
|
|
|
|
|
my @t = $self->{ars}->tables; |
42
|
|
|
|
|
|
|
for my $t ( @t ) { |
43
|
|
|
|
|
|
|
my $n = $t->name; |
44
|
|
|
|
|
|
|
$tm{$n} = Alzabo::Display::SWF::Table->new( |
45
|
|
|
|
|
|
|
$t, $self->{mov}, $scale, $self->{cfg}, |
46
|
|
|
|
|
|
|
); |
47
|
|
|
|
|
|
|
$g->add_node( $n, |
48
|
|
|
|
|
|
|
height => ( 2 + $tm{$t->name}->{height} )/72, |
49
|
|
|
|
|
|
|
width => ( 2 + $tm{$t->name}->{width} )/72 |
50
|
|
|
|
|
|
|
); |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
foreach my $fk ($t->all_foreign_keys) { |
53
|
|
|
|
|
|
|
my @from_id = qw( columns_from columns_to ); |
54
|
|
|
|
|
|
|
my $id1 = join "\0", map { $_->name } map { $fk->$_() } |
55
|
|
|
|
|
|
|
@from_id, qw( table_from table_to ); |
56
|
|
|
|
|
|
|
$id1 .= "\0"; |
57
|
|
|
|
|
|
|
$id1 .= join "\0", $fk->cardinality; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
my @to_id = qw( columns_to columns_from ); |
60
|
|
|
|
|
|
|
my $id2 = join "\0", map { $_->name } map { $fk->$_() } |
61
|
|
|
|
|
|
|
@to_id, qw( table_to table_from ); |
62
|
|
|
|
|
|
|
$id2 .= "\0"; |
63
|
|
|
|
|
|
|
$id2 .= join "\0", reverse $fk->cardinality; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
next if $fk{$id1} || $fk{$id2}; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
my %p; |
68
|
|
|
|
|
|
|
my ($taillabel) = $fk->cardinality; |
69
|
|
|
|
|
|
|
$taillabel .= 'd' if $fk->from_is_dependent; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
my ($headlabel) = reverse $fk->cardinality; |
72
|
|
|
|
|
|
|
$headlabel .= 'd' if $fk->to_is_dependent; |
73
|
|
|
|
|
|
|
$p{taillabel} = $taillabel; |
74
|
|
|
|
|
|
|
$p{headlabel} = $headlabel; |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
if ($fk->is_one_to_one) { |
77
|
|
|
|
|
|
|
$p{dir} = 'none'; |
78
|
|
|
|
|
|
|
$p{arrowhead} = $fk->from_is_dependent ? 'dot' : 'odot'; |
79
|
|
|
|
|
|
|
$p{arrowtail} = $fk->to_is_dependent ? 'dot' : 'odot'; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
elsif ($fk->is_many_to_one) { |
82
|
|
|
|
|
|
|
$p{dir} = 'forward'; |
83
|
|
|
|
|
|
|
$p{arrowhead} = $fk->from_is_dependent ? 'dot' : 'odot'; |
84
|
|
|
|
|
|
|
$p{arrowtail} = $fk->to_is_dependent ? 'invdot' : 'invodot'; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
else { |
87
|
|
|
|
|
|
|
$p{dir} = 'back'; |
88
|
|
|
|
|
|
|
$p{arrowhead} = $fk->from_is_dependent ? 'invdot' : 'invodot'; |
89
|
|
|
|
|
|
|
$p{arrowtail} = $fk->to_is_dependent ? 'dot' : 'odot'; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# PLAYED AROUND WITH WEIGHT AND LEN OF EDGES IN ORDER TO MAKE |
93
|
|
|
|
|
|
|
# THE PICTURES OF BIG DATABASES SMALLER. |
94
|
|
|
|
|
|
|
my @w = map { ( $fk->$_->all_foreign_keys ) } qw/table_from table_to/; |
95
|
|
|
|
|
|
|
# $p{weight} = 5 / scalar @w; |
96
|
|
|
|
|
|
|
$p{len} = scalar @w / ( 2 * sqrt(scalar @t) ); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
$g->add_edge( $fk->table_from->name, $fk->table_to->name, %p ); |
99
|
|
|
|
|
|
|
$fk{$id1} = $fk{$id2} = 1; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
$self->{tmv} = \%tm; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub create_movie { |
106
|
|
|
|
|
|
|
my $self = shift; |
107
|
|
|
|
|
|
|
local $_ = $self->{gvz}->as_text; |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# DIMENSION OF THE GRAPH/MOVIE |
110
|
|
|
|
|
|
|
my ($x, $y) = |
111
|
|
|
|
|
|
|
( $self->{X}, $self->{Y} ) = /graph \[.*bb="0,0,(\d+),(\d+)".*\];/; |
112
|
|
|
|
|
|
|
my $m = $self->{mov}; |
113
|
|
|
|
|
|
|
$m->setDimension( $x+10, $y+10 ); |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# POSITIONS OF TABLES IN THE MOVIE |
116
|
|
|
|
|
|
|
my %tm = %{ $self->{tmv} }; |
117
|
|
|
|
|
|
|
for my $t ( $self->{ars}->tables ) { |
118
|
|
|
|
|
|
|
my $n = $t->name; |
119
|
|
|
|
|
|
|
/^\s*$n\s\[.*pos="(\d+),(\d+)".*\];/m |
120
|
|
|
|
|
|
|
or die "Didn't find table $n in GraphViz Output"; |
121
|
|
|
|
|
|
|
$tm{$n}->{dx} = $1 - int($tm{$n}->{width} / 2); |
122
|
|
|
|
|
|
|
$tm{$n}->{dy} = $y - ( $2 + 6 + int($tm{$n}->{height} / 2) ); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# TABLE KEYS (PRIMARY AND FOREIGN) |
126
|
|
|
|
|
|
|
my $cfg = $self->{cfg}; |
127
|
|
|
|
|
|
|
my @primary_fill = rgb2ary( $cfg->{table}{key}{primary} ); |
128
|
|
|
|
|
|
|
my @foreign_fill = rgb2ary( $cfg->{table}{key}{foreign} ); |
129
|
|
|
|
|
|
|
my $ew = $cfg->{table}{edge}{width}; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
for my $t ( $self->{ars}->tables ) { |
132
|
|
|
|
|
|
|
my $n = $t->name; |
133
|
|
|
|
|
|
|
for my $fk ($t->all_foreign_keys) { |
134
|
|
|
|
|
|
|
if ($n eq $fk->table_from->name) { |
135
|
|
|
|
|
|
|
my $tn = $fk->table_to->name; |
136
|
|
|
|
|
|
|
for my $c_pair ($fk->column_pairs) { |
137
|
|
|
|
|
|
|
# $c_from = Alzabo::Display::SWF::Column Object |
138
|
|
|
|
|
|
|
my $c_from = $tm{$n}->{column_by_name}{$c_pair->[0]->name}; |
139
|
|
|
|
|
|
|
$c_from->{is_foreign_key} = 1; |
140
|
|
|
|
|
|
|
my $cn = $c_pair->[1]->name; |
141
|
|
|
|
|
|
|
# $c_to = Alzabo::Display::SWF::Column Object |
142
|
|
|
|
|
|
|
my $c_to = $tm{$tn}->{column_by_name}{$cn}; |
143
|
|
|
|
|
|
|
push @{ $c_from->{foreign_keys} }, { |
144
|
|
|
|
|
|
|
name => $cn, |
145
|
|
|
|
|
|
|
xy => [ |
146
|
|
|
|
|
|
|
$tm{$tn}->{dx} - $tm{$n}->{dx}, |
147
|
|
|
|
|
|
|
$tm{$tn}->{dy} - $tm{$n}->{dy} + $c_to->{dy} - $c_from->{dy} |
148
|
|
|
|
|
|
|
] |
149
|
|
|
|
|
|
|
}; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
else { |
153
|
|
|
|
|
|
|
for my $c ($fk->columns_to) { |
154
|
|
|
|
|
|
|
my $co = $tm{$n}->{column_by_name}{$c->name}; |
155
|
|
|
|
|
|
|
$co->{is_foreign_key} = 1; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
for my $c ($t->columns) { |
160
|
|
|
|
|
|
|
my $co = $tm{$n}->{column_by_name}{$c->name}; |
161
|
|
|
|
|
|
|
$co->{is_primary_key} = 1 if $c->is_primary_key; |
162
|
|
|
|
|
|
|
my $cs = new Alzabo::Display::SWF::Key qw/r 6 segments 4/, |
163
|
|
|
|
|
|
|
name => $n.$c->name, |
164
|
|
|
|
|
|
|
linestyle_up => [ $scale*$ew, |
165
|
|
|
|
|
|
|
rgb2ary( $cfg->{table}{linestyle}{color} ) ], |
166
|
|
|
|
|
|
|
linestyle_over => [ $scale*$ew, |
167
|
|
|
|
|
|
|
rgb2ary( $cfg->{column}{color}{fg}{over} ) ], |
168
|
|
|
|
|
|
|
opac => hex( $cfg->{table}{edge}{opacity} ); |
169
|
|
|
|
|
|
|
if ( $co->{is_primary_key} ) { |
170
|
|
|
|
|
|
|
if ( $co->{is_foreign_key} ) { |
171
|
|
|
|
|
|
|
$cs->{fill2} = [ @primary_fill ]; |
172
|
|
|
|
|
|
|
$cs->{fill1} = [ @foreign_fill ]; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
else { $cs->{fill1} = [ @primary_fill ] } |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
else { |
177
|
|
|
|
|
|
|
if ( $co->{is_foreign_key} ) { |
178
|
|
|
|
|
|
|
$cs->{fill1} = [ @foreign_fill ]; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
my $k; |
182
|
|
|
|
|
|
|
if ( $co->{is_primary_key} or $co->{is_foreign_key} ) { |
183
|
|
|
|
|
|
|
$k = $cs->indicator( $co->{foreign_keys} ); |
184
|
|
|
|
|
|
|
my $ki = $m->add($k); |
185
|
|
|
|
|
|
|
push @{ $tm{$n}->{keys} }, $ki; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
else { push @{ $tm{$n}->{keys} }, undef } |
188
|
|
|
|
|
|
|
my $ci = $m->add($co->{sprite}); |
189
|
|
|
|
|
|
|
push @{ $tm{$n}->{columns} }, $ci; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
$tm{$n}->moveTo($tm{$n}->{dx}, $tm{$n}->{dy}); |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# EDGES BETWEEN TABLES |
196
|
|
|
|
|
|
|
my @xy = /(\w+)\s-[->]\s(\w+)\s.* |
197
|
|
|
|
|
|
|
pos="s,(\d+),(\d+) \s |
198
|
|
|
|
|
|
|
e,(\d+),(\d+) \s |
199
|
|
|
|
|
|
|
\d+,\d+ \s [^"]*\d+,\d+ |
200
|
|
|
|
|
|
|
"/xg; |
201
|
|
|
|
|
|
|
my @edge; |
202
|
|
|
|
|
|
|
for my $i ( map $_*6, 0 .. scalar(@xy)/6 - 1 ) { |
203
|
|
|
|
|
|
|
local $_; |
204
|
|
|
|
|
|
|
my ($t1, $t2) = map $tm{$xy[$_]}, $i, $i+1; |
205
|
|
|
|
|
|
|
my ($x1, $y1, $x2, $y2) = map $xy[$_], $i+2 .. $i+5; |
206
|
|
|
|
|
|
|
$y1 = $y - $y1; |
207
|
|
|
|
|
|
|
$y2 = $y - $y2; |
208
|
|
|
|
|
|
|
($x1, $y1) = get_coordinates($t1, $x1, $y1); |
209
|
|
|
|
|
|
|
($x2, $y2) = get_coordinates($t2, $x2, $y2); |
210
|
|
|
|
|
|
|
$y1 += 6; $y2 += 6; |
211
|
|
|
|
|
|
|
my $edge = new SWF::Shape; |
212
|
|
|
|
|
|
|
$edge->setLineStyle( $cfg->{schema}{edge}{width} * $scale, |
213
|
|
|
|
|
|
|
rgb2ary( $cfg->{schema}{color}{fg} ), |
214
|
|
|
|
|
|
|
hex( $cfg->{schema}{edge}{opacity} ) ); |
215
|
|
|
|
|
|
|
$edge->movePenTo($x1, $y1); |
216
|
|
|
|
|
|
|
if ( $xy[$i] eq $xy[$i+1] ) { |
217
|
|
|
|
|
|
|
my ($xc, $yc); |
218
|
|
|
|
|
|
|
if ( $x1 == $x2 ) { |
219
|
|
|
|
|
|
|
local $_ = int( ($y2 - $y1)/2 ); |
220
|
|
|
|
|
|
|
$yc = $y1 + $_; |
221
|
|
|
|
|
|
|
$xc = $x1 > $t1->{dx} ? $x1 + abs : $x1 - abs; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
else { |
224
|
|
|
|
|
|
|
local $_ = int( ($x2 - $x1)/2 ); |
225
|
|
|
|
|
|
|
$xc = $x1 + $_; |
226
|
|
|
|
|
|
|
$yc = $y1 > $t1->{dy} ? $y1 + abs : $y1 - abs; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
$edge->drawCurveTo( $xc, $yc, $x2, $y2 ); |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
else { $edge->drawLineTo($x2, $y2) } |
231
|
|
|
|
|
|
|
push @edge, $edge; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
my $f = new SWF::Font $cfg->{fdb_dir} . '/' . $cfg->{schema}{fdb} . '.fdb'; |
235
|
|
|
|
|
|
|
my @c_fg = rgb2ary( $cfg->{schema}{color}{fg} ); |
236
|
|
|
|
|
|
|
my @c_bg = rgb2ary( $cfg->{schema}{color}{bg} ); |
237
|
|
|
|
|
|
|
my $sn = $self->{ars}->name; |
238
|
|
|
|
|
|
|
my $t = new Alzabo::Display::SWF::Text $sn, $f, @c_fg; |
239
|
|
|
|
|
|
|
my $w = $t->getStringWidth($sn); |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
my $bs1 = button_shape($w, @c_bg); |
242
|
|
|
|
|
|
|
my $bs2 = button_shape($w, @c_bg); |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
my $b = new SWF::Button; |
245
|
|
|
|
|
|
|
$b->addShape($bs1, SWF::Button::SWFBUTTON_HIT); |
246
|
|
|
|
|
|
|
$b->addShape($bs1, SWF::Button::SWFBUTTON_UP); |
247
|
|
|
|
|
|
|
$b->addShape($bs2, SWF::Button::SWFBUTTON_OVER); |
248
|
|
|
|
|
|
|
$b->setAction( |
249
|
|
|
|
|
|
|
new SWF::Action("play();"), |
250
|
|
|
|
|
|
|
SWF::Button::SWFBUTTON_MOUSEDOWN |
251
|
|
|
|
|
|
|
); |
252
|
|
|
|
|
|
|
my $bi = $m->add($b); |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
my @lp = /graph \[.*lp="(\d+),(\d+)".*\];/ or die; |
255
|
|
|
|
|
|
|
$bi->moveTo($lp[0], $y - $lp[1]); |
256
|
|
|
|
|
|
|
my $ti = $m->add($t); |
257
|
|
|
|
|
|
|
$ti->moveTo($lp[0] + 1, $y - $lp[1] + 11); |
258
|
|
|
|
|
|
|
$m->nextFrame; |
259
|
|
|
|
|
|
|
$m->add(new SWF::Action("stop();")); |
260
|
|
|
|
|
|
|
$m->nextFrame; |
261
|
|
|
|
|
|
|
for my $edge (@edge) { $m->add($edge) } |
262
|
|
|
|
|
|
|
$m->nextFrame(); |
263
|
|
|
|
|
|
|
$m->add(new SWF::Action("stop();")); |
264
|
|
|
|
|
|
|
$m->nextFrame(); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub save { |
268
|
|
|
|
|
|
|
my ($self, $file) = @_; |
269
|
|
|
|
|
|
|
$self->{mov}->save( $file ); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub dim { ($_[0]->{X}, $_[0]->{Y}) } |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
1; |