pre { 'Running ETL'.println(); var db : new DB!Database; } post { -- Store traceability links in custom model var trace : new Trace!Trace; for (t in transTrace.transformations) { var link : new Trace!TraceLink; link.sources.add(t.source); link.targets := t.targets; link.description := 'Transformed by ' + t.getRule().name; trace.links.add(link); } } -- Transforms a class into a table and -- a primary key column rule Class2Table transform c : OO!Class to t : DB!Table, pk : DB!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 DB!ForeignKey; var childFkCol : new DB!Column; var parentFkCol : DB!Column; var parentTable : DB!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 : OO!Attribute to c : DB!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 : OO!Attribute to t : DB!Table, pkCol : DB!Column, valueCol : DB!Column, fkCol : DB!Column, fk : DB!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 : OO!Reference to fk : DB!ForeignKey, fkCol : DB!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 DB!Table primaryKeyName() : String { return self.name.firstToLowerCase() + 'Id'; } operation OO!Attribute valuesTableName() : String { return self.owner.name + '_' + self.name.firstToUpperCase() + 'Values'; } operation Any toDbType() : String { var mapping : OO2DB!TypeMapping; mapping := OO2DB!TypeMapping.allInstances().select(tm|tm.source = self).first; if (not mapping.isDefined()){ ('Cannot find DB type for OO type ' + self + '. Setting the default.').println(); return OO2DB!TypeMap.allInstances().first().default.target; } else { return mapping.target; } }
@namespace(uri="OO", prefix="OO") package OO; class Model extends Package { } abstract class PackageableElement extends NamedElement { ref Package#contents ~package; } abstract class AnnotatedElement { val Annotation[*] annotations; } class Annotation { attr String key; attr String value; } abstract class NamedElement extends AnnotatedElement { attr String name; } class Package extends PackageableElement { val PackageableElement[*]#~package contents; } abstract class ~Classifier extends PackageableElement { } class ExternalClass extends ~Class { } class ~Class extends ~Classifier { ref ~Class#extendedBy ~extends; ref ~Class[*]#~extends extendedBy; val Feature[*]#owner features; attr Boolean isAbstract; } class Datatype extends ~Classifier { } abstract class Feature extends NamedElement { ref ~Class#features owner; ref ~Classifier type; attr VisibilityEnum visibility; } abstract class StructuralFeature extends Feature { attr Boolean isMany; } class Operation extends Feature { val Parameter[*]#owner parameters; } class Parameter extends NamedElement { ref ~Classifier type; ref Operation#parameters owner; } class Reference extends StructuralFeature { } class Attribute extends StructuralFeature { } enum VisibilityEnum { public = 1; private = 2; }
@namespace(uri="DB", prefix="DB") package DB; abstract class NamedElement { attr String name; } class Database { val DatabaseElement[*]#database contents; } abstract class DatabaseElement extends NamedElement { ref Database#contents database; } class Table extends DatabaseElement { val Column[*]#table columns; ref Column[*] primaryKeys; } class Column extends DatabaseElement { ref Table#columns table; attr String type; } class ForeignKey extends DatabaseElement { ref Column parent; ref Column child; attr Boolean isMany; }
There are two ways to get the code of this example:
Once you have checked out/imported the code, to run the example you need to go through the following steps:
In this example, we use the Epsilon Transformation Language to transform a model that conforms to the OO.emf metamodel to a model that conforms to the DB.emf metamodel.
.emf files are Ecore metamodels expressed using the Emfatic textual syntax.
More examples are available in the examples folder of the SVN repository.