|  | // Transforms an object-oriented model | 
|  | // to a relational database schema | 
|  | pre { | 
|  | var db : new Target!Database; | 
|  | } | 
|  |  | 
|  | // Transforms a class into a table and | 
|  | // a primary key column | 
|  | rule Class2Table | 
|  | transform c : Source!Class | 
|  | to t : Target!Table, pk : Target!Column { | 
|  |  | 
|  | t.name = c.name; | 
|  | t.database = db; | 
|  |  | 
|  | // Fill the details of the primary key | 
|  | // of the table | 
|  | pk.name = t.primaryKeyName(); | 
|  | pk.type = "INT"; | 
|  | t.columns.add(pk); | 
|  | t.primaryKeys.add(pk); | 
|  |  | 
|  | // If the class extends some other class | 
|  | // create a foreign key pointing towards | 
|  | // the primary key of the parent class | 
|  | if (c.`extends`.isDefined()){ | 
|  |  | 
|  | var fk : new Target!ForeignKey; | 
|  | var childFkCol : new Target!Column; | 
|  | var parentFkCol : Target!Column; | 
|  | var parentTable : Target!Table; | 
|  |  | 
|  | parentTable ::= c.`extends`; | 
|  | parentFkCol = parentTable.primaryKeys.first(); | 
|  |  | 
|  | childFkCol.name = parentFkCol.name; | 
|  | childFkCol.type = "INT"; | 
|  | childFkCol.table = t; | 
|  |  | 
|  | fk.database = db; | 
|  | fk.parent = parentFkCol; | 
|  | fk.child = childFkCol; | 
|  | fk.name = c.name + "Extends" + c.`extends`.name; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Transforms a single-valued attribute | 
|  | // to a column | 
|  | rule SingleValuedAttribute2Column | 
|  | transform a : Source!Attribute | 
|  | to c : Target!Column { | 
|  |  | 
|  | guard : not a.isMany | 
|  |  | 
|  | c.name = a.name; | 
|  | c.table ::= a.owner; | 
|  | c.type = a.type.name.toDbType(); | 
|  | } | 
|  |  | 
|  | // Transforms a multi-valued attribute | 
|  | // to a table where its values are stored | 
|  | // and a foreign key | 
|  | rule MultiValuedAttribute2Table | 
|  | transform a : Source!Attribute | 
|  | to t : Target!Table, pkCol : Target!Column, valueCol : Target!Column, | 
|  | fkCol : Target!Column, fk : Target!ForeignKey { | 
|  |  | 
|  | guard : a.isMany | 
|  |  | 
|  | // The table that stores the values | 
|  | // has an "id" column and a "value" column | 
|  | t.name = a.valuesTableName(); | 
|  | t.database = db; | 
|  |  | 
|  | pkCol.name = "id"; | 
|  | pkCol.table = t; | 
|  | pkCol.type = "INT"; | 
|  | valueCol.name = "value"; | 
|  | valueCol.table = t; | 
|  | valueCol.type = a.type.name.toDbType(); | 
|  |  | 
|  | // Another column is added into the table | 
|  | // to link with the "id" column of the | 
|  | // values table | 
|  | fkCol.name = a.name + "Id"; | 
|  | fkCol.table ::= a.owner; | 
|  | fkCol.type = "INT"; | 
|  |  | 
|  | // The foreign key that connects | 
|  | // the two columns is defined | 
|  | fk.parent = pkCol; | 
|  | fk.child = fkCol; | 
|  | fk.database = db; | 
|  | } | 
|  |  | 
|  | // Transforms a referecne into a foreign key | 
|  | rule Reference2ForeignKey | 
|  | transform r : Source!Reference | 
|  | to fk : Target!ForeignKey, fkCol : Target!Column { | 
|  |  | 
|  | fkCol.table ::= r.type; | 
|  | fkCol.name = r.name + "Id"; | 
|  | fkCol.type = "INT"; | 
|  | fk.database = db; | 
|  | fk.parent = r.owner.equivalent().primaryKeys.first(); | 
|  | fk.child = fkCol; | 
|  | fk.name = r.name; | 
|  |  | 
|  | } | 
|  |  | 
|  | operation Target!Table primaryKeyName() : String { | 
|  | return self.name.firstToLowerCase() + "Id"; | 
|  | } | 
|  |  | 
|  | operation Source!Attribute valuesTableName() : String { | 
|  | return self.owner.name + "_" + self.name.firstToUpperCase() + "Values"; | 
|  | } | 
|  |  | 
|  | operation Any toDbType() : String { | 
|  | switch (self) { | 
|  | case "String": return "NVARCHAR"; | 
|  | case "Boolean": return "BIT"; | 
|  | case "Integer": return "INT"; | 
|  | case "Real": return "NUMBER"; | 
|  | default: return "NVARCHAR"; | 
|  | } | 
|  | } |