# How many we get back. Use '1' for 1 and '*' for lots.
# TODO If not set then decide depening on the return?
},
- {
+ {
name => "operations",
path => "UML:Classifier.feature/UML:Operation",
class => "operation",
multiplicity => "*",
},
- {
+ {
name => "taggedValues",
path => 'UML:ModelElement.taggedValue/UML:TaggedValue',
- class => "taggedValue",
+ class => "taggedValue",
multiplicity => "*",
- # Nice if we could say that the list should me folded into a hash
- # on the name key. type=>"hash", hash_key=>"name" or something!
- },
+ map => "name",
+ # Add a _map_taggedValues to the data. Its a hash of the name data
+ # which refs the normal list of kids
+ },
],
};
path => 'UML:ModelElement.taggedValue/UML:TaggedValue',
class => "taggedValue",
multiplicity => "*",
+ map => "name",
},
],
};
path => 'UML:ModelElement.taggedValue/UML:TaggedValue',
class => "taggedValue",
multiplicity => "*",
+ map => "name",
},
],
};
$spec12->{parameter} = {
name => "parameter",
plural => "parameters",
- default_path => '//UML:BehavioralFeature.parameter/UML:Parameter[@xmi.id]',
+ default_path => '//UML:Parameter[@xmi.id]',
attrib_data => [qw/name isSpecification kind/],
path_data => [
{
mk_get_dispatch();
# Build lookups etc. Its important that each spec item becomes self contained
-# so we can build good closures, therefor we do all the lookups 1st.
+# so we can build good closures, therefore we do all the lookups 1st.
sub init_specs {
my $specs = shift;
foreach my $spec ( values %$specs ) {
- foreach ( @{$spec->{kids}} ) {
+ # Look up for kids get method
+ foreach ( @{$spec->{kids}} ) {
$_->{get_method} = "get_".$specs->{$_->{class}}{plural};
}
+
+ # Add xmi.id ti all specs. Everything we want at the moment (in both
+ # versions) has an id. The tags that don't seem to be used for
+ # structure.
+ my $attrib_data = $spec->{attrib_data} ||= [];
+ push @$attrib_data, "xmi.id";
}
}
$me = { xml_xpath => $xp };
# Work out the version of XMI we have and generate the get subs to parse it
- my $xmiv = "".$xp->findvalue('/XMI/@xmi.version')
- or die "Can't find XMI version";
+ my $xmiv = $args{xmi_version}
+ || "".$xp->findvalue('/XMI/@xmi.version')
+ || die "Can't find XMI version";
$me->{xmi_get_} = mk_gets($SPECS->{$xmiv});
return bless $me, $class;
# NB: DO NOT use publicly as you will break the version independance. e.g. When
# using _xpath you need to know which version of XMI to use. This is handled by
# the use of different paths in the specs.
-#
+#
# _context => The context node to use, if not given starts from root.
-#
+#
# _xpath => The xpath to use for finding stuff.
-#
+#
use Data::Dumper;
sub mk_get {
my $spec = shift;
-
+
# get_* closure using $spec
return sub {
my ($me, %args) = @_;
for my $node (@nodes) {
my $thing = {};
# my $thing = { xpNode => $node };
-
+
# Get the Tag attributes
foreach ( @{$spec->{attrib_data}} ) {
$thing->{$_} = $node->getAttribute($_);
}
-
+
# Add the path data
foreach ( @{$spec->{path_data}} ) {
#warn "Searching for $spec->{plural} - $_->{name} using:$_->{path}\n";
$thing->{$_->{name}} = @nodes ? $nodes[0]->getData
: (exists $_->{default} ? $_->{default} : undef);
}
-
- # Run any filters set
- #
+
+ # Run any filters set
+ #
# Should we do this after the kids as we may want to test them?
# e.g. test for number of attribs
if ( my $filter = $args{filter} ) {
local $_ = $thing;
next unless $filter->($thing);
}
-
+
# Kids
#
foreach ( @{$spec->{kids}} ) {
- my $data;
+ my $data;
my $meth = $_->{get_method};
- $data = $me->$meth( _context => $node, _xpath => $_->{path},
+ my $path = $_->{path};
+ $data = $me->$meth( _context => $node, _xpath => $path,
filter => $args{"filter_$_->{name}"} );
-
+
if ( $_->{multiplicity} eq "1" ) {
$thing->{$_->{name}} = shift @$data;
}
else {
- $thing->{$_->{name}} = $data || [];
+ my $kids = $thing->{$_->{name}} = $data || [];
+ if ( my $key = $_->{"map"} ) {
+ $thing->{"_map_$_->{name}"} = _mk_map($kids,$key);
+ }
}
}
} # /mk_get
+sub _mk_map {
+ my ($kids,$key) = @_;
+ my $map = {};
+ foreach (@$kids) {
+ $map->{$_->{$key}} = $_ if exists $_->{$key};
+ }
+ return $map;
+}
+
1; #===========================================================================
=head2 new
-Pass in name/value arg of either filename, xml or ioref for the XMI data you
-want to parse.
+Pass in name/value arg of either C<filename>, C<xml> or C<ioref> for the XMI
+data you want to parse.
+
+The version of XMI to use either 1.0 or 1.2 is worked out from the file. You
+can also use a C<xmi_version> arg to set it explicitley.
=head2 get_* methods