资讯专栏INFORMATION COLUMN

EMF学习笔记(二)——使用EMF编程——开发元数据

Jiavan / 2435人阅读

摘要:使用元数据包中包含了中每一个被建模类对应的接口。任何对象的元数据是使用的实现来表示的。加载模型的序列化形式是个在运行期间获取元数据的有效方法。反射提供一个反射式,可以检查对象的元数据以及一般地访问和操纵数据。

使用元数据

  Java包org.eclipse.emf.ecore中包含了Ecore中每一个被建模类对应的接口。任何EMF对象的元数据是使用Ecore的实现(implementation)来表示的。这些信息在运行期间(runtime)总是可获取的,所以我们可以使用Ecore API来查询模型的结构,还可以使用反射式EObject API访问和操纵实例中的数据。此外,仅通过实例化 Ecore,我们就可以在运行期间动态定义一个模型。
  本章我们仔细研究EMF对象暴露的元数据,并且看看元数据是如何被反射动态EMF机制所利用的。

包(Packages)

  在第10章中,EMF生成了一个包接口,它为定义在包中的类型提供了对元数据的简单访问。
  这个生成的接口仅仅是个便利:它提供的所有访问也可以在基本接口中使用泛型方法(generic methods)。例如,接口EPO2Package,它从ExtendedPO2模型中生成,它提供了有个便捷的方法来访问PurchaseOrder类。我们可以像这样使用它:

EClass poClass = epo2Package.getPurchaseOrder();

  不使用这个生成类,我们可以使用EPackage接口中的getEClassifier方法来简单检索类,如下:

EClass poClass = (EClass)epo2Package.getEClassifier("PurchaseOrder");

  我们可以类似地访问生成的包提供的属性引用以及其他所有内容。
  
  加载模型的序列化形式是个在运行期间获取元数据的有效方法。事实上,这也是在内存创建模型的最初方法。但是,从磁盘中读取,解析XML,构建整个模型是非常昂贵的操作,所以对于给定的模型,我们应该只做一次。
  我们希望可以检查两个EMF对象是否是同一个类的实例。
  包注册表( package registry)提供对含有元数据的包的访问,不管这些包是如何被初始化的。
  使用java.util.Map填充包注册表。键(key)是String类型命名空间URI,EPackage值(value)。一些值也可能是EPackage的实例。
  使用注册表键入的getEPackage()方法来注册生成的包。
  当EMF独立运行时,生成的包通常是在构建的时候注册的,如下:

EPO2Package epo2Package = EPO2Package.eINSTANCE;

  注册包还有其他种通常的机制:URIConvertersxsi:schemaLocation,它们与没有生成代码的模型一起使用。包从它们的序列化形式中加载并自动注册。
  一旦包被注册,就可以在任何需要它的时候轻松访问:在合适的 EPackage.Registry上调用getEPackage()方法。例如,基于名称和包的命名空间URI,使用全局包注册表来获取特定EClassifier,如下:

public static EClassifier getEClassifier(String nsURI, String name)
{
    EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
    return ePackage == null ? null : ePackage.getEClassifier(name);
}

  我们也可以使用这个方法来从ExtendedPO2模型中获取特定的类,如下:

EClassifier poClass = getEClassifier(
"http://www.example.com/epo2.ecore","PurchaseOrder"));

  回顾EPackage.Registry接口,它包含一个getEFactory() 方法,这个方法可以获取已注册的包的工厂(factory)。回顾第五章,每个EPackage总有一个关联的EFactory,这个EFactory可以被用来初始化定义在包中的分类器(classifiers)getEFactory() 方法可以作为获取包以及调用包的getEFactoryInstance() 的一种捷径。
  

反射(reflection)

  EMF提供一个反射式API,可以检查对象的元数据以及一般地访问操纵数据。EMF反射类似于Java反射,但是EMF反射运行于稍微高一点的层次。

创建对象

  EMF对象是使用工厂来创建的。例子如下:

PurchaseOrder order = epo2Factory.createPurchaseOrder();

  生成的工厂扩展了基础的工厂接口,EFactory继承了可以反射式创建对象的通用的create() 方法。这种方法使用一个EClass来指定哪个类应该被实例化:

EClass poClass = ...
EFactory epo2factory = poClass.getEPackage().getEFactoryInstance();
EObject order = epo2factory.create(poClass);

  可以看出创建一个对象,所需要的仅是一个EClass。从其中我们可以轻松地获取它包含的,以及对应的工厂。事实上,有个捷径,我们可以使用一个EMF多功能的方法来实现:

EObject order = EcoreUtil.create(poClass);

  我们如何让一个EClass用于创建对象?根本上,它来自于包。回顾上一章从注册表中访问包。首先,基于命名空间URI检索包,然后从包中获取命名的类,最后,反射地实例化这个类:

public static EObject createEObject(String nsURI, String name)
{
    EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
    EClass eClass = (EClass)ePackage.getEClassifier(name);
    return ePackage.getEFactoryInstance().create(eClass);
}

  我们现在可以创建一个PurchaseOrder类的实例:

EObject order = createEObject("http://www.example.com/epo2.ecore","PurchaseOrder");

  这实际上与EMF的XML资源实现(XML resource implementation)加载过程中必须在基于命名空间限定类型名称来创建对象时非常相似。
  反射的对象创建总是留给我们一个EObject的实例,EMF的用于建模对象的高性能基础接口。接下来,我们就可以使用这个接口来检索对象的元数据以及对其特性(features)的一般性访问

查询和修改对象

  既然我们现在知道了如何去检查元数据以及创建对象,接下来就只需要一种方式来反射地访问属性(attribute)引用(reference)。这就用到了反射的 EObject API
  我们通常访问元数据使用的是EObject的eClass()方法,其返回的是描述对象的EClass。然后我们可以使用元数据,通过反射性eGet(), eSet(), eIsSet(), eUnset() 方法来访问设置对象属性和引用的。这些方法每个都有EStructuralFeature识别将被访问的特性(feature)
  下面是打印出任意一个对象的名称以及所有属性的值的简单方法:

public static void printAttributeValues(EObject object)
{
    EClass eClass = object.eClass();
    System.out.println(eClass.getName());
    for (Iterator iter = eClass.getEAllAttributes().iterator();
    iter.hasNext(); )
    {
        EAttribute attribute = (EAttribute)iter.next();
        Object value = object.eGet(attribute);
        System.out.print(" " + attribute.getName() + ": " + value);
        if (object.eIsSet(attribute))
        {
            System.out.println();
        }
        else
        {
            System.out.println(" (default)");
        }
    }
}

  getEAllAttributes()方法返回所有的属性。对于每个属性,调用反射性eGet()方法来检索属性的值。我们也调用eIsSet()来决定是否将额外的信息“(default)”附加到打印的值后面。对于Item的实例,输出如下:

    Item
    productName: Tire
    quantity: 4
    USPrice: 50
    comment: null (default)
    shipDate: null (default)
    partNum: 622-RT

  接下来看一个修改数据的例子,寻找一个名称为"USPrice"的整型特性,把其现有的值减少10%,如下:

public static void adjustPrice(EObject object)
{
    EStructuralFeature feature =object.eClass().getEStructuralFeature("USPrice");
    if (feature != null &&feature.getEType() == EcorePackage.Literals.EINT)
    {
        int price = ((Integer)object.eGet(feature)).intValue();
        object.eSet(feature, new Integer(price * 9 / 10));
    }
}

  Ecore的EInt数据类型代表了原始(primitive) Java类型int,所以我们仍然必须使用带有eGet()eSet()整型封装类。
  除了值的类型总是EObject之外,在对象中操纵引用(reference)与检索属性值类似。EObject也有其他方法可以帮忙,例如,如果想要寻找对象的容器(container),你就倾向于编写方法findContainer(),它可以遍历所有的对象引用,并检查isContainer(),最后返回第一个非null的值:

public static EObject findContainer(EObject object)
{
    for (Iterator i = object.eClass().getEAllReferences().iterator();i.hasNext(); )
    {
        EReference reference = (EReference)i.next();
        if (reference.isContainer())
        {
            EObject value = (EObject)object.eGet(reference);
            if (value != null)
            {
                return value;
            }
        }
    }
    return null;
}

  EObject总是知道自己的容器,并且通过eContainer()方法使其可用。如果这个方法返回的是null,那么这个对象就没有被包含。
  类似地,你可能想获取某个对象包含的所有对象。当然也可以使用上面类似地方法,然而,有个更便捷的方式来实现:使用EObject API eContents()。它可以返回一个所有对象包含引用的只读列表(list)视图,此外,eCrossReferences() 返回的是非包含的引用的视图。
  我们可以使用eContents()来建立价格调整的例子:

public static void adjustPrices(EObject object)
{
    adjustPrice(object);
    for (Iterator i = object.eContents().iterator(); i.hasNext(); )
    {
        adjustPrices((EObject)i.next());
    }
}

  这个例子使用了递归访问的方法,如果使用eAllContents()就可以消除递归:

public static void adjustPrices(EObject object)
{
    adjustPrice(object);
    for (Iterator i = object.eAllContents(); i.hasNext(); )
    {
        adjustPrice((EObject)i.next()); // No recursion!
    }
}

(可以再补充抽象类相关)

动态EMF(Dynamic EMF)

  生成的代码也可以看作是类型安全(type-safe) API,而反射性EObject API的动态实现( dynamic implementation),可以看做是类型指定(type-specific) API。相比于动态实现,生成的代码要更高效且使用更少的内存,以及提供更快的数据访问。然而,如果你不需要提供一个类型安全API或者修改默认的行为(behavior),动态实现则是更好的选择,它允许模型能被更自由地共享,以及在不维护生成的类的情况下进行演化。这就是动态和静态实现之间的正常权衡(trade-off)
  同时兼得代码生成的高性能和动态EMF的灵活性的一种可能的方法是,在运行期间调用代码生成器,然后动态地加载生成的类。
  (JDT JIT 以及其他未完待续)

扩展的元数据(Extended Metadata)

  (与第8章和第9章相关)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67473.html

相关文章

  • Eclipse Modeling Framework, 2nd Edition. (EMF)学习笔记

    摘要:定义模型元模型用于表示中模型的模型称为。用于表示的类型,它可以是基本类型,例如或对象类型等。此外,因为是货物的容器并会在其中将货物作为孩子序列化,所以需要标识出。 EMF介绍 为了理解EMF究竟是什么,你只需要知道一件事:模型(model)是什么?模型的目的是什么? EMF不要求全新的方法论亦或是任何复杂的建模工具。只需要从Eclipse的Java开发工具着手开始。 EMF将建模概念...

    yagami 评论0 收藏0
  • Eclipse Modeling Framework, 2nd Edition. (EMF)学习笔记

    摘要:定义模型元模型用于表示中模型的模型称为。用于表示的类型,它可以是基本类型,例如或对象类型等。此外,因为是货物的容器并会在其中将货物作为孩子序列化,所以需要标识出。 EMF介绍 为了理解EMF究竟是什么,你只需要知道一件事:模型(model)是什么?模型的目的是什么? EMF不要求全新的方法论亦或是任何复杂的建模工具。只需要从Eclipse的Java开发工具着手开始。 EMF将建模概念...

    yacheng 评论0 收藏0
  • EMF学习笔记(三)——使用EMF编程——持久化

    摘要:生成的包首次被访问时,在全局包注册表中自动地注册。然而,类似于资源工厂注册表,这种显式注册的过程仅当独立运行时被要求,在下运行时通过扩展指针来自动地完成。通过使用合适的资源工厂,就可以确定被产生的和被使用的持久化形式。 持久化(Persistence)   EMF拥有一个强大的模型持久化框架。通过一个高度可定制资源实现(resource implementation)来支持XML序列化...

    villainhr 评论0 收藏0
  • EMF学习笔记(三)——使用EMF编程——持久化

    摘要:生成的包首次被访问时,在全局包注册表中自动地注册。然而,类似于资源工厂注册表,这种显式注册的过程仅当独立运行时被要求,在下运行时通过扩展指针来自动地完成。通过使用合适的资源工厂,就可以确定被产生的和被使用的持久化形式。 持久化(Persistence)   EMF拥有一个强大的模型持久化框架。通过一个高度可定制资源实现(resource implementation)来支持XML序列化...

    helloworldcoding 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<