| UMMF documentation | Contained in the UMMF distribution. |
UMMF::Core::Util - Utilities for querying meta-models and models.
Useful manipulations of model. These can be used for any UML meta level.
This allows other modules, like UMMF::UML::Export::*, to assume that the UML meta-model is "stupid" i.e. has no support methods other than accessors for Attributes and Associations.
Eventually these could be imported into generated models as supplimentary methods.
None exported.
Kurt Stephens, kstephens@users.sourceforge.net 2003/04/15
UMMF::Core::MetaMetaModel (UMMF::Core::MetaMetaModel)
$Revision: 1.36 $
my @names = ModelElement_name_qualified($obj); my $qname = ModelElement_name_qualified($obj, $sep); my $qname = ModelElement_name_qualified($obj, $sep, $filter);
Returns the fully-qualified name for a ModelElement.
Applies $name = $filter->($obj, $name) to each ModelElement, if $filter is defined.
In list context, returns the names of all parent namespaces.
In scalar context, joins the names of all parent namespaces with $sep
$sep defaults to '::'.
Returns the root Namespace of a ModelElement.
Returns the Namespace parent that is common to two ModelElements.
For example: if $x is in a UML Namespace "A::B" and $y is in a Namespace "A::C::D", ModelElement_namespace_common($x, $y) will return the "A" Namespace object.
Returns a list of all Namespace nodes owned by a Namespace.
Returns a list of all Classifier nodes owned by a Namespace.
Returns a list of all Class nodes owned by a Namespace.
Returns a list of all AssociationClass nodes owned by a Namespace.
Returns a list of all Interface nodes owned by a Namespace.
Returns a list of all Enumeration nodes owned by a Namespace.
Returns a list of the Generalization parents (superclasses) of a GeneralizableElement.
Returns a list of all the Generalization parents (superclasses) of a GeneralizableElement, toward the root Generalization (root baseclasses).
Returns all Attribute features.
Returns all Operation features
Returns all Method features.
Returns the return Parameter.
Returns the body text of an Expression for a specific language.
Returns the body text of an Attribute's initialValue Expression.
@other_ends = AssociationEnd_opposite($end);
Returns a list of all the AssociationEnds opposite to the AssociationEnd. Typically this list has only one AssociationEnd.
my $multiplicity = Multiplicity_fromString($str, $factory);
Creates a Multiplicity object, using factory $factory by parsing string $str.
Returns a list of new Attribute objects that are a typical representation of opposite AssociationEnds in a Class.
Returns a list of new Attribute objects that are a typical representation of the AssociationEnds in a AssociationClass.
| UMMF documentation | Contained in the UMMF distribution. |
package UMMF::Core::Util; use 5.6.0; use strict; #use warnings; # no warnings, too much hassle to make them go away. our $AUTHOR = q{ kstephens@users.sourceforge.net 2003/04/15 }; our $VERSION = do { my @r = (q$Revision: 1.36 $ =~ /\d+/g); sprintf "%d." . "%03d" x $#r, @r };
#################################################################################### use base qw(Exporter); our @EXPORT_OK = qw( ModelElement_initialize ModelElement_name_qualified ModelElement_namespace_root ModelElement_namespace_common Namespace_ownedElement_match Namespace_ownedElement_name_safe Namespace_ownedElement_name Namespace_ownedElement_name_ Namespace_lookup Namespace_namespace Namespace_classifier Namespace_class Namespace_interface Namespace_enumeration Namespace_associationClass GeneralizableElement_generalization_parent GeneralizableElement_generalization_parent_all GeneralizableElement_generalization_child GeneralizableElement_generalization_child_all Multiplicity_fromString Multiplicity_asString Multiplicity_lower Multiplicity_upper MultiplicityRange_asString Association_asString AssociationEnd_asString Attribute_asString ModelElement_taggedValue_name ModelElement_taggedValue_name_true ModelElement_taggedValue_inheritsFrom ModelElement_taggedValue_inherited ModelElement_taggedValue_inherited_true ModelElement_taggedValue_trace ModelElement_set_taggedValue_name TagDefinition_for_name Classifier_attribute Classifier_operation Classifier_method Class_Association_Attribute AssociationClass_Attribute Attribute_initialValue_language Operation_return Expression_body_language __fix_association_end_names AssociationEnd_opposite AssociationEnd_association Model_clone Attribute_clone Operation_clone Association_clone Model_destroy trimws trim_ws_undef String_toBoolean ISA_super index_array unique_proc unique unique_ref ); our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); ####################################################################### use Carp qw(confess); ####################################################################### sub trimws { my ($x) = @_; no warnings; $x =~ s/^[\s\n]+//s; $x =~ s/[\s\n]+$//s; $x; } sub trim_ws_undef { my ($x) = @_; if ( ref($x) ) { return $$x = trim_ws_undef($$x); } return undef unless defined $x; $x =~ s/^[\s\n]+//sg; $x =~ s/[\s\n]+$//sg; length $x ? $x : undef; } sub String_toBoolean { my ($x) = @_; return undef unless defined $x; $x = trimws($x); no warnings; $x ne '' && $x ne 'false' && $x ne 'no' && $x ne '0'; } sub ISA_super { my ($x) = @_; # $DB::single = 1; $x = ref($x) || $x; no strict 'refs'; my @x = ( $x ); my @c; while ( @x ) { my $x = pop @x; next if grep($_ eq $x, @c); push(@c, $x); push(@x, @{"${x}::ISA"}); } wantarray ? @c : \@c; } sub members { my ($x) = @_; $x ? (UNIVERSAL::can($x, 'members') ? $x->members() : @$x) : (); } sub index_array { my ($x, @x) = @_; for ( my $i = -1; ++ $i < @x; ) { return $i if $x[$i] eq $x; } undef; } sub identity { shift } sub unique_proc ($$) { my ($proc, $x) = @_; my %x = map(($proc->($_), $_), @$x); wantarray ? values %x : [ values %x ]; } sub unique { unique_proc(\&identity, \@_); } sub unique_ref ($) { unique_proc(\&identity, $_[0]); } ####################################################################### sub ModelElement_initialize { my ($self) = @_; my $name = $self->{'name'}; if ( length $name ) { die("Invalid ModelElement name '$name'") if $name =~ /::/; } $self->SUPER::initialize; } sub ModelElement_set_name { my ($self, $name) = @_; use Carp qw(confess); # Cannot rename. # confess("$self already named") if ( $self->{'name'} ); # Cannot be qualified name. confess("Name '$name' is qualified") if $name =~ /::/; # Must be reasonable. if ( length($name) ) { confess("Name '$name' is invalid") if $name =~ /::/; } if ( $self->{'name'} ne $name ) { # Recursion lock my $old = $self->{'name'}; $self->{'name'} = $name; # Recursion lock # Force name collision check. my $ns = $self->{'namespace'}; if ( $ns ) { my $x; if ( ($x = (grep($_->name eq $name, $ns->ownedElement))[0]) && $x ne $self ) { confess("Namespace '" . $ns->name . "' already has element '$x' named '$name' for $self"); } } } $self; } ####################################################################### sub Attribute_initialize { my ($self) = @_; # IMPLEMENT: Is this a UML 1.5 constraint?? $self->{'namespace'} = $self->{'owner'}; # $DB::single = 1 unless defined $self->{'multiplicity'}; # $DB::single = 1 unless defined $self->{'multiplicity'}; $self->{'multiplicity'} ||= 1; Multiplicity_fromString(\$self->{'multiplicity'}, UMMF::UML::MetaMetaModel->factory); die("Invalid type: $self->{type}") unless $self->{'type'}; die("Invalid name: $self->{name}") unless $self->{'name'} =~ /^[A-Za-z0-9_]+$/; $self; } ####################################################################### sub Association_initialize { my ($self) = @_; my $x = $self->{'connection'}; if ( $x ) { $x->[0]->set_namespace($x->[1]->participant) unless $x->[0]->namespace; $x->[1]->set_namespace($x->[0]->participant) unless $x->[1]->namespace; # Set the Association's namespace # to be the common Namespace of all the Participants. # This seems to be what ArgoUML is doing. unless ( $self->{'namespace'} ) { # $DB::single = 1; $self->set_namespace( ModelElement_namespace_common(map($_->namespace, @$x)) ); } } $self; } ####################################################################### sub AssociationEnd_initialize { my ($self) = @_; # IMPLEMENT: Is this a UML 1.5 constraint?? #$self->{'namespace'} ||= $self->participant->namespace; # $DB::single = 1 unless defined $self->{'multiplicity'}; $self->{'multiplicity'} ||= 1; Multiplicity_fromString(\$self->{'multiplicity'}, UMMF::UML::MetaMetaModel->factory); $self; } ####################################################################### sub ModelElement_name_anon { my ($node) = @_; my $name = $node->name; # Handle anonymous model elements. unless ( length($name) ) { # Use the position of ModelElement in it's Namespace's ownedElement # Association, even though that AssociationEnd is not ordered. if ( $node->namespace ) { my $i = 0; for my $x ( $node->namespace->ownedElement ) { if ( $x eq $node ) { $name = "__Anon__$i"; last; } $i ++; } } # Fall back on the memory address of the ModelElement. unless ( length($name) ) { $node =~ /0x([0-9a-f]+)/i; $name = $1 ? "__Anon__$1" : "__Anon_IGiveUp"; } } $name; }
sub ModelElement_name_qualified { my ($node, $sep, $filter) = @_; my @x; confess("$node is not ref") unless ref($node); # # Cull the Model's name (Model's are root namespaces, ie. Model->namespace = nil) # Model names are not usually intended for # scoping ModelElements when generating code. # if ( $node->namespace ) { @x = ModelElement_name_qualified($node->namespace, undef, $filter); { my $name = ModelElement_name_anon($node); $name = $filter->($node, $name) if $filter; push(@x, $name); } } # $DB::single = 1; wantarray ? @x : join($sep || '::', @x); }
sub ModelElement_namespace_root { my ($ns) = @_; while ( my $x = $ns ? $ns->namespace : confess("ns") ) { $ns = $x; } $ns; }
sub ModelElement_namespace_common { my ($me1, $me2, @other) = @_; my $x = $me1 || $me2; X: while ( $x ) { my $y = $me2 || confess("me2"); while ( $y ) { last X if $y eq $x; $y = $y->namespace; } $x = $x->namespace; } if ( @other ) { $x = ModelElement_namespace_common($x, @other); } $x; } our $namespace_trace = 0; sub Namespace_ownedElement_match { my ($ns, $match, $recur, $limit, $ns_too) = @_; unless ( ref($match) ) { my $meth = $match; $match = sub { $_[0]->$meth }; } if ( $namespace_trace ) { print STDERR "N_oE_m ", scalar ModelElement_name_qualified($ns), " : $namespace_trace :\n"; } my @x; my $oE = $ns->ownedElement; push(@x, grep($match->($_), $ns)); if ( $recur ) { for my $x ( @$oE ) { confess("BAAAAAH $x") if ref($x) eq 'main'; if ( $x->isaNamespace ) { if ( $ns_too ) { push(@x, grep($match->($x), $x)); last if $limit && @x >= $limit; } push(@x, Namespace_ownedElement_match($x, $match, $recur, $limit, $ns_too)); last if $limit && @x >= $limit; } else { push(@x, grep($match->($x), $x)); } } } else { push(@x, grep($match->($_), @$oE)); } if ( $namespace_trace ) { local $" = ', '; print STDERR "N_oE_m ", scalar ModelElement_name_qualified($ns), " : = @x\n"; } @x = @x[0 .. $limit - 1] if $limit; if ( $limit && $namespace_trace ) { local $" = ', '; print STDERR "N_oE_m ", scalar ModelElement_name_qualified($ns), " limited = @x\n"; } wantarray ? @x : \@x; } sub Namespace_ownedElement_name_safe { my ($ns, $name) = @_; #$DB::single = 1 if $ns->{'name'} eq 'Model_Management'; # Nice hack for relative Namespaces. if ( $name eq '.' ) { return $ns; } elsif ( $name eq '..' ) { $ns = $ns->namespace || $ns; return $ns; } # Try ownedElements first. for my $elem ( $ns->ownedElement ) { # $DB::single = 1 unless ref($elem); confess("ownedElement $elem is not blessed ref") unless ref($elem) =~ /::/; return $elem if ( $elem->name eq $name ); } # Try importedElement if $ns is a package! if ( $ns->isaPackage ) { for my $elem ( $ns->importedElement ) { # alias through ElementImport? #$DB::single = 1 unless ref($elem); confess("importedElement is not ref") unless ref($elem); return $elem if ( $elem->name eq $name ); } } undef; } sub Namespace_ownedElement_name { my ($ns, $name) = @_; my @name = ref($name) ? @$name : split('::', $name); if ( @name != 1 ) { if ( $name[0] eq '.' ) { $ns = $ns; shift @name; } else { $ns = ModelElement_namespace_root($ns); } my $last_name = pop(@name); for my $pn ( @name ) { my $x = Namespace_ownedElement_name_safe($ns, $pn); unless ( $x ) { $DB::single = 1; confess("Cannot find Namespace named '$pn' from Namespace '" . ModelElement_name_qualified($ns) . "' $ns"); } $ns = $x; } $name = $last_name; } Namespace_ownedElement_name_safe($ns, $name); } sub Namespace_ownedElement_name_ { my ($ns, $name) = @_; # Incase its already resolved. return $name if ref($name); my $x = Namespace_ownedElement_name($ns, $name); confess("Cannot find ModelElement named '$name' from Namespace '" . ModelElement_name_qualified($ns) . "' $ns") unless $x; $x; } # # This is a lexical convention, not a UML convention. # # If looking up by name from within the context of a Classifier fails, # fallback on the Classifier's namespace. # # This may not be typical of most languages but we are # parsing a specification, and we dont need a bunch of Usages # when most Classifiers in a Package colaborate with each other. # # There was some mention of direct ownedElements are visible from all # parent namespaces in the UML spec. # sub Namespace_lookup { my ($ns, $name) = @_; # Shorthand for search/replace. if ( ref($name) eq 'SCALAR' ) { return $$name = Namespace_lookup($ns, $$name); } # Namespace searches in a Classifier should # always bounce out to its namespace. if ( $ns->isaClassifier ) { my $x = Namespace_ownedElement_name($ns, $name); return $x if $x; # Try Classifier's namespace. $ns = $ns->namespace; } Namespace_ownedElement_name_($ns, $name); }
sub Namespace_namespace { Namespace_ownedElement_match($_[0], 'isaNamespace', 1); }
sub Namespace_classifier { Namespace_ownedElement_match($_[0], 'isaClassifier', 1); }
sub Namespace_class { Namespace_ownedElement_match($_[0], 'isaClass', 1); }
sub Namespace_associationClass { Namespace_ownedElement_match($_[0], 'isaAssociationClass', 1); }
sub Namespace_interface { Namespace_ownedElement_match($_[0], 'isaInterface', 1); }
sub Namespace_enumeration { Namespace_ownedElement_match($_[0], 'isaEnumeration', 1); } #######################################################################
sub GeneralizableElement_generalization_parent ($) { my ($self) = @_; # $DB::single = 1 unless $self =~ /::/; # confess("not a ref") unless ref($self); my @x = map($_->parent, $self->generalization); @x; }
sub GeneralizableElement_generalization_parent_all ($) { my ($self) = @_; # $DB::single = 1 unless $self =~ /::/; my @gen_all; my @q = GeneralizableElement_generalization_parent($self); while ( @q ) { my $q = pop @q; next if grep($_ eq $q, @gen_all); push(@gen_all, $q); push(@q, map($_->parent, grep(defined, $q->generalization), ) ); } @gen_all; } sub GeneralizableElement_generalization_child ($) { my ($self) = @_; # $DB::single = 1 unless $self =~ /::/; # confess("not a ref") unless ref($self); my @x = map($_->child, $self->generalization); @x; } sub GeneralizableElement_generalization_child_all ($) { my ($self) = @_; # $DB::single = 1 unless $self =~ /::/; my @gen_all; my @q = GeneralizableElement_generalization_child($self); while ( @q ) { my $q = pop @q; next if grep($_ eq $q, @gen_all); push(@gen_all, $q); push(@q, map($_->child, grep(defined, $q->generalization), ) ); } @gen_all; } #######################################################################
sub Classifier_attribute { my ($node) = @_; grep($_->isaAttribute, $node->feature); }
sub Classifier_operation { my ($node) = @_; grep($_->isaOperation, $node->feature); }
sub Classifier_method { my ($node) = @_; grep($_->isaMethod, $node->feature); } #######################################################################
sub Operation_return { my ($node) = @_; my @x = grep($_->kind eq 'return', $node->parameter); @x ? $x[0] : undef; } #######################################################################
sub Expression_body_language { my ($obj, $lang) = @_; my $value; if ( $obj ) { # Specific language? if ( $lang && lc($obj->language) eq lc($lang) ) { # confess("$obj -> body") unless UNIVERSAL::can($obj, 'body'); $value = $obj->body; } # Universal language? elsif ( $lang && ! length($obj->language) ) { $value = $obj->body; } # No language specified. elsif ( ! $lang ) { $value = $obj->body; } } if ( defined $value ) { $value = trimws($value); } $value = undef if defined $value && ! length $value; $value; } #######################################################################
sub Attribute_initialValue_language { my ($node, $lang) = @_; no warnings; my $body; # Use initialValue.body if it matches the requested language. my $iV = $node->initialValue; if ( $iV && lc($iV->language) eq lc($lang) ) { $body = $iV->body; } else { # Try tagged values. $lang = ucfirst($lang); $body = (grep(defined, map(join('', ModelElement_taggedValue_name($node, $_)), "ummf.$lang.initialValue", "ummf.initialValue") ))[0]; } # Default to initialValue.body if initialValue.language is not specified. if ( ! defined $body ) { if ( $iV && ! length($iV->language) ) { $body = $iV->body; } } $body; } ####################################################################### sub __make_association_end_name { my ($x, $y) = @_; $x = $x->name if ( ref($x) ); $x =~ s/^.*:://s; if ( @_ == 2 ) { $x = "${y}_$x" if $y; $x .= '_'; } else { $x = lcfirst($x); } $x; } sub __fix_association_end_names { if ( ! $_[1] && $_[3] ) { # T1 "" --- T2 n2 => n2_T1_ # $DB::single = 1; #local $" = "\",\t\""; print STDERR "A\t@_\n"; $_[1] = __make_association_end_name($_[0]); #print STDERR " =>\t@_\n"; } elsif ( $_[1] && ! $_[3] ) { # T1 n1 --- T2 "" => n1_T2_ # $DB::single = 1; #local $" = "\",\t\""; print STDERR "A\t@_\n"; $_[3] = __make_association_end_name($_[2]); #print STDERR " =>\t@_\n"; } elsif ( ! $_[1] && ! $_[3] ) { # $DB::single = 1; #local $" = "\",\t\""; print STDERR "A\t@_\n"; $_[1] = __make_association_end_name($_[0]); $_[3] = __make_association_end_name($_[2]); #print STDERR " =>\t@_\n"; } if ( $_[1] eq $_[3] ) { $_[1] .= '0'; $_[3] .= '1'; } } my ($a_type, $a_name, $e_type, $e_name) = ( 'Association', '', 'AssociationEnd', 'connection', ); __fix_association_end_names($a_type, $a_name, $e_type, $e_name); sub AssociationEnd_association { my ($end) = @_; confess("undef") unless $end; my $assoc; # # Hack to get around the unamed AssocationEnd between # AssocationEnd (connection) ------ () Association. # if ( 1 ) { # See modified MetaModel.spec. # #$DB::single = 1; $assoc = $end->{'_association'}; } if ( 0 && ! $assoc ) { # See __fix_association_end_names() $assoc = $end->$a_name; } unless ( $assoc ) { # Since the UML meta model does not have a role name for the Association # it is not navigable. # So to find the AssociationEnd opposite another end, we have to search the entire # model. # local $namespace_trace = "AssociationEnd_association $end"; my ($assoc) = Namespace_ownedElement_match (ModelElement_namespace_root($end->participant), sub { $_[0]->isaAssociation && grep($_ eq $end, $_[0]->connection); }, 1, ); } $DB::single = 1 unless $assoc; confess("Cannot get Association from AssocationEnd " . AssociationEnd_asString($end)) unless $assoc; $assoc; }
sub AssociationEnd_opposite { my ($end) = @_; my @x; my $assoc = AssociationEnd_association($end); @x = grep($_ ne $end, $assoc->connection); @x; } ####################################################################### # These assume a Factory. #
sub Multiplicity_fromString { my ($str, $factory) = @_; # Shorthand. if ( ref($str) eq 'SCALAR' ) { return $$str = Multiplicity_fromString($$str, $factory); } # Dont bother if its already an object. return $str if ref($str); my @range = split(/\s*,\s*/, $str); push(@range, 1) unless @range; for my $r ( @range ) { my @x = split(/\s*\.\.\s*/, $r, 2); $r = $factory->create('MultiplicityRange', 'lower' => $x[0] ne '*' ? $x[0] : 0, 'upper' => $x[1] || $x[0], ); } my $x = $factory->create('Multiplicity', 'range' => \@range, ); $x; } sub Multiplicity_asString { my ($node) = @_; return $node unless ref $node; join(',', map(MultiplicityRange_asString($_), $node->range) ); } sub Multiplicity_lower { my ($multi) = @_; my $lower; # Note: # This code does not use accessor methods, # so UMMF::Boot::Factory::Object::AUTOLOAD() # will not go into recursion. # if ( $multi ) { for my $r ( members($multi->{'range'}) ) { my $x = $r->{'lower'}; # ArgoUML and Poseidon use '-1' for UnlimitedInteger. $x = '*' if $x < 0; if ( $x eq '*' ) { confess("Unexpected UnlimitedInteger for MultiplicityRange lower"); } elsif ( ! defined $lower ) { $lower = $x; } elsif ( $lower > $x ) { $lower = $x; } } } $lower = 1 unless defined $lower; # If none specified. $lower; } sub Multiplicity_upper { my ($multi) = @_; my $upper = 1; # If none specified. # Note: # This code does not use accessor methods, # so UMMF::Boot::Factory::Object::AUTOLOAD() # will not go into recursion. # if ( $multi ) { for my $r ( members($multi->{'range'}) ) { my $x = $r->{'upper'}; # ArgoUML and Poseidon use '-1' for UnlimitedInteger. $x = '*' if $x < 0; if ( $x eq '*' ) { $upper = $x; last; } elsif ( $upper < $x ) { $upper = $x; } } } $upper; } sub MultiplicityRange_asString { my ($node) = @_; return 1 unless $node; my $u = $node->upper; my $l = $node->lower; $l = '*' if $l < 0; $u eq $l ? $u : "$l..$u"; } sub ModelElement_name_with_id { my ($p) = @_; no warnings; my $id = ref($p) ? $p->{'_id'} : ''; $p = ref($p) ? $p->{'name'} : $p; $p = '""' unless length $p; $p = "$p /*$id*/" if ( $id ); $p; } sub Association_asString { my ($assoc, %end_annot) = @_; no warnings; my $name = ModelElement_name_with_id($assoc); sprintf(" @ @ %-50s\n", $name ) . join(",\n", map(" " . AssociationEnd_asString($_, $end_annot{$_}), members($assoc->{'connection'}) ) ) . ';' } sub AssociationEnd_asString { my ($x, $end_annot) = @_; no warnings; my $p = ModelElement_name_with_id($x->{'participant'}); sprintf("%-2s %-3s %25s : %-20s %-5s %s %s%s%s", ( (String_toBoolean($x->{'isNavigable'}) && '->'), ($x->{'aggregation'} eq 'aggregate' && "<>") || ($x->{'aggregation'} eq 'composite' && "<#>") ), ( ($x->{'visibility'} eq 'public' && '+') || ($x->{'visibility'} eq 'private' && '-') || ($x->{'visibility'} eq 'protected' && '#') || ($x->{'visibility'} eq 'package' && '~') || ' ' ) . ($x->{'name'} || '""'), $p, Multiplicity_asString($x->{'multiplicity'}), ($x->{'ordering'} eq 'ordered' && " {ordered}"), ($x->{'_phantom'} && " {phantom}"), ($x->{'_trace'} && " <<trace>> --> " . $x->{'_trace'}{'name'}), " /*$x->{_id}*/ $end_annot", ); } sub Attribute_asString { my ($x) = @_; no warnings; my $p = ModelElement_name_with_id($x->{'type'}); my $m = Multiplicity_asString($x->{'multiplicity'}); my $iV = $x->{'initialValue'}; if ( ref($iV) ) { $iV = $iV->{'body'} . "/* $iV->{language} */"; } sprintf(" %25s %-30s\n", '', "/* Attribute $x->{_id} */", ) . sprintf(" %25s : %-20s %-7s%s;%s", ( ($x->{'visibility'} eq 'public' && '+') || ($x->{'visibility'} eq 'private' && '-') || ($x->{'visibility'} eq 'protected' && '#') || ($x->{'visibility'} eq 'package' && '%') || ' ' ) . ($x->{'name'} || '""'), $p, $m ne '1' ? "[$m]" : '', (defined $iV ? ' = ' . $iV : ''), ($x->{'_trace'} && ("/* <<trace>> --> " . $x->{'_trace'}{'name'} . " */")), ); } ####################################################################### sub ModelElement_taggedValue_name_array { my ($node, $name, $default) = @_; $DB::single = 1 unless ref($node); #$DB::single = 1 if ( ModelElement_name_qualified($node) =~ /sonema/ ); return +() unless $node->can('taggedValue'); my @x = grep($_->type->name eq $name, $node->taggedValue); #local $" = ', '; print STDERR "ME_tV_n ", scalar ModelElement_name_qualified($node), " $name => @x \n"; @x ? @x : ( defined $default ? +( $default ) : () ); } sub ModelElement_taggedValue_name { my ($node, $name, $default) = @_; my @x = ModelElement_taggedValue_name_array($node, $name); # This assumes that everybody wants dataValue as a string, # instead of the String[*] its specified as in the UML 1.5 Spec. @x ? join('', $x[0]->dataValue) : $default; } sub ModelElement_taggedValue_name_true { String_toBoolean(ModelElement_taggedValue_name(@_)); } sub ModelElement_taggedValue_inheritsFrom { my ($node) = @_; # Traverse up containment. # # Many XMI models do not specify namespaces for # Features (Attributes), so use the Feature's owner # for taggedValue inheritance. # if ( $node->isaFeature ) { $node = $node->owner; } else { $node = $node->namespace; } $node; } my $hiddenDefault = [ ]; sub ModelElement_taggedValue_inherited { my ($node, $name, $default) = @_; # print STDERR "ME_tV_i ", scalar ModelElement_name_qualified($node), " $name =>\n"; while ( $node ) { my $x = ModelElement_taggedValue_name($node, $name, $hiddenDefault); if ( $x ne $hiddenDefault ) { $default = $x; last; } $node = ModelElement_taggedValue_inheritsFrom($node); } # print STDERR " '$default'\n"; $default; } sub ModelElement_taggedValue_inherited_true { String_toBoolean(ModelElement_taggedValue_inherited(@_)); } sub ModelElement_taggedValue_trace { ModelElement_taggedValue_inherited(@_); } sub ModelElement_set_taggedValue_name { my ($node, $name, $value, $factory) = @_; my @x = grep($_->type->name eq $name, $node->taggedValue); if ( @x ) { grep($_->set_dataValue($value), @x); } else { my $td = TagDefinition_for_name($node, $name, $factory); my $tv = $factory->create('TaggedValue', 'type' => $td, 'dataValue' => [ $value ], ); $node->add_taggedValue($tv); } $node } sub TagDefinition_for_name { my ($node, $name, $factory, $multiplicity) = @_; my $model = $node && ModelElement_namespace_root($node); my $td; ($td) = grep($_->isaTagDefinition && $_->name eq $name, $model->ownedElement) if $model; unless ( $td ) { $multiplicity ||= '1'; $td = $factory->create('TagDefinition', 'name' => $name, 'multiplicity' => Multiplicity_fromString($multiplicity, $factory), 'tagType' => '', ); $model->add_ownedElement($td) if $model; } $td; } #######################################################################
sub Class_Association_Attribute { my ($cls, $factory) = @_; $factory ||= $cls->__factory; my @attr; for my $cls_end ( $cls->association ) { for my $end ( AssociationEnd_opposite($cls_end) ) { my $name = $end->name; # IMPLEMENT: naming. next unless $name; next unless $end->isNavigable eq 'true'; my $multi = $end->multiplicity; my $type = $end->participant; my $targetScope = $cls_end->targetScope; my $ordering = $end->ordering; my $visibility = $end->visibility; my $attr = $factory->create('Attribute', # 'owner' => $cls, 'name' => $name, 'type' => $type, 'multiplicity' => $multi, 'targetScope' => $targetScope, 'ordering' => $ordering, 'visibility' => $visibility, ); # IMPLEMENT: Add trace push(@attr, $attr); } } wantarray ? @attr : \@attr; } #######################################################################
sub AssociationClass_Attribute { my ($cls, $factory) = @_; $factory ||= $cls->__factory; my @attr; for my $end ( $cls->connection ) { my $name = $end->name; # IMPLEMENT: naming. next unless $name; next unless $end->isNavigable eq 'true'; my $multi = $end->multiplicity; my $type = $end->participant; my $targetScope = 'instance'; my $ordering = $end->ordering; my $visibility = $end->visibility; my $attr = $factory->create('Attribute', # 'owner' => $cls, 'name' => $name, 'type' => $type, 'multiplicity' => $multi, 'targetScope' => $targetScope, 'ordering' => $ordering, 'visibility' => $visibility, ); # IMPLEMENT: Add trace push(@attr, $attr); } wantarray ? @attr : \@attr; } ####################################################################### sub Model_clone { my ($node, $map, $map_) = @_; $map ||= { }; my $ref = ref($node); return $node unless $ref; return [ map(Model_clone($_, $map, $map_), @$node) ] if $ref eq 'ARRAY'; return { map(Model_clone($_, $map, $map_), %$node) } if $ref eq 'HASH'; my $node_x = $map->{$node}; return $node_x if $node_x; $map->{$node} = $node_x = bless({ %$node }, $ref); $map_->{$node_x} = $node if $map_; for my $key ( keys %$node_x ) { my $v = \{$node_x->{$key}}; $$v = Model_clone($$v, $map, $map_); } $node_x; } sub Attribute_clone { my ($node) = @_; $node = $node->__clone; $node->{'owner'} = undef; $node; } sub Operation_clone { my ($node) = @_; $node = $node->__clone; $node->{'owner'} = undef; my @x = map($_->__clone, $node->parameter); grep($_->{'parameter_'} = undef, @x); $node->{'parameter'} = [ ]; $node->set_parameter(@x); $node; } sub Association_clone { my ($node) = @_; confess("undef") unless $node; $node = $node->__clone; # Make new AssociationEnds. # $DB::single = 1; my @x = map($_->__clone, $node->connection); grep($_->{'connection_'} = undef, @x); $node->{'connection'} = [ ]; $node->set_connection(@x); $node; } ####################################################################### sub Model_destroy { my ($x) = @_; my $ref = ref($x); return unless $ref; my @x; if ( $ref =~ /ARRAY/ ) { @x = @$x; @$ref = (); } elsif ( $ref =~ /HASH/ ) { @x = @$x; %$ref = (); } elsif ( $ref =~ /SCALAR/ ) { $$ref = undef; } grep(Model_destroy($_), @x); } ####################################################################### 1; ####################################################################### ### Keep these comments at end of file: kstephens@users.sourceforge.net 2003/04/06 ### ### Local Variables: ### ### mode:perl ### ### perl-indent-level:2 ### ### perl-continued-statement-offset:0 ### ### perl-brace-offset:0 ### ### perl-label-offset:0 ### ### End: ###