1 package SQL::Translator::Parser::XML::XMI;
3 # -------------------------------------------------------------------
4 # $Id: XMI.pm,v 1.2 2003-09-08 12:27:29 grommit Exp $
5 # -------------------------------------------------------------------
6 # Copyright (C) 2003 Mark Addison <mark.addison@itn.co.uk>,
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License as
10 # published by the Free Software Foundation; version 2.
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 # -------------------------------------------------------------------
25 SQL::Translator::Parser::XML::XMI - Parser to create Schema from UML
26 Class diagrams stored in XMI format.
30 # -------------------------------------------------------------------
34 use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
35 $VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;
36 $DEBUG = 0 unless defined $DEBUG;
40 use base qw(Exporter);
41 @EXPORT_OK = qw(parse);
43 use base qw/SQL::Translator::Parser/; # Doesnt do anything at the mo!
44 use SQL::Translator::Utils 'debug';
46 use XML::XPath::XMLParser;
49 # Custom XPath functions
50 #-----------------------------------------------------------------------------
53 # Pass a nodeset. If the first node has an xmi.idref attrib then return
54 # the nodeset for that id
56 sub XML::XPath::Function::xmideref {
58 my ($node, @params) = @_;
60 die "xmideref() function takes one or no parameters\n";
63 my $nodeset = shift(@params);
64 return $nodeset unless $nodeset->size;
65 $node = $nodeset->get_node(1);
67 die "xmideref() needs an Element node."
68 unless $node->isa("XML::XPath::Node::Element");
70 my $id = $node->getAttribute("xmi.idref") or return $node;
71 return $node->getRootNode->find('//*[@xmi.id="'.$id.'"]');
74 sub XML::XPath::Function::hello {
75 return XML::XPath::Literal->new("Hello World");
81 #-----------------------------------------------------------------------------
84 # is_visible( {ELEMENT|VIS_OF_THING}, VISLEVEL)
86 # Returns true or false for whether the visibility of something e.g. Class,
87 # Attribute, is visible at the level given.
100 die "is_visible : Needs something to test" unless $arg;
101 if ( $arg->isa("XML::XPath::Node::Element") ) {
102 $foo = $arg->getAttribute("visibility");
107 return 1 if $vislevel{$vis} >= $vislevel{$foo};
113 my ( $translator, $data ) = @_;
114 local $DEBUG = $translator->debug;
115 my $schema = $translator->schema;
116 my $pargs = $translator->parser_args;
118 debug "Visibility Level:$pargs->{visibility}" if $DEBUG;
120 my $xp = XML::XPath->new(xml => $data);
121 $xp->set_namespace("UML", "org.omg.xmi.namespace.UML");
124 # - Options to set the initial context node so we don't just
125 # blindly do all the classes. e.g. Select a diag name to do.
128 # Work our way through the classes, creating tables. We only
129 # want class with xmi.id attributes and not the refs to them,
130 # which will have xmi.idref attributes.
132 my @nodes = $xp->findnodes('//UML:Class[@xmi.id]');
134 debug "Found ".scalar(@nodes)." Classes: ".join(", ",
135 map {$_->getAttribute("name")} @nodes) if $DEBUG;
137 for my $classnode (@nodes) {
138 # Only process classes with <<Table>> and name
139 next unless my $classname = $classnode->getAttribute("name");
140 next unless !$pargs->{visibility}
141 or is_visible($classnode, $pargs->{visibility});
143 my $stereotype = "".$classnode->find(
144 'xmideref(UML:ModelElement.stereotype/UML:Stereotype)/@name');
145 next unless $stereotype eq "Table";
148 debug "Adding class: $classname as table:$classname" if $DEBUG;
149 my $table = $schema->add_table(name=>$classname)
150 or die "Schema Error: ".$schema->error;
153 # Fields from Class attributes
155 # name data_type size default_value is_nullable
156 # is_auto_increment is_primary_key is_foreign_key comments
158 foreach my $attrnode ( $classnode->findnodes(
159 'UML:Classifier.feature/UML:Attribute[@xmi.id]',)
161 next unless my $fieldname = $attrnode->getAttribute("name");
162 next unless !$pargs->{visibility}
163 or is_visible($attrnode, $pargs->{visibility});
165 my $stereotype = "".$attrnode->findvalue(
166 'xmideref(UML:ModelElement.stereotype/UML:Stereotype)/@name');
169 data_type => "".$attrnode->find(
170 'xmideref(UML:StructuralFeature.type/UML:DataType)/@name'),
171 is_primary_key => $stereotype eq "PK" ? 1 : 0,
172 #is_foreign_key => $stereotype eq "FK" ? 1 : 0,
174 if ( my @body = $attrnode->findnodes(
175 'UML:Attribute.initialValue/UML:Expression/@body')
177 $data{default_value} = $body[0]->getData;
180 debug "Adding field:",Dumper(\%data);
181 my $field = $table->add_field( %data ) or die $schema->error;
183 $table->primary_key( $field->name ) if $data{'is_primary_key'};
186 # - We should be able to make the table obj spot this when
198 # -------------------------------------------------------------------
205 use SQL::Translator::Parser::XML::XMI;
207 my $translator = SQL::Translator->new(
210 filename => 'schema.xmi',
215 print $obj->translate;
219 =head2 UML Data Modeling
221 To tell the parser which Classes are tables give them a <<Table>> stereotype.
223 Any attributes of the class will be used as fields. The datatype of the
224 attribute must be a UML datatype and not an object, with the datatype's name
225 being used to set the data_type value in the schema.
227 Primary keys are attributes marked with <<PK>> stereotype.
231 The parser has been built using XMI generated by PoseidonUML 2beta, which
232 says it uses UML 2. So the current conformance is down to Poseidon's idea
241 visibilty=public|protected|private
243 What visibilty of stuff to translate. e.g when set to 'public' any private
244 and package Classes will be ignored and not turned into tables. Applies
245 to Classes and Attributes.
247 If not set or false (the default) no checks will be made and everything is
254 Seems to be slow. I think this is because the XMI files can get pretty
255 big and complex, especially all the diagram info.
259 B<field sizes> Don't think UML does this directly so may need to include
260 it in the datatype names.
262 B<table_visibility and field_visibility args> Seperate control over what is
263 parsed, setting visibility arg will set both.
265 Everything else! Relations, fkeys, constraints, indexes, etc...
269 Mark D. Addison E<lt>mark.addison@itn.co.ukE<gt>.
273 perl(1), SQL::Translator, XML::XPath, SQL::Translator::Producer::XML::SQLFairy,
274 SQL::Translator::Schema.