+ Added visability arg.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / XML / XMI.pm
CommitLineData
1223c9b2 1package SQL::Translator::Parser::XML::XMI;
2
3# -------------------------------------------------------------------
ef2d7798 4# $Id: XMI.pm,v 1.2 2003-09-08 12:27:29 grommit Exp $
1223c9b2 5# -------------------------------------------------------------------
6# Copyright (C) 2003 Mark Addison <mark.addison@itn.co.uk>,
7#
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.
11#
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.
16#
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
20# 02111-1307 USA
21# -------------------------------------------------------------------
22
23=head1 NAME
24
25SQL::Translator::Parser::XML::XMI - Parser to create Schema from UML
26Class diagrams stored in XMI format.
27
28=cut
29
30# -------------------------------------------------------------------
31
32use strict;
33
34use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
ef2d7798 35$VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;
1223c9b2 36$DEBUG = 0 unless defined $DEBUG;
37
38use Data::Dumper;
39use Exporter;
40use base qw(Exporter);
41@EXPORT_OK = qw(parse);
42
43use base qw/SQL::Translator::Parser/; # Doesnt do anything at the mo!
44use SQL::Translator::Utils 'debug';
45use XML::XPath;
46use XML::XPath::XMLParser;
47
48
49# Custom XPath functions
50#-----------------------------------------------------------------------------
51
52#
53# Pass a nodeset. If the first node has an xmi.idref attrib then return
54# the nodeset for that id
55#
56sub XML::XPath::Function::xmideref {
57 my $self = shift;
58 my ($node, @params) = @_;
59 if (@params > 1) {
60 die "xmideref() function takes one or no parameters\n";
61 }
62 elsif (@params) {
63 my $nodeset = shift(@params);
64 return $nodeset unless $nodeset->size;
65 $node = $nodeset->get_node(1);
66 }
67 die "xmideref() needs an Element node."
68 unless $node->isa("XML::XPath::Node::Element");
ef2d7798 69
1223c9b2 70 my $id = $node->getAttribute("xmi.idref") or return $node;
71 return $node->getRootNode->find('//*[@xmi.id="'.$id.'"]');
72}
73
74sub XML::XPath::Function::hello {
75 return XML::XPath::Literal->new("Hello World");
76}
77
78
79
80# Parser
81#-----------------------------------------------------------------------------
82
ef2d7798 83#
84# is_visible( {ELEMENT|VIS_OF_THING}, VISLEVEL)
85#
86# Returns true or false for whether the visibility of something e.g. Class,
87# Attribute, is visible at the level given.
88#
89{
90 my %vislevel = (
91 public => 1,
92 protected => 2,
93 private => 3,
94 );
95
96 sub is_visible {
97 my ($arg, $vis) = @_;
98 return 1 unless $vis;
99 my $foo;
100 die "is_visible : Needs something to test" unless $arg;
101 if ( $arg->isa("XML::XPath::Node::Element") ) {
102 $foo = $arg->getAttribute("visibility");
103 }
104 else {
105 $foo = $arg;
106 }
107 return 1 if $vislevel{$vis} >= $vislevel{$foo};
108 return 0;
109 }
110}
111
1223c9b2 112sub parse {
113 my ( $translator, $data ) = @_;
114 local $DEBUG = $translator->debug;
115 my $schema = $translator->schema;
ef2d7798 116 my $pargs = $translator->parser_args;
1223c9b2 117
ef2d7798 118 debug "Visibility Level:$pargs->{visibility}" if $DEBUG;
119
120 my $xp = XML::XPath->new(xml => $data);
1223c9b2 121 $xp->set_namespace("UML", "org.omg.xmi.namespace.UML");
122 #
123 # TODO
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.
ef2d7798 126
1223c9b2 127 #
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.
131 #
132 my @nodes = $xp->findnodes('//UML:Class[@xmi.id]');
ef2d7798 133
1223c9b2 134 debug "Found ".scalar(@nodes)." Classes: ".join(", ",
ef2d7798 135 map {$_->getAttribute("name")} @nodes) if $DEBUG;
136
1223c9b2 137 for my $classnode (@nodes) {
138 # Only process classes with <<Table>> and name
139 next unless my $classname = $classnode->getAttribute("name");
ef2d7798 140 next unless !$pargs->{visibility}
141 or is_visible($classnode, $pargs->{visibility});
142
1223c9b2 143 my $stereotype = "".$classnode->find(
144 'xmideref(UML:ModelElement.stereotype/UML:Stereotype)/@name');
145 next unless $stereotype eq "Table";
ef2d7798 146
1223c9b2 147 # Add the table
ef2d7798 148 debug "Adding class: $classname as table:$classname" if $DEBUG;
1223c9b2 149 my $table = $schema->add_table(name=>$classname)
150 or die "Schema Error: ".$schema->error;
151
152 #
153 # Fields from Class attributes
154 #
ef2d7798 155 # name data_type size default_value is_nullable
1223c9b2 156 # is_auto_increment is_primary_key is_foreign_key comments
157 #
158 foreach my $attrnode ( $classnode->findnodes(
ef2d7798 159 'UML:Classifier.feature/UML:Attribute[@xmi.id]',)
1223c9b2 160 ) {
161 next unless my $fieldname = $attrnode->getAttribute("name");
ef2d7798 162 next unless !$pargs->{visibility}
163 or is_visible($attrnode, $pargs->{visibility});
164
1223c9b2 165 my $stereotype = "".$attrnode->findvalue(
166 'xmideref(UML:ModelElement.stereotype/UML:Stereotype)/@name');
167 my %data = (
168 name => $fieldname,
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,
173 );
174 if ( my @body = $attrnode->findnodes(
175 'UML:Attribute.initialValue/UML:Expression/@body')
176 ) {
177 $data{default_value} = $body[0]->getData;
178 }
179
180 debug "Adding field:",Dumper(\%data);
181 my $field = $table->add_field( %data ) or die $schema->error;
182
183 $table->primary_key( $field->name ) if $data{'is_primary_key'};
184 #
185 # TODO:
ef2d7798 186 # - We should be able to make the table obj spot this when
1223c9b2 187 # we use add_field.
188 #
189 }
190
191 } # Classes loop
192
193 return 1;
194}
195
1961;
197
198# -------------------------------------------------------------------
199
200=pod
201
202=head1 SYNOPSIS
203
204 use SQL::Translator;
205 use SQL::Translator::Parser::XML::XMI;
206
207 my $translator = SQL::Translator->new(
208 from => 'XML-XMI',
209 to => 'MySQL',
210 filename => 'schema.xmi',
211 show_warnings => 1,
212 add_drop_table => 1,
213 );
214
215 print $obj->translate;
216
217=head1 DESCRIPTION
218
219=head2 UML Data Modeling
220
221To tell the parser which Classes are tables give them a <<Table>> stereotype.
222
223Any attributes of the class will be used as fields. The datatype of the
224attribute must be a UML datatype and not an object, with the datatype's name
225being used to set the data_type value in the schema.
226
227Primary keys are attributes marked with <<PK>> stereotype.
228
229=head2 XMI Format
230
231The parser has been built using XMI generated by PoseidonUML 2beta, which
232says it uses UML 2. So the current conformance is down to Poseidon's idea
233of XMI!
234
235=head1 ARGS
236
237=over 4
238
ef2d7798 239=item visibility
1223c9b2 240
ef2d7798 241 visibilty=public|protected|private
1223c9b2 242
243What visibilty of stuff to translate. e.g when set to 'public' any private
ef2d7798 244and package Classes will be ignored and not turned into tables. Applies
245to Classes and Attributes.
1223c9b2 246
ef2d7798 247If not set or false (the default) no checks will be made and everything is
248translated.
1223c9b2 249
250=back
251
252=head1 BUGS
253
ef2d7798 254Seems to be slow. I think this is because the XMI files can get pretty
255big and complex, especially all the diagram info.
256
1223c9b2 257=head1 TODO
258
ef2d7798 259B<field sizes> Don't think UML does this directly so may need to include
1223c9b2 260it in the datatype names.
261
ef2d7798 262B<table_visibility and field_visibility args> Seperate control over what is
263parsed, setting visibility arg will set both.
264
1223c9b2 265Everything else! Relations, fkeys, constraints, indexes, etc...
266
267=head1 AUTHOR
268
269Mark D. Addison E<lt>mark.addison@itn.co.ukE<gt>.
270
271=head1 SEE ALSO
272
273perl(1), SQL::Translator, XML::XPath, SQL::Translator::Producer::XML::SQLFairy,
274SQL::Translator::Schema.
275
276=cut