line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
package UR::Namespace::Command::Update::ClassDiagram; |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
23
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
7
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
30
|
|
8
|
1
|
|
|
1
|
|
4
|
use UR; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5
|
|
9
|
|
|
|
|
|
|
our $VERSION = "0.46"; # UR $VERSION; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
UR::Object::Type->define( |
12
|
|
|
|
|
|
|
class_name => __PACKAGE__, |
13
|
|
|
|
|
|
|
is => 'UR::Namespace::Command::Base', |
14
|
|
|
|
|
|
|
has => [ |
15
|
|
|
|
|
|
|
data_source => {type => 'String', doc => 'Which datasource to use', is_optional => 1}, |
16
|
|
|
|
|
|
|
depth => { type => 'Integer', doc => 'Max distance of related classes to include. Default is 1. 0 means show only the named class(es), -1 means to include everything', is_optional => 1}, |
17
|
|
|
|
|
|
|
file => { type => 'String', doc => 'Pathname of the Umlet (.uxf) file' }, |
18
|
|
|
|
|
|
|
show_attributes => { type => 'Boolean', is_optional => 1, default => 1, doc => 'Include class attributes in the diagram' }, |
19
|
|
|
|
|
|
|
show_methods => { type => 'Boolean', is_optional => 1, default => 0, doc => 'Include methods in the diagram (not implemented yet' }, |
20
|
|
|
|
|
|
|
include_ur_object => { type => 'Boolean', is_optional => 1, default => 0, doc => 'Include UR::Object and UR::Entity in the diagram (default = no)' }, |
21
|
|
|
|
|
|
|
initial_name => { |
22
|
|
|
|
|
|
|
is_many => 1, |
23
|
|
|
|
|
|
|
is_optional => 1, |
24
|
|
|
|
|
|
|
shell_args_position => 1 |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
], |
27
|
|
|
|
|
|
|
); |
28
|
|
|
|
|
|
|
|
29
|
0
|
|
|
0
|
0
|
|
sub sub_command_sort_position { 4 }; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub help_brief { |
32
|
0
|
|
|
0
|
0
|
|
"Update an Umlet diagram based on the current class definitions" |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub help_detail { |
36
|
0
|
|
|
0
|
0
|
|
return <
|
37
|
|
|
|
|
|
|
Creates a new Umlet diagram, or updates an existing diagram. Bare arguments |
38
|
|
|
|
|
|
|
are taken as class names to include in the diagram. Other classes may be |
39
|
|
|
|
|
|
|
included in the diagram based on their distance from the names classes |
40
|
|
|
|
|
|
|
and the --depth parameter. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
If an existing file is being updated, the position of existing elements |
43
|
|
|
|
|
|
|
will not change. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
EOS |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# The max X coord to use when placing boxes. After this, move down a line and go back to the left |
49
|
1
|
|
|
1
|
|
4
|
use constant MAX_X_AUTO_POSITION => 800; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1177
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub execute { |
52
|
0
|
|
|
0
|
|
|
my $self = shift; |
53
|
|
|
|
|
|
|
|
54
|
0
|
|
|
|
|
|
my $params = shift; |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
#$DB::single = 1; |
57
|
0
|
|
|
|
|
|
my $namespace = $self->namespace_name; |
58
|
0
|
|
|
|
|
|
eval "use $namespace"; |
59
|
0
|
0
|
|
|
|
|
if ($@) { |
60
|
0
|
|
|
|
|
|
$self->error_message("Failed to load module for $namespace: $@"); |
61
|
0
|
|
|
|
|
|
return; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
0
|
|
|
|
|
|
my @initial_name_list = $self->initial_name; |
65
|
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
my $diagram; |
67
|
0
|
0
|
|
|
|
|
if (-f $params->{'file'}) { |
68
|
0
|
0
|
|
|
|
|
$params->{'depth'} = 0 unless (exists $params->{'depth'}); # Default is just update what's there |
69
|
0
|
|
|
|
|
|
$diagram = UR::Object::Umlet::Diagram->create_from_file($params->{'file'}); |
70
|
0
|
|
|
|
|
|
push @initial_name_list, map { $_->subject_id } UR::Object::Umlet::Class->get(diagram_name => $diagram->name); |
|
0
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
} else { |
72
|
0
|
0
|
|
|
|
|
$params->{'depth'} = 1 unless exists($params->{'depth'}); |
73
|
0
|
|
|
|
|
|
$diagram = UR::Object::Umlet::Diagram->create(name => $params->{'file'}); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# FIXME this can get removed when attribute defaults work correctly |
77
|
0
|
0
|
|
|
|
|
unless (exists $params->{'show_attributes'}) { |
78
|
0
|
|
|
|
|
|
$self->show_attributes(1); |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
0
|
|
|
|
|
|
my @involved_classes; |
82
|
0
|
|
|
|
|
|
foreach my $class_name ( @initial_name_list ) { |
83
|
0
|
|
|
|
|
|
push @involved_classes, UR::Object::Type->get(class_name => $class_name); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
push @involved_classes, $self->_get_related_classes_via_inheritance( |
87
|
|
|
|
|
|
|
names => \@initial_name_list, |
88
|
0
|
|
|
|
|
|
depth => $params->{'depth'}, |
89
|
|
|
|
|
|
|
); |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
push @involved_classes, $self->_get_related_classes_via_properties( |
92
|
|
|
|
|
|
|
#names => [ map { $_->class_name } @involved_classes ], |
93
|
|
|
|
|
|
|
names => \@initial_name_list, |
94
|
0
|
|
|
|
|
|
depth => $params->{'depth'}, |
95
|
|
|
|
|
|
|
); |
96
|
0
|
|
|
|
|
|
my %involved_class_names = map { $_->class_name => $_ } @involved_classes; |
|
0
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# The initial placement, and how much to move over for the next box |
99
|
0
|
|
|
|
|
|
my($x_coord, $y_coord, $x_inc, $y_inc) = (20,20,40,40); |
100
|
0
|
0
|
|
|
|
|
my @objs = sort { $b->y <=> $a->y or $b->x <=> $a->x } UR::Object::Umlet::Class->get(); |
|
0
|
|
|
|
|
|
|
101
|
0
|
0
|
|
|
|
|
if (@objs) { |
102
|
0
|
|
|
|
|
|
my $maxobj = $objs[0]; |
103
|
0
|
|
|
|
|
|
$x_coord = $maxobj->x + $maxobj->width + $x_inc; |
104
|
0
|
|
|
|
|
|
$y_coord = $maxobj->y + $maxobj->height + $y_inc; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# First, place all the classes |
109
|
0
|
|
|
|
|
|
my @all_boxes = UR::Object::Umlet::Class->get( diagram_name => $diagram->name ); |
110
|
0
|
|
|
|
|
|
foreach my $class ( values %involved_class_names ) { |
111
|
0
|
|
|
|
|
|
my $umlet_class = UR::Object::Umlet::Class->get(diagram_name => $diagram->name, |
112
|
|
|
|
|
|
|
subject_id => $class->class_name); |
113
|
0
|
|
|
|
|
|
my $created = 0; |
114
|
0
|
0
|
|
|
|
|
unless ($umlet_class) { |
115
|
0
|
|
|
|
|
|
$created = 1; |
116
|
0
|
|
|
|
|
|
$umlet_class = UR::Object::Umlet::Class->create( diagram_name => $diagram->name, |
117
|
|
|
|
|
|
|
subject_id => $class->class_name, |
118
|
|
|
|
|
|
|
label => $class->class_name, |
119
|
|
|
|
|
|
|
x => $x_coord, |
120
|
|
|
|
|
|
|
y => $y_coord, |
121
|
|
|
|
|
|
|
); |
122
|
|
|
|
|
|
|
# add the attributes |
123
|
0
|
0
|
|
|
|
|
if ($self->show_attributes) { |
124
|
0
|
|
0
|
|
|
|
my $attributes = $umlet_class->attributes || []; |
125
|
0
|
|
|
|
|
|
my %attributes_already_in_diagram = map { $_->{'name'} => 1 } @{ $attributes }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
|
my %id_properties = map { $_ => 1 } $class->id_property_names; |
|
0
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
|
128
|
0
|
|
|
|
|
|
my $line_count = scalar @$attributes; |
129
|
0
|
|
|
|
|
|
foreach my $property_name ( $class->direct_property_names ) { |
130
|
0
|
0
|
|
|
|
|
next if $attributes_already_in_diagram{$property_name}; |
131
|
0
|
|
|
|
|
|
$line_count++; |
132
|
0
|
|
|
|
|
|
my $property = UR::Object::Property->get(class_name => $class->class_name, property_name => $property_name); |
133
|
0
|
0
|
|
|
|
|
push @$attributes, { is_id => $id_properties{$property_name} ? '+' : ' ', |
134
|
|
|
|
|
|
|
name => $property_name, |
135
|
|
|
|
|
|
|
type => $property->data_type, |
136
|
|
|
|
|
|
|
line => $line_count, |
137
|
|
|
|
|
|
|
}; |
138
|
|
|
|
|
|
|
} |
139
|
0
|
|
|
|
|
|
$umlet_class->attributes($attributes); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
0
|
0
|
|
|
|
|
if ($self->show_methods) { |
143
|
|
|
|
|
|
|
# Not implemented yet |
144
|
|
|
|
|
|
|
# Use the same module the schemabrowser uses to get that info |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# Make sure this box dosen't overlap other boxes |
148
|
0
|
|
|
|
|
|
while(my $overlapped = $umlet_class->is_overlapping(@all_boxes) ) { |
149
|
0
|
0
|
|
|
|
|
if ($umlet_class->x > MAX_X_AUTO_POSITION) { |
150
|
0
|
|
|
|
|
|
$umlet_class->x(20); |
151
|
0
|
|
|
|
|
|
$umlet_class->y( $umlet_class->y + $y_inc); |
152
|
|
|
|
|
|
|
} else { |
153
|
0
|
|
|
|
|
|
$umlet_class->x( $overlapped->x + $overlapped->width + $x_inc ); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
0
|
|
|
|
|
|
push @all_boxes, $umlet_class; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
0
|
0
|
|
|
|
|
if ($created) { |
161
|
0
|
|
|
|
|
|
$x_coord = $umlet_class->x + $umlet_class->width + $x_inc; |
162
|
0
|
0
|
|
|
|
|
if ($x_coord > MAX_X_AUTO_POSITION) { |
163
|
0
|
|
|
|
|
|
$x_coord = 20; |
164
|
0
|
|
|
|
|
|
$y_coord += $y_inc; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# Next, connect the classes together |
170
|
0
|
|
|
|
|
|
foreach my $class ( values %involved_class_names ) { |
171
|
0
|
0
|
|
|
|
|
my @properties = grep { $_->is_delegated and $_->data_type} $class->all_property_metas(); |
|
0
|
|
|
|
|
|
|
172
|
0
|
|
|
|
|
|
foreach my $property ( @properties ) { |
173
|
|
|
|
|
|
|
|
174
|
0
|
0
|
|
|
|
|
next unless (exists $involved_class_names{$property->data_type}); |
175
|
|
|
|
|
|
|
|
176
|
0
|
|
|
|
|
|
my @property_links = eval { $property->get_property_name_pairs_for_join }; |
|
0
|
|
|
|
|
|
|
177
|
0
|
0
|
|
|
|
|
next unless @property_links; |
178
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
my $id_by = join(':', map { $_->[0] } @property_links); |
|
0
|
|
|
|
|
|
|
180
|
0
|
|
|
|
|
|
my $their_id_by = join (':', map { $_->[1] } @property_links); |
|
0
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
|
182
|
0
|
|
|
|
|
|
my $umlet_relation = UR::Object::Umlet::Relation->get( diagram_name => $diagram->name, |
183
|
|
|
|
|
|
|
from_entity_name => $property->class_name, |
184
|
|
|
|
|
|
|
to_entity_name => $property->data_type, |
185
|
|
|
|
|
|
|
from_attribute_name => $id_by, |
186
|
|
|
|
|
|
|
to_attribute_name => $their_id_by, |
187
|
|
|
|
|
|
|
); |
188
|
0
|
0
|
|
|
|
|
unless ($umlet_relation) { |
189
|
0
|
|
|
|
|
|
$umlet_relation = UR::Object::Umlet::Relation->create( diagram_name => $diagram->name, |
190
|
|
|
|
|
|
|
relation_type => '<-', |
191
|
|
|
|
|
|
|
from_entity_name => $property->class_name, |
192
|
|
|
|
|
|
|
to_entity_name => $property->data_type, |
193
|
|
|
|
|
|
|
from_attribute_name => $id_by, |
194
|
|
|
|
|
|
|
to_attribute_name => $their_id_by, |
195
|
|
|
|
|
|
|
); |
196
|
0
|
0
|
|
|
|
|
unless ($umlet_relation->connect_entity_attributes()) { |
197
|
|
|
|
|
|
|
# This didn't link to anything on the diagram |
198
|
0
|
|
|
|
|
|
$umlet_relation->delete; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
0
|
|
|
|
|
|
foreach my $parent_class_name ( @{ $class->is } ) { |
|
0
|
|
|
|
|
|
|
205
|
0
|
0
|
|
|
|
|
next unless ($involved_class_names{$parent_class_name}); |
206
|
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
my $umlet_relation = UR::Object::Umlet::Relation->get( diagram_name => $diagram->name, |
208
|
|
|
|
|
|
|
from_entity_name => $class->class_name, |
209
|
|
|
|
|
|
|
to_entity_name => $parent_class_name, |
210
|
|
|
|
|
|
|
); |
211
|
0
|
0
|
|
|
|
|
unless ($umlet_relation) { |
212
|
0
|
|
|
|
|
|
$umlet_relation = UR::Object::Umlet::Relation->create( diagram_name => $diagram->name, |
213
|
|
|
|
|
|
|
relation_type => '<<-', |
214
|
|
|
|
|
|
|
from_entity_name => $class->class_name, |
215
|
|
|
|
|
|
|
to_entity_name => $parent_class_name, |
216
|
|
|
|
|
|
|
); |
217
|
0
|
|
|
|
|
|
$umlet_relation->connect_entities(); |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
0
|
|
|
|
|
|
$diagram->save_to_file($params->{'file'}); |
223
|
|
|
|
|
|
|
|
224
|
0
|
|
|
|
|
|
1; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub _get_related_classes_via_properties { |
230
|
0
|
|
|
0
|
|
|
my($self, %params) = @_; |
231
|
|
|
|
|
|
|
|
232
|
0
|
0
|
|
|
|
|
return unless (@{$params{'names'}}); |
|
0
|
|
|
|
|
|
|
233
|
0
|
0
|
|
|
|
|
return unless $params{'depth'}; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Make sure the named classes are loaded |
236
|
0
|
|
|
|
|
|
foreach ( @{ $params{'names'} } ) { |
|
0
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
eval { $_->class }; |
|
0
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# Get everything linked to the named things |
241
|
0
|
|
|
|
|
|
my @related_names = grep { eval { $_->class } } |
|
0
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
#grep { $_ } |
243
|
0
|
|
|
|
|
|
map { $_->data_type } |
244
|
0
|
|
|
|
|
|
map { UR::Object::Property->get(class_name => $_ ) } |
245
|
0
|
|
|
|
|
|
@{ $params{'names'}}; |
|
0
|
|
|
|
|
|
|
246
|
0
|
|
|
|
|
|
push @related_names, grep { eval { $_->class } } |
|
0
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
#grep { $_ } |
248
|
0
|
|
|
|
|
|
map { $_->class_name } |
249
|
0
|
|
|
|
|
|
map { UR::Object::Property->get(data_type => $_ ) } |
250
|
0
|
|
|
|
|
|
@{ $params{'names'}}; |
|
0
|
|
|
|
|
|
|
251
|
0
|
0
|
|
|
|
|
return unless @related_names; |
252
|
|
|
|
|
|
|
|
253
|
0
|
|
|
|
|
|
my @objs = map { UR::Object::Type->get(class_name => $_) } @related_names; |
|
0
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
#my @related_names = grep { $_ } map { $_->$related_param } $related_class->get($item_param => $params{'names'}); |
256
|
|
|
|
|
|
|
#push @related_names, grep { $_ } map { $_->$item_param } $related_class->get($related_param => $params{'names'}); |
257
|
|
|
|
|
|
|
#return unless @related_names; |
258
|
|
|
|
|
|
|
# |
259
|
|
|
|
|
|
|
# my @objs = $item_class->get($item_param => \@related_names); |
260
|
|
|
|
|
|
|
|
261
|
0
|
0
|
|
|
|
|
unless ($self->include_ur_object) { |
262
|
|
|
|
|
|
|
# Prune out UR::Object and UR::Entity |
263
|
0
|
0
|
|
|
|
|
@objs = grep { $_->class_name ne 'UR::Object' and $_->class_name ne 'UR::Entity' } @objs; |
|
0
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# make a recursive call to get the related objects by name |
267
|
0
|
|
|
|
|
|
return ( @objs, $self->_get_related_classes_via_properties( %params, names => \@related_names, depth => --$params{'depth'}) ); |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub _get_related_classes_via_inheritance { |
271
|
0
|
|
|
0
|
|
|
my($self,%params) = @_; |
272
|
|
|
|
|
|
|
|
273
|
0
|
0
|
|
|
|
|
return unless (@{$params{'names'}}); |
|
0
|
|
|
|
|
|
|
274
|
0
|
0
|
|
|
|
|
return unless $params{'depth'}; |
275
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
my @related_class_names; |
277
|
0
|
|
|
|
|
|
foreach my $class_name ( @{ $params{'names'} } ) { |
|
0
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# get the class loaded |
279
|
0
|
|
|
|
|
|
eval { $class_name->class }; |
|
0
|
|
|
|
|
|
|
280
|
0
|
0
|
|
|
|
|
if ($@) { |
281
|
0
|
|
|
|
|
|
$self->warning_message("Problem loading class $class_name: $@"); |
282
|
0
|
|
|
|
|
|
next; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# Get this class' parents |
286
|
|
|
|
|
|
|
#push @related_class_names, $class_name->parent_classes; |
287
|
0
|
|
|
|
|
|
push @related_class_names, @{ $class_name->__meta__->is }; |
|
0
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
0
|
|
|
|
|
|
my @objs = map { $_->__meta__ } @related_class_names; |
|
0
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
|
292
|
0
|
0
|
|
|
|
|
unless ($self->include_ur_object) { |
293
|
|
|
|
|
|
|
# Prune out UR::Object and UR::Entity |
294
|
0
|
0
|
|
|
|
|
@objs = grep { $_->class_name ne 'UR::Object' and $_->class_name ne 'UR::Entity' } @objs; |
|
0
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# make a recursive call to get their parents |
298
|
|
|
|
|
|
|
return ( @objs, |
299
|
|
|
|
|
|
|
$self->_get_related_classes_via_inheritance( %params, |
300
|
|
|
|
|
|
|
names => \@related_class_names, |
301
|
0
|
|
|
|
|
|
depth => --$params{'depth'}, |
302
|
|
|
|
|
|
|
) |
303
|
|
|
|
|
|
|
); |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
1; |
309
|
|
|
|
|
|
|
|