Model Transformation In Practice

As part of a submission to the MTIP’05 Workshop at the MoDELS 2005 Conference, authors must implement a
mandatory transformation example using their chosen language/tool.
It’s a variant on the usual class-model to relational-model problem, but
there are several problems:

  1. The specification of the mapping itself is quite convoluted and
    unclear (IMO) although thismay well be an illuminating element of
    real-world-ness.
  2. The class metamodel has no way to specify association/attribute
    multiplicities and the specification does not indicate what they should
    default to.
  3. There is only one sample input/output pair and it doesn’t
    demonstrate most of the interesting (complicated) aspects that a
    transformation should address such as inheritance, class-typed
    attributes, transitive primary-/foreign-key aggregation, etc.
  4. The example itself is inconsistent: the resulting relational model
    allows for the “address” Association to be many-to-many, but restricts
    the “customer” Association to being many-to-one. This fits perfectly
    with the semantics of the domain model (an Order can only be associated
    with a single Customer, but a Customer may have multiple Addresses), but
    this information is neither represented not representable in the input
    model so cannot be reflected in the output model.

Leaving these issues aside (although I am surprised that at this late
date no-one else has noticed/commented AFAIK on this last problem), I
applaud the workshop organisers for taking this approach with the
workshop, but am wondering if/how they intend to verify that proposed
solutions do conform to the supplied specification.

Wed, 10 Aug 2005

Tefkat: Sample Solution

The following is a work-in-progress.
I believe it correctly implements the MTIP’05 specification, but
it is not yet extensively tested.

Note, when applied to the sample input, it does not produce the sample output;
it links all three Columns of the Order Table as primary key Columns, thus
allowing an Order to be associated with several Customers.


TRANSFORMATION mtip05_class_to_relational: class -> relational

IMPORT platform:/resource/com.dstc.tefkat.examples/mtip05/models/class.ecore
IMPORT platform:/resource/com.dstc.tefkat.examples/mtip05/models/rdbms.ecore

CLASS ClsToTbl {
  Class class;
  Table table;
};

CLASS AttrToCol {
  Class class;
  Attribute attr;
  Column col;
};

// ----------------------------------------------------------------------

// Class-typed Attributes and Associations are equivalent
//
PATTERN ClassHasReference(SrcClass, DstClass, RefName)
  WHERE Attrs(SrcClass, DstClass, RefName)
     OR References(SrcClass, DstClass, RefName)
;

PATTERN Attrs(SrcClass, DstClass, RefName)
  FORALL Class SrcClass { attrs: Attribute _ { type: Class DstClass; name: RefName; }; }
;

PATTERN References(SrcClass, DstClass, RefName)
  FORALL Association _ { src: SrcClass; dest: DstClass; name: RefName; }
;

// A Class "has" and Attribute for the purposes of the mapping if
//  1. It is directly owned and a primitive type
//  2. A referenced Class (via an Attribute or an Association) "has" the Attribute
//  3. The Class's children "have" the Attribute
//
PATTERN ClassHasAttr(Class, Attr, Name)
  WHERE ClassHasSimpleAttr(Class, Attr, Name)
     OR ClassHasIncludedAttr(Class, Attr, Name)
     OR ClassChildHasAttr(Class, Attr, Name)
;

PATTERN ClassHasSimpleAttr(Class, Attr, Name)
  FORALL Class Class {
           attrs: Attribute Attr {
             type: PrimitiveDataType _PT;
             name: Name;
           };
         }
;

PATTERN ClassHasIncludedAttr(Class, Attr, Name)
  FORALL Class Class
  WHERE ClassHasReference(Class, Type, RefName)
    AND ClassHasAttr(Type, Attr, AttrName)
    AND IF Type.is_persistent = true
        THEN
          Attr.is_primary = true
        ENDIF
    AND Name = join("_", RefName, AttrName)
;

PATTERN ClassChildHasAttr(Class, Attr, Name)
  FORALL Class SubClass
  WHERE Class = SubClass.parent
    AND ClassHasAttr(SubClass, Attr, Name)
;

PATTERN RootClass(Class, Root)
  WHERE IF Parent = Class.parent
        THEN
          RootClass(Parent, Root)
        ELSE
          Root = Class
        ENDIF
;

// ----------------------------------------------------------------------

// 1. Classes that are marked as persistent in the source model
//    should be transformed into a single table of the same
//    name in the target model.
//
RULE ClassAndTable(C, T)
  FORALL Class C {
    is_persistent: true;
    name: N;
  }
  MAKE Table T {
    name: N;
  }
  LINKING ClsToTbl WITH class = C, table = T
;

// Transitively owned Attributes map to Columns
// If the Attribute is primary, so is the corresponding Column
//
RULE MakeColumns
  WHERE ClassHasAttr(C, A, N)
    AND ClsToTbl LINKS class = C, table = T
  MAKE Column Col FROM col(C, N) {
         name: N;
         type: A.type.name;
  }
  SET T.cols = Col,
      IF A.is_primary = true
      THEN
        SET T.pkey = Col
      ENDIF
  LINKING AttrToCol WITH class = C, attr = A, col = Col
;

// References to persistent Classes result in an FKey to
// the corresponding Table.
//
// Columns corresponding to primary Attributes of the target Class
// constitute the corresponding foreign key columns.
//
RULE MakeFKeys
  WHERE ClassHasReference(Class, TgtClass, RefName)
    AND TgtClass.is_persistent = true
    AND RootClass(Class, SrcClass)
    AND ClsToTbl LINKS class = SrcClass, table = SrcTable
    AND ClsToTbl LINKS class = TgtClass, table = TgtTable
    // now determine the foreign key Columns
    AND AttrToCol LINKS class = SrcClass, attr = Attr, col = Col
    AND Attr.is_primary = true
    AND ClassHasAttr(TgtClass, Attr, _)
  MAKE FKey FKey FROM fkey(SrcTable, TgtTable, RefName) {
         references: TgtTable;
         cols: Col;
       }
       SET SrcTable.fkeys = FKey
;

Post a Comment

You must be logged in to post a comment.