/usr/local/CPAN/UR/UR/Object/Umlet/Relation.pm
use strict;
use warnings;
package UR::Object::Umlet::Relation;
use UR;
use IO::File;
use XML::Simple;
UR::Object::Type->define(
class_name => __PACKAGE__,
is => 'UR::Object::Umlet::PictureElement',
has => [
width => { type => 'Integer' },
height => { type => 'Integer'},
relation_type => { type => 'String' },
head_multiplicity => { type => 'String', is_optional => 1 },
tail_multiplicity => { type => 'String', is_optional => 1 },
head_qualifier => { type => 'String', is_optional => 1 },
tail_qualifier => { type => 'String', is_optional => 1 },
head_role => { type => 'String', is_optional => 1 },
tail_role => { type => 'String', is_optional => 1 },
head_x_offset => { type => 'Integer' },
head_y_offset => { type => 'Integer' },
tail_x_offset => { type => 'Integer' },
tail_y_offset => { type => 'Integer' },
waypoints => { type => 'Object', is_optional => 1 },
from_entity_name => { type => 'String' },
to_entity_name => { type => 'String' },
from_attribute_name => { type => 'String', is_optional => 1},
to_attribute_name => { type => 'String', is_optional => 1},
],
doc => 'Represents one of the arrow-type elements of an umlet diagram',
);
sub create {
my($class, %params) = @_;
unless (exists $params{'subject_id'}) {
no warnings;
$params{'subject_id'} = join(';', @params{'from_entity_name','to_entity_name','from_attribute_name','to_attribute_name'});
}
return $class->SUPER::create(%params);
}
sub umlet_type_string { return 'com.umlet.element.base.Relation' };
sub create_from_element {
my($class,%params) = @_;
my $addl = delete $params{'element'}->{'additional_attributes'};
my %addl = $class->_parse_additional_attributes($addl);
my $self = $class->SUPER::create_from_element(%params, %addl);
return $self;
}
sub _fixup_panel_attributes {
my($class, %attributes) = @_;
%attributes = $class->SUPER::_fixup_panel_attributes(%attributes);
$attributes{'relation_type'} = delete $attributes{'lt'};
$attributes{'head_multiplicity'} = delete $attributes{'m1'};
$attributes{'tail_multiplicity'} = delete $attributes{'m2'};
$attributes{'head_qualifier'} = delete $attributes{'q1'};
$attributes{'tail_qualifier'} = delete $attributes{'q2'};
$attributes{'head_role'} = delete $attributes{'r1'};
$attributes{'tail_role'} = delete $attributes{'r2'};
@attributes{'from_entity_name','to_entity_name','from_attribute_name','to_attribute_name'} = split(';',$attributes{'subject_id'});
return %attributes;
}
sub _parse_additional_attributes {
my($class,$string) = @_;
my @points = split(';', $string);
my %retval;
# The format is head_x ; head_y ; waypoint_x; waypoint_y; tail_x; tail_y
# And you can have 0 or more waypoints
$retval{'head_x_offset'} = shift @points;
$retval{'head_y_offset'} = shift @points;
$retval{'tail_y_offset'} = pop @points;
$retval{'tail_x_offset'} = pop @points;
$retval{'waypoints'} = \@points;
return %retval;
}
our %attr_map = ( head_multiplicity => 'm1', tail_multiplicity => 'm2',
head_qualifier => 'q1', tail_qualifier => 'q1',
head_role => 'r1', tail_role => 'r2',
);
sub panel_attributes {
my($self) = @_;
my $string = '';
if ($self->label) {
$string .= $self->label."\n";
}
$string .= "lt=" . $self->relation_type . "\n";
foreach my $key ( keys %attr_map ) {
my $value = $self->$key();
if (defined $value) {
my $attr = $attr_map{$key};
$string .= "$attr=$value\n";
}
}
my $id_string = join(';', $self->from_entity_name, $self->to_entity_name,
$self->from_attribute_name || '', $self->to_attribute_name || '');
$string .= "//subject_id:$id_string\n";
$string = $self->escape_xml_data($string);
return "<panel_attributes>$string</panel_attributes>\n";;
}
sub additional_attributes {
my($self) = @_;
my @points = ($self->head_x_offset, $self->head_y_offset);
if ($self->waypoints && scalar @{ $self->waypoints } ) {
push @points, @{ $self->waypoints };
}
push @points, ($self->tail_x_offset, $self->tail_y_offset);
return "<additional_attributes>" . join(';', @points) . "</additional_attributes>\n";
}
sub connect_entities {
my $self = shift;
my $from_entity = UR::Object::Umlet::Class->get(diagram_name => $self->diagram_name,
subject_id => $self->from_entity_name);
my $to_entity = UR::Object::Umlet::Class->get(diagram_name => $self->diagram_name,
subject_id => $self->to_entity_name);
return unless ($from_entity and $to_entity);
# The coords for both ends of the arrow
my($head_x_coord, $head_y_coord, $tail_x_coord, $tail_y_coord);
if ($from_entity->y > $to_entity->y) {
$tail_y_coord = $from_entity->y + $from_entity->height;
$head_y_coord = $to_entity->y;
} else {
$tail_y_coord = $from_entity->y;
$head_y_coord = $to_entity->y + $to_entity->height;
}
$tail_x_coord = $from_entity->x + int($from_entity->width / 2);
$head_x_coord = $to_entity->x + int($to_entity->width / 2);
my $x = ( $head_x_coord < $tail_x_coord ? $head_x_coord : $tail_x_coord ) - 20;
my $y = ( $head_y_coord < $tail_y_coord ? $head_y_coord : $tail_y_coord ) - 20;
my $width = abs($head_x_coord - $tail_x_coord) + 40;
my $height = abs($head_y_coord - $tail_y_coord) + 40;
my $head_x_offset = $head_x_coord - $x;
my $head_y_offset = $head_y_coord - $y;
my $tail_x_offset = $tail_x_coord - $x;
my $tail_y_offset = $tail_y_coord - $y;
$self->x($x);
$self->y($y);
$self->width($width);
$self->height($height);
$self->head_x_offset($head_x_offset);
$self->head_y_offset($head_y_offset);
$self->tail_x_offset($tail_x_offset);
$self->tail_y_offset($tail_y_offset);
return 1;
}
sub connect_entity_attributes {
my $self = shift;
my $from_entity = UR::Object::Umlet::Class->get(diagram_name => $self->diagram_name,
subject_id => $self->from_entity_name);
my $to_entity = UR::Object::Umlet::Class->get(diagram_name => $self->diagram_name,
subject_id => $self->to_entity_name);
return unless ($from_entity and $to_entity);
my $from_attribute_pos = 0;
foreach my $attr ( @{ $from_entity->attributes } ) {
if ($attr->{'name'} eq $self->from_attribute_name) {
$from_attribute_pos = ($attr->{'line'}) * 18;
last;
}
}
my $to_attribute_pos = 0;
foreach my $attr ( @{ $to_entity->attributes } ) {
if ($attr->{'name'} eq $self->to_attribute_name) {
$to_attribute_pos = ($attr->{'line'}) * 18;
last;
}
}
# The coords for both ends of the arrow
my($head_x_coord, $head_y_coord, $tail_x_coord, $tail_y_coord);
$tail_y_coord = $from_entity->y + $from_attribute_pos;
$head_y_coord = $to_entity->y + $to_attribute_pos;
if ($from_entity->x < $to_entity->x) {
$tail_x_coord = $from_entity->x + $from_entity->width;
$head_x_coord = $to_entity->x;
} elsif ($from_entity->x > $to_entity->x) {
$tail_x_coord = $from_entity->x;
$head_x_coord = $to_entity->x + $to_entity->width;
} else {
$tail_x_coord = $from_entity->x;
$head_x_coord = $to_entity->x;
}
my $x = ( $head_x_coord < $tail_x_coord ? $head_x_coord : $tail_x_coord ) - 20;
my $y = ( $head_y_coord < $tail_y_coord ? $head_y_coord : $tail_y_coord ) - 20;
my $width = abs($head_x_coord - $tail_x_coord) + 40;
my $height = abs($head_y_coord - $tail_y_coord) + 40;
my $head_x_offset = $head_x_coord - $x;
my $head_y_offset = $head_y_coord - $y;
my $tail_x_offset = $tail_x_coord - $x;
my $tail_y_offset = $tail_y_coord - $y;
$self->x($x);
$self->y($y);
$self->width($width);
$self->height($height);
$self->head_x_offset($head_x_offset);
$self->head_y_offset($head_y_offset);
$self->tail_x_offset($tail_x_offset);
$self->tail_y_offset($tail_y_offset);
return 1;
}
1;