Saturday, June 23, 2007

Modeling Generics With XML Schema

Are you feeling curious today? I'll bet you are!



With the introduction of generics to Ecore itself we needed to think about keeping our round trip stories complete so there was a little bit of last minute scrambling to complete the support to make Ecore -> XML Schema -> Ecore and Ecore -> Java -> Ecore both continue to be round trips for all the new constructs we've added.

This schema shows that it's now possible to define your complex types as normal, and to specify their type arguments as well, kind of like having your cake and eating it too:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
xmlns:tree="http:/www.example.org/tree"
ecore:nsPrefix="tree"
ecore:package="org.example.tree"
targetNamespace="http:/www.example.org/tree">
<xsd:element name="Node" type="tree:Node"/>
<xsd:complexType name="Node">
<xsd:annotation>
<xsd:appinfo
ecore:key="typeParameters"
source="http://www.eclipse.org/emf/2002/Ecore">
<typeParameter name="T"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element
ecore:keys="label"
ecore:opposite="parent"
ecore:type="tree:Node{T}"
maxOccurs="unbounded"
minOccurs="0"
name="children"
type="tree:Node"/>
</xsd:sequence>
<xsd:attribute
ecore:reference="T"
name="data"
type="xsd:anyURI"/>
<xsd:attribute
name="label"
type="xsd:string"/>
<xsd:attribute
ecore:opposite="children"
ecore:reference="tree:Node{T}"
name="parent"
type="xsd:anyURI"/>
</xsd:complexType>
</xsd:schema>

Notice that we use {} as synonyms for <>, since the later requires the use of entities in the XML representation, which tends to make the results quite unreadable. By hey, is XML actually designed to be human readable, or does it favor the machine? Given that a < within quotes could be handled unescaped by a machine without much challenge and yet must be escape to make it even easier for the machine, I'd argue that XML is sadly twisted in favor of the machine.

Converting this XML Schema to Ecore produces this model, which records as EAnnotations just enough information about the schema so that instances can be serialized to conform to it:



Generating the Java for this Ecore model produces the following, which records as @model annotations just enough information to be able to recover the original Ecore model

package org.example.tree;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;

/**
* @model extendedMetaData="name='Node' kind='elementOnly'"
*/
public interface Node<T> extends EObject
{
/**
* @model opposite="parent" containment="true" keys="label"
* extendedMetaData="kind='element' name='children'"
*/
EList<Node<T>> getChildren();

/**
* @model kind="reference"
* extendedMetaData="kind='attribute' name='data'"
*/
T getData();
void setData(T value);

/**
* @model dataType="org.eclipse.emf.ecore.xml.type.String"
* extendedMetaData="kind='attribute' name='label'"
*/
String getLabel();
void setLabel(String value);

/**
* @model opposite="children" transient="false"
* extendedMetaData="kind='attribute' name='parent'"
*/
Node<T> getParent();
void setParent(Node<T> value);
}


Given the above Java, we can reproduce the originating Ecore and from that we can export the original schema. So in this case, even XML Schema -> Ecore -> Java -> Ecore -> XML Schema is a round trip. The value of this lies in the fact that any of these different forms of the same model can be your starting point and any of the others can be produced from it. Ecore is like the hub in a hub-and-spoke transportation system; every time another two-way spoke is added to the hub, everyone benefits. The UML project provides Ecore importers and exporters to support its own spoke and so the network grows...

It's clear that Ecore, XML Schema, UML, and Java are very different things, but the really useful insight is to see their similarities and then to use those similarities to build tools and runtimes that capitalize on them...

No comments: