Added m:n joins. Can now model m:n association in your class diag and the
Mark Addison [Tue, 14 Oct 2003 23:19:44 +0000 (23:19 +0000)]
parser will create a link table for it when creating the schema :)

lib/SQL/Translator/Parser/XML/XMI/SQLFairy.pm
t/28xml-xmi-parser-sqlfairy.t
t/data/xmi/OrderDB.sqlfairy.poseidon2.xmi

index 6bf9c3b..1861a5f 100644 (file)
@@ -1,7 +1,7 @@
 package SQL::Translator::Parser::XML::XMI::SQLFairy;
 
 # -------------------------------------------------------------------
-# $Id: SQLFairy.pm,v 1.2 2003-10-13 17:05:55 grommit Exp $
+# $Id: SQLFairy.pm,v 1.3 2003-10-14 23:19:43 grommit Exp $
 # -------------------------------------------------------------------
 # Copyright (C) 2003 Mark Addison <mark.addison@itn.co.uk>,
 #
@@ -29,7 +29,7 @@ SQL::Translator::Parser::XML::XMI::SQLFairy - Create Schema from UML Models.
 use strict;
 
 use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
-$VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/;
 $DEBUG   = 0 unless defined $DEBUG;
 use Exporter;
 use base qw(Exporter);
@@ -151,7 +151,8 @@ sub classes2schema {
         else
         {
             # m:n
-            warn "Sorry, n:m associations not yet implimented for xmi.id=".$assoc->{"xmi.id"}."\n";
+            many2many($assoc);
+            #warn "Sorry, n:m associations not yet implimented for xmi.id=".$assoc->{"xmi.id"}."\n";
         }
 
     }
@@ -232,7 +233,8 @@ sub add_pkey {
 }
 
 # Maps a 1:M association into the schema
-sub one2many {
+sub one2many
+{
     my ($assoc) = @_;
     my @ends = @{$assoc->{associationEnds}};
     my ($end1) = grep $_->{multiplicity}{rangeUpper} == 1, @ends;
@@ -272,6 +274,48 @@ sub one2many {
     ) or die $schema->error;
 }
 
+# Maps m:n into schema by building a link table.
+sub many2many
+{
+    my ($assoc) = @_;
+    my @end = @{$assoc->{associationEnds}};
+
+    # Create the link table
+    my $name = $end[0]->{participant}{name}."_".$end[1]->{participant}{name};
+    my $link_table = $schema->add_table( name => $name )
+    or die "Schema Error: ".$schema->error;
+
+    # Export the pkey(s) from the ends into the link table
+    my @pkeys;
+    foreach (@end) {
+        my $table = $schema->get_table($_->{participant}{name});
+        my @fkeys = $table->primary_key->fields;
+        push @pkeys,@fkeys;
+        foreach ( @fkeys ) {
+            my $fld = $table->get_field($_);
+            my %data;
+            $data{$_} = $fld->$_()
+                foreach (
+                qw/name size data_type default_value is_nullable is_unique/);
+            $data{is_auto_increment} = 0;
+            $data{extra} = { $fld->extra }; # Copy
+            $link_table->add_field(%data) or die $table->error;
+        }
+        $link_table->add_constraint(
+            type   => "FOREIGN_KEY",
+            fields => [@fkeys],
+            reference_table => $table->{name},
+            reference_fields => [@fkeys],
+        ) or die $schema->error;
+
+    }
+    # Add pkey constraint
+    $link_table->add_constraint( type => "PRIMARY KEY", fields => [@pkeys] )
+    or die $link_table->error;
+
+
+    # Add fkeys to our participants
+}
 1; #---------------------------------------------------------------------------
 
 __END__
index a3e2f21..71c2d38 100644 (file)
@@ -133,7 +133,7 @@ sub test_table {
 # Testing 1,2,3,..
 #=============================================================================
 
-plan tests => 94;
+plan tests => 151;
 
 my $testschema = "$Bin/data/xmi/OrderDB.sqlfairy.poseidon2.xmi";
 die "Can't find test schema $testschema" unless -e $testschema;
@@ -158,8 +158,9 @@ my $scma = $obj->schema;
 is( $scma->is_valid, 1, 'Schema is valid' );
 my @tblnames = map {$_->name} $scma->get_tables;
 is(scalar(@{$scma->get_tables}), scalar(@tblnames), "Right number of tables");
-is_deeply( \@tblnames, [qw/Order OrderLine Customer/]
-    ,"tables");
+is_deeply( \@tblnames, 
+    [qw/Order OrderLine Customer ContactDetails ContactDetails_Customer/]
+,"tables");
 
 test_table( $scma->get_table("Customer"),
     name => "Customer",
@@ -202,6 +203,85 @@ test_table( $scma->get_table("Customer"),
        ],
 );
 
+test_table( $scma->get_table("ContactDetails_Customer"),
+    name => "ContactDetails_Customer",
+    fields => [
+    {
+        name => "ContactDetailsID",
+        data_type => "INT",
+               size => 10,
+        default_value => undef,
+        is_nullable => 0,
+        is_primary_key => 1,
+        is_auto_increment => 0,
+    },
+    {
+        name => "CustomerID",
+        data_type => "INT",
+               size => 10,
+        default_value => undef,
+        is_nullable => 0,
+        is_primary_key => 1,
+        is_auto_increment => 0,
+    },
+    ],
+       constraints => [
+               {
+                       type => "FOREIGN KEY",
+                       fields => "ContactDetailsID",
+                       reference_table => "ContactDetails",
+                       reference_fields => "ContactDetailsID",
+               },
+               {
+                       type => "FOREIGN KEY",
+                       fields => "CustomerID",
+                       reference_table => "Customer",
+                       reference_fields => "CustomerID",
+               },
+               {
+                       type => "PRIMARY KEY",
+                       fields => "ContactDetailsID,CustomerID",
+               },
+       ],
+);
+
+test_table( $scma->get_table("ContactDetails"),
+    name => "ContactDetails",
+    fields => [
+    {
+        name => "address",
+        data_type => "VARCHAR",
+        size => "255",
+        default_value => undef,
+        is_nullable => 1,
+        is_primary_key => 0,
+    },
+    {
+        name => "telephone",
+        data_type => "VARCHAR",
+        size => "255",
+        default_value => undef,
+        is_nullable => 1,
+        is_primary_key => 0,
+    },
+    {
+        name => "ContactDetailsID",
+        data_type => "INT",
+               size => 10,
+        default_value => undef,
+        is_nullable => 0,
+        is_primary_key => 1,
+        is_auto_increment => 1,
+    },
+    ],
+       constraints => [
+               {
+                       type => "PRIMARY KEY",
+                       fields => "ContactDetailsID",
+               },
+       ],
+);
+
 test_table( $scma->get_table("Order"),
     name => "Order",
     fields => [
index 55ea6bf..70bf1bc 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version = '1.0' encoding = 'UTF-8' ?>\r
-<XMI xmi.version = '1.2' xmlns:UML = 'org.omg.xmi.namespace.UML' timestamp = 'Mon Oct 13 17:05:12 BST 2003'>\r
+<XMI xmi.version = '1.2' xmlns:UML = 'org.omg.xmi.namespace.UML' timestamp = 'Wed Oct 15 00:05:38 BST 2003'>\r
   <XMI.header>\r
     <XMI.documentation>\r
       <XMI.exporter>Netbeans XMI Writer</XMI.exporter>\r
         <UML:MultiplicityRange xmi.id = '5539d8:f7b62bc3a2:-7feb' lower = '1' upper = '1'/>\r
       </UML:Multiplicity.range>\r
     </UML:Multiplicity>\r
+    <UML:Multiplicity xmi.id = '9f3e95:f83c9ca08d:-7ffb'>\r
+      <UML:Multiplicity.range>\r
+        <UML:MultiplicityRange xmi.id = '9f3e95:f83c9ca08d:-7ffa' lower = '1' upper = '1'/>\r
+      </UML:Multiplicity.range>\r
+    </UML:Multiplicity>\r
+    <UML:Multiplicity xmi.id = '9f3e95:f83c9ca08d:-7ff9'>\r
+      <UML:Multiplicity.range>\r
+        <UML:MultiplicityRange xmi.id = '9f3e95:f83c9ca08d:-7ff8' lower = '1' upper = '1'/>\r
+      </UML:Multiplicity.range>\r
+    </UML:Multiplicity>\r
+    <UML:Multiplicity xmi.id = '9f3e95:f83c9ca08d:-7ff7'>\r
+      <UML:Multiplicity.range>\r
+        <UML:MultiplicityRange xmi.id = '9f3e95:f83c9ca08d:-7ff6' lower = '0' upper = '-1'/>\r
+      </UML:Multiplicity.range>\r
+    </UML:Multiplicity>\r
     <UML:Model xmi.id = '5539d8:f7b62bc3a2:-7ff5' name = 'OrderDB' isSpecification = 'false'\r
       isRoot = 'false' isLeaf = 'false' isAbstract = 'false'>\r
       <UML:Namespace.ownedElement>\r
           <UML:Stereotype.baseClass>ModelElement</UML:Stereotype.baseClass>\r
         </UML:Stereotype>\r
         <UML:Comment xmi.id = '24d0fa:f8019a3e88:-7fa7' name = '' visibility = 'public'\r
-          isSpecification = 'false' body = 'sqlfSize override&#10;on customerID&#10;&#10;Implied pkey&#10;- uses 1st field'>\r
+          isSpecification = 'false' body = 'sqlfSize override&#10;on customerID'>\r
           <UML:Comment.annotatedElement>\r
             <UML:Class xmi.idref = '5539d8:f7b62bc3a2:-7fe7'/>\r
           </UML:Comment.annotatedElement>\r
             </UML:TaggedValue>\r
           </UML:ModelElement.taggedValue>\r
         </UML:DataType>\r
+        <UML:Class xmi.id = '9f3e95:f83c9ca08d:-7ff5' name = 'ContactDetails' visibility = 'public'\r
+          isSpecification = 'false' isRoot = 'false' isLeaf = 'false' isAbstract = 'false'\r
+          isActive = 'false'>\r
+          <UML:Classifier.feature>\r
+            <UML:Attribute xmi.id = '9f3e95:f83c9ca08d:-7ff4' name = 'address' visibility = 'public'\r
+              isSpecification = 'false' ownerScope = 'instance'>\r
+              <UML:StructuralFeature.type>\r
+                <UML:DataType xmi.idref = '24d0fa:f8019a3e88:-7f93'/>\r
+              </UML:StructuralFeature.type>\r
+            </UML:Attribute>\r
+            <UML:Attribute xmi.id = '9f3e95:f83c9ca08d:-7ff3' name = 'telephone' visibility = 'public'\r
+              isSpecification = 'false' ownerScope = 'instance'>\r
+              <UML:StructuralFeature.type>\r
+                <UML:DataType xmi.idref = '24d0fa:f8019a3e88:-7f93'/>\r
+              </UML:StructuralFeature.type>\r
+            </UML:Attribute>\r
+          </UML:Classifier.feature>\r
+        </UML:Class>\r
+        <UML:Association xmi.id = '9f3e95:f83c9ca08d:-7ff2' isSpecification = 'false'\r
+          isRoot = 'false' isLeaf = 'false' isAbstract = 'false'>\r
+          <UML:Association.connection>\r
+            <UML:AssociationEnd xmi.id = '9f3e95:f83c9ca08d:-7ff1' visibility = 'public'\r
+              isSpecification = 'false' isNavigable = 'true' ordering = 'unordered' aggregation = 'none'\r
+              targetScope = 'instance' changeability = 'changeable'>\r
+              <UML:AssociationEnd.multiplicity>\r
+                <UML:Multiplicity xmi.id = '9f3e95:f83c9ca08d:-7ff0'>\r
+                  <UML:Multiplicity.range>\r
+                    <UML:MultiplicityRange xmi.id = '9f3e95:f83c9ca08d:-7fef' lower = '0' upper = '-1'/>\r
+                  </UML:Multiplicity.range>\r
+                </UML:Multiplicity>\r
+              </UML:AssociationEnd.multiplicity>\r
+              <UML:AssociationEnd.participant>\r
+                <UML:Class xmi.idref = '9f3e95:f83c9ca08d:-7ff5'/>\r
+              </UML:AssociationEnd.participant>\r
+            </UML:AssociationEnd>\r
+            <UML:AssociationEnd xmi.id = '9f3e95:f83c9ca08d:-7fee' name = '' visibility = 'public'\r
+              isSpecification = 'false' isNavigable = 'true' ordering = 'unordered' aggregation = 'none'\r
+              targetScope = 'instance' changeability = 'changeable'>\r
+              <UML:AssociationEnd.multiplicity>\r
+                <UML:Multiplicity xmi.id = '9f3e95:f83c9ca08d:-7fed'>\r
+                  <UML:Multiplicity.range>\r
+                    <UML:MultiplicityRange xmi.id = '9f3e95:f83c9ca08d:-7fec' lower = '1' upper = '-1'/>\r
+                  </UML:Multiplicity.range>\r
+                </UML:Multiplicity>\r
+              </UML:AssociationEnd.multiplicity>\r
+              <UML:AssociationEnd.participant>\r
+                <UML:Class xmi.idref = '5539d8:f7b62bc3a2:-7fe7'/>\r
+              </UML:AssociationEnd.participant>\r
+            </UML:AssociationEnd>\r
+          </UML:Association.connection>\r
+        </UML:Association>\r
       </UML:Namespace.ownedElement>\r
     </UML:Model>\r
     <UML:TagDefinition xmi.id = '1f5eb7f:f7bb15dc4a:-7ffa' name = 'size' isSpecification = 'false'\r