From: Mark Addison Date: Tue, 9 Sep 2003 01:00:44 +0000 (+0000) Subject: More refactoring and code tidy. We now have get_attributes and X-Git-Tag: v0.04~187 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=91ed9c327cb0ce549a85f176d74fba2d949362d2;hp=3bfe5e1ab30054040cc94762c741051dcee64cb4;p=dbsrgits%2FSQL-Translator.git More refactoring and code tidy. We now have get_attributes and get_stereotype. --- diff --git a/lib/SQL/Translator/Parser/XML/XMI.pm b/lib/SQL/Translator/Parser/XML/XMI.pm index c44286c..5716ae9 100644 --- a/lib/SQL/Translator/Parser/XML/XMI.pm +++ b/lib/SQL/Translator/Parser/XML/XMI.pm @@ -1,7 +1,7 @@ package SQL::Translator::Parser::XML::XMI; # ------------------------------------------------------------------- -# $Id: XMI.pm,v 1.3 2003-09-08 17:10:07 grommit Exp $ +# $Id: XMI.pm,v 1.4 2003-09-09 01:00:44 grommit Exp $ # ------------------------------------------------------------------- # Copyright (C) 2003 Mark Addison , # @@ -82,7 +82,7 @@ translated. use strict; use vars qw[ $DEBUG $VERSION @EXPORT_OK ]; -$VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/; +$VERSION = sprintf "%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/; $DEBUG = 0 unless defined $DEBUG; use Data::Dumper; @@ -95,6 +95,9 @@ use SQL::Translator::Utils 'debug'; use XML::XPath; use XML::XPath::XMLParser; +# XMI XPath parsing +#----------------------------------------------------------------------------- + # # get_classes( XPATHOBJ, ARGS ); # @@ -106,68 +109,101 @@ use XML::XPath::XMLParser; # xpath => The xpath to use for finding classes. Default is //UML:Classes # which will find all the classes in the XMI. # -# attribute_test => An XPath predicate (ie the bit between [] ) to test the -# attributes with to decide if we should parse them. ie -# attribute_test => '@name="foo"' would only pass out attribs -# with a name of foo. +# test => An XPath predicate (ie the bit between [] ) to test the +# classes with to decide if we should parse them. ie +# test => '@name' would only pass out classes with a name. +# Can also give it an array ref and it will and the tests. +# It gets tacked onto to xpath so don't put any [] on +# xpath if you use test as well. +# +# attribute_test => An XPath predicate to pass onto get_attributes. # + +# _add_xpath_tests $path, [qw/@name xmiVisible("public")/]; # and +# _add_xpath_tests $path, [qw/@name xmiVisible("public")/], "or"; +sub _add_xpath_tests { + my ($path,$tests,$join) = @_; + return $path unless defined $tests; + my @tests = ref($tests) ? @$tests : $tests; + return $path unless @tests; + $join ||= "and"; + return $path."[".join(" $join ", @tests)."]"; +} + +sub get_stereotype { + my ($xp) = @_; + return "".$xp->findvalue( + 'xmiDeref(UML:ModelElement.stereotype/UML:Stereotype)/@name'); + # TODO Test for difference between it existing or being "" ? +} + sub get_classes { my ($xp,%args) = @_; - my $classes = []; + my $classes; my $xpath = $args{xpath} ||= '//UML:Class'; # Default: all classes - $xpath .= "[$args{class_test}]" if $args{class_test}; + $xpath = _add_xpath_tests $xpath, $args{test}; + debug "Searching for Classes using:$xpath"; my @nodes = $xp->findnodes($xpath); return unless @nodes; for my $classnode (@nodes) { my $class = {}; - - # attributes - foreach ( + + foreach ( qw/name visibility isSpecification isRoot isLeaf isAbstract isActive/ ) { $class->{$_} = $classnode->getAttribute($_); } + $class->{stereotype} = get_stereotype($classnode); - # Stereotype - $class->{stereotype} = "".$classnode->find( - 'xmiDeref(UML:ModelElement.stereotype/UML:Stereotype)/@name'); - - # # Class Attributes - # my $xpath = 'UML:Classifier.feature/UML:Attribute'; - $xpath .= "[$args{attribute_test}]" if $args{attribute_test}; - foreach my $attrnode ( $classnode->findnodes($xpath) ) { - my $attr = {}; - # attributes - foreach (qw/name visibility isSpecification ownerScope/) { - $attr->{$_} = $attrnode->getAttribute($_); - } - - $attr->{stereotype} = "".$attrnode->findvalue( - 'xmiDeref(UML:ModelElement.stereotype/UML:Stereotype)/@name'); - - $attr->{datatype} = "".$attrnode->find( - 'xmiDeref(UML:StructuralFeature.type/UML:DataType)/@name'); - if ( my @body = $attrnode->findnodes( - 'UML:Attribute.initialValue/UML:Expression/@body') - ) { - $attr->{initialValue} = $body[0]->getData; - } - - push @{$class->{attributes}}, $attr; - } - + $class->{attributes} = get_attributes( $classnode, + xpath => $xpath, test => $args{attribute_test} ); + push @$classes, $class; } - - return $classes; + return wantarray ? @$classes : $classes; }; +sub get_attributes { + my ($xp, %args) = @_; + + my $xpath = $args{xpath} ||= '//UML:Classifier.feature/UML:Attribute'; + $xpath = _add_xpath_tests $xpath, $args{test}; + debug "Searching for Attributes using:$xpath"; + + my $attributes; + foreach my $node ( $xp->findnodes($xpath) ) { + my $attr = {}; + + foreach (qw/name visibility isSpecification ownerScope/) { + $attr->{$_} = $node->getAttribute($_); + } + $attr->{stereotype} = get_stereotype($node); + + # Get datatype name and the name body of the initial value + $attr->{datatype} = "".$node->find( + 'xmiDeref(UML:StructuralFeature.type/UML:DataType)/@name'); + if ( my @body = $node->findnodes( + 'UML:Attribute.initialValue/UML:Expression/@body') + ) { + $attr->{initialValue} = $body[0]->getData; + } + + push @$attributes, $attr; + } + return wantarray ? @$attributes : $attributes; +} + + + +# SQLFairy Parser +#----------------------------------------------------------------------------- + sub parse { eval { @@ -188,20 +224,18 @@ sub parse { # # Build an XPath for the classes and attributes we want... # - my @tests = ('@xmi.id'); # Only classes with an id so we don't get any - # refs to classes ie xmi.idref classes - push @tests, '@name'; # Only Classes with a name + # Only classes with an id (so we don't get any refs to classes ie + # xmi.idref classes). They also need a name to be usefull. + my @tests = ('@xmi.id and @name'); push @tests, "xmiVisible('$pargs->{visibility}')" if $pargs->{visibility}; - my $path = '//UML:Class['.join(' and ',@tests).']'; - my $attrib_test = '@name'; + my $attrib_test = '@name and @xmi.id'; $attrib_test .= " and xmiVisible('$pargs->{visibility}')" if $pargs->{visibility}; # ...and parse them out - debug "Searching for Classes using:$path"; my $classes = get_classes( $xp, - xpath => $path, attribute_test => $attrib_test); + xpath => "//UML:Class", test => [@tests], attribute_test => $attrib_test); debug "Found ".scalar(@$classes)." Classes: ".join(", ", map {$_->{"name"}} @$classes) if $DEBUG;