内容


为 Rational Software Architect 开发一种语言特性

Comments

介绍

这篇文章将会指导您开发一个IBM® Rational® Software Architect (RSA)的语言特性,它可以提供一个建模环境以及代码转换工具,从而在UML模型生成用 Objective-C [1]的代码。Objective-C起源于C语言,但是添加了面向对象编程结构;虽然C++有更广泛的应用,但是Objective-C的发展和C++是平行的关系。无论如何,这种语言还存在,并且Apple Mac OS X操作系统的核心API都是用Objective-C开发的。使用这种操作系统的开发人员非常熟悉诸如Cocoa[2]这样的框架。

文章中提到的插件(请到下载部分获取资源)提供了以下内容:

  • 一个UML到Objective-C的转换,使用RSA Transformation Framework和Java™ Emitter Templates (JET) [3]
  • 为Objective-C建模提供一个UML 概要文件(profile)
  • 为Objective-C数据类型提供一个Data Type模型
  • 一个带有被应用的概要文件和被导入的数据类型模型的Template模型
  • 一个样例模型

这个特性的结构如图1所示。当前它由3个插件组成,下面章节将会详细介绍这3个插件。

图1:特性的插件
The plug-ins
The plug-ins

注意:为了减小图片的大小,图片名称开始的前缀"com.ibm.xtools"都被除去了。

Objective-C的建模

当使用UML为某种语言建模时,UML总会有比目标语言更加受限或者开放的地方。在基于C的语言中,例如,UML构架中缺少到指针类型的映射,是一个很大的问题。使用Objective-C建模时,您必须查看一下该语言的特性,例如:

  1. Objective-C只支持类之间的单一继承,而UML支持多重继承。
  2. 基于C的Objective-C使用指针类型,UML中则没有。
  3. 这种语言包含一种结构(protocol),它和Java或者C#中的接口概念类似。一个类实现一个协议。
  4. 这种语言包含一种结构(category),它允许程序员给一个已经存在的类添加新的方法;UML中则没有这种功能。
    • 类别只提供操作,不提供属性。
    • 类别不支持接口,也可能不实现协议。
  5. 一个类的属性只是基于实例的(不是基于类的)。属性包括publicprivate或者protected
  6. 一个类的操作即可以基于实例又可以基于类。操作都是public类型。
  7. 当定义操作参数时,除了用于实现的名称,每一参数都有一个用于调用的名称。UML中则不提供。

在Objective-C建模时,您需要一个概要文件(定义那些在UML中不存在的语言元素)以及一些约束(限制UML匹配语言的能力)的合并。这种概要文件如下表所示:

表1:Objective-C如何建模
名称类型属性描述
categoryClassNone这种原型常被应用于一个类中,让其作为一个类别而不是一个类。类别可能不包含任何属性,所有操作都是public
frameworkPackageNone一个framework 是报头包的集合,并被配置为一个单元。请注意Objective-C 概要文件不包含这个原型。您将会在Basic 概要文件中复用现有的原型。
protocolInterfaceNone这个原型表示一个接口被当作一个协议定义来处理。这个原型是可选的,所有接口都将被当作协议。
attributePropertypointers: String为了以指针形式传递,您需要提供一种方法将指针类型作为这个原型的属性进行捕获。这是自动应用的原型,它会应用到所有模型元素。
classClassNone这个原型被用来提供限制。当它被应用的时候,类的属性可能不是static,但所有操作都是public类型。这是自动应用的原型,它会应用到所有模型元素。
ParameterParametermessageName:String
pointers : String
这个原型的属性是捕获在UML中不被提供的值。这是自动应用的原型,它会应用到所有模型元素。

图2是一个样例模型,它显示了上下文中的公用体结构。类接口AddressModelClass的代码在列表1中:

图2:一个源模型的例子
Source Model
Source Model

注意:NSObject和NSURLClient是Mac OS X Cocoa Framework提供的类,代码如列表1所示:

列表1:AddressModelClass
@interface AddresModelClass : NSObject <NSURLClient>
{
  @public
  NSString* _Street1;
  NSString* _Street2;
  NSString* _City;
  NSString* _State;
  NSString* _PostalCode;
  NSString* _Country;
  @private 
  id _Formatter;
}

/* Class Operations */
- (id)init;
- (id)initWithStreet:(NSString*)Street 
            withCity:(NSString*)City 
           withState:(NSString*)State
      withPostalCode:(NSString*)PostalCode
         withCountry:(NSString*)Country;
- (id)initFromURL:(NSURL*)url;
- (NSString*)formatAddress;

/* Accessors */
- (void)setStreet1:(NSString *)newStreet1;
- (NSString*)Street1; 
- (void)setStreet2:(NSString *)newStreet2;
- (NSString*)Street2;
- (void)setCity:(NSString *)newCity;
- (NSString*)City;
- (void)setState:(NSString *)newState;
- (NSString*)State;
- (void)setPostalCode:(NSString *)newPostalCode;
- (NSString*)PostalCode;
- (void)setCountry:(NSString *)newCountry;
- (NSString*)Country;

/* protocol NSURLClient */
- (void)URL:(NSURL *)sender resourceDataDidBecomeAvailable:(NSData *)newBytes;
- (void)URL:(NSURL *)sender resourceDidFailLoadingWithReason:(NSString *)reason;
- (void)URLResourceDidCancelLoading:(NSURL *)sender;
- (void)URLResourceDidFinishLoading:(NSURL *)sender;
@end

开发建模资产

这一章节将会描述打包和配置建模资产中所用到的插件以支持随后将被讨论的转换。

UML 概要文件和数据类型模型

第一个插件打包了两个资产——前面章节中提到的概要文件和一个数据类型库——它是一个核心类型的模型,经常在Objective-C编程中被用到。图3描述了RSA插件,通过插件的扩展点您可以了解到详细的信息。

图3:您的概要文件插件的从属关系
Dependencies
Dependencies

首先,您需要建立一个新的插件项目,使用标准的Eclipse New Plug-In Project向导将其命名为com.ibm.xtools.sample.profiles.objectivec。请不要选择向导中其余的选项,我们只要建立一个基本的空的 plugin.xml。在产生的项目中,添加两个文件夹:profileslibraries,用于存放插件提供的文件。

图4中所示的概要文件命名为ObjCProfile.epx,并实现了在之前的建模讨论中描述的原型集。

图4:ObjCProfile.epxprofile
ObjCProfile.epx

库的名称是ObjCDataTypes.emx,它提供了从C语言继承来的固有数据类型,和被添加到Objective-C中的数据类型。这个库模型的结构如图5所示。注意Basic 概要文件中的«modelLibrary»原型已经被添加到模型中,以便可以让RSA识别它。

图5:OBJCDataTypes.emx 库
OBJCDataTypes.emx

在您的插件中加入这些资产并使其有效,需要添加插件扩展名来注册数据类型库和概要文件。为这些扩展填写详细信息,生成了如列表2所示的 plugin.xml:

列表2:概要文件和库文件plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin
   id="com.ibm.xtools.sample.profiles.objectivec"
   name="Objective-C Profile Plug-in"
   version="1.0.0"
   provider-name="IBM"
   class="com.ibm.xtools.sample.profiles.objectivec.ObjectivecPlugin">

   <runtime>
      <library name="objc-profile.jar">
         <export name="*"/>
      </library>
   </runtime>

   <requires>
      <import plugin="org.eclipse.ui"/>
      <import plugin="org.eclipse.core.runtime"/>
      <import plugin="com.ibm.xtools.uml2.msl"/>
      <import plugin="com.ibm.xtools.emf.msl"/>
   </requires>

   <!-- First the pathmap entry used in the profile extension -->
   <extension 
      id="com.ibm.xtools.sample.profiles.objectivec.pathmap" 
      name="Objective-C Profile Pathmap" 
      point="com.ibm.xtools.emf.msl.Pathmaps">
      <pathmap 
         name="OBJC_PROFILES" 
	 plugin="com.ibm.xtools.sample.profiles.objectivec" 
	 path=""/>
   </extension>
	
   <!-- Add the required profiles -->	   
   <extension
      id="com.ibm.xtools.sample.profiles.objectivec.profile"
      name="ObjectiveCProfile"
      point="com.ibm.xtools.uml2.msl.UMLProfiles">
      <UMLProfile
         id="com.ibm.xtools.sample.profiles.objectivec.profile"
         name="Objective-C"
         path="pathmap://OBJC_PROFILES/profiles/ObjCProfile.epx"
         required="false"
        visible="true">
      </UMLProfile>
   </extension>

   <!-- Add the library data type model -->
   <extension
      id="objcdatatypes"
      name="Objective-C Data Types Library"
      point="com.ibm.xtools.uml2.msl.UMLLibraries">
      <UMLLibrary
         id="objcdatatypes"
         name="Objective-C Data Types"
         path="pathmap://OBJC_PROFILES/libraries/ObjCDataTypes.uml2"
         required="false"
         visible="true">
      </UMLLibrary>
   </extension>

</plugin>

现在插件已经完成了。

模板和样例模型

用户通过选择File -> New -> UML Model,使用RSA中的模版模型;此动作产生一个对话框,其中列出了你能够选择的已经注册的模版模型。一旦您选择了一个模型,RSA将会在您选择的项目中实例化一份模版的拷贝。样例模型通过RSA样例库(Samples gallery)提供,它是先前样例(AddressModelClass)的完整版。

和图3的情况类似,图6显示了这个插件和RSA以及Eclipse平台的从属关系。再建立一个空的插件,将其命名为com.ibm.xtools.sample.models.objectivec。和之前一样,建立两个文件夹:examplestemplates,用于存放您的内容。

图6:您的模型插件的从属关系
Dependencies2
Dependencies2

为了包含一个模版模型,您首先需要启动一个运行时工作台。这是由于您想要在概要文件插件中让您的模版模型使用概要文件和数据类型库,而这个插件可以通过启动另一个工作台使其处于活动状态。因此,一旦您启动了运行时工作台,建立一个新的模型项目并在其中建立新的空白模型,将其命名为Objective-C Model.emx。RSA将会自动为您打开这个模型。这里的主要活动是将这个模型和您之前建立的概要文件和库文件连接起来。

首先选择模型,打开属性(Properties)视图,在Profiles标签上点击Add按钮。这时会弹出两个选项:一个是已经注册的概要文件,另一个是您可以从文件系统的什么位置选择一个概要文件。现在您的概要文件应该在已经注册的概要文件的列表中,如图7所示:

如果您没有在该列表中看到您的概要文件,那么请回到您的开发工作台,检查控制台是否有错误信息。请确认在您的运行时工作台配置中选中了您的插件。

图7:添加配置概要文件
Deployed Profile
Deployed Profile

下一步将要包括数据类型库。右键点击模型然后选择Import Model Library。这时您的库模型将会以已经注册的形式列在表中,如图8所示:

图8:添加Deployed Model Library
Deployed Model
Deployed Model

在保存和关闭模型之前,请先建立一个新的包,命名为MyFramework,然后把原型 «framework» 从Basic 概要文件应用到新的包中。现在您的模型应该和图9中的例子相似:

图9:模板模型结构
Template Model
Template Model

最后您需要将模型作为模板打包到您的插件中。为了完成这个工作,您首先要在开发工作台的模版文件夹中建立一个.ve文件,该文件内容如列表3所示。然后您需要将刚刚建立好的模型添加到这个文件夹中,同时添加 一个在 New UML Model对话框中显示的图标(这里我借用了RSA中标准的可视化模型图标)。这使得模版的开发更加完善。

列表3:.ve文件
#
# START NON-TRANSLATABLE
#
id=com.ibm.xtools.sample.models.objectivec.template
templateFile=Objective-C Model.emx
icon=Objective-C Model.gif
#
# END NON-TRANSLATABLE
#

name=Objective-C Model
description=Create a new Model for designing frameworks and applications in Objective-C.

通过再一次打开运行时工作台,并复制您刚刚创建的模版模型,创建一个样本模型。这将生成一个正确的开始点,所有必要的模型都被连接到了此样本模型。在这个例子中我们重命名框架包,并且添加一系列的类,以及协议和类别的原型。如果您觉得您的模型很吸引人,很有用处,那么请尽可能的在其中添加注释,这样可以帮助人们理解您的模型,并使用它。

您还需要一个简单的HTML文件(overview_simple.html)来描述您的样本,样例库中将包含这个文件。这个文件是标准的HTML;可是Install the Sample 操作还需要回调到RSA中去执行安装。如列表4所示(请注意为了便于清晰的查看,文本已经被重新格式化,因此请不要重新使用这些文本)。注意,liveAction函数可以确定您将要在plug-in.xml文件中定义的向导的名称。您必须正确的进行设置。

列表4:样例库中的 Live actions
<a href="#" 
   onclick='liveAction("com.ibm.xtools.sample", 
      "com.ibm.xtools.sample.internal.LiveActionDelegate", 
      "com.ibm.xtools.sample.models.objectivec.simplewizard")' >
   <img src="/SampleGallery/topic?file=com.ibm.gallery.common/images\import_obj.gif" 
      border="0" 
      width="16" 
      height="16" 
      alt="Import icon">
</a>
&nbsp;&nbsp;&nbsp;
<a href="#" 
   onclick='liveAction("com.ibm.xtools.sample", 
      "com.ibm.xtools.sample.internal.LiveActionDelegate", 
      "com.ibm.xtools.sample.models.objectivec.simplewizard")' >
   Import the sample
</a>

再一次您需要考虑打包,但是这是一个简单的工作,因为大部分棘手的问题都在plug-in.xml文件中。为了将样例打包到样例库中,您需要为样例模型和Eclipse .project文件建立一个.zip文件。这个项目文件非常简单(如列表5所示)。当您建立好了zip文件后,将其添加到插件项目的样例文件夹中。

列表5:样本项目文件
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
   <name>SampleObjectiveCProject</name>
   <comment></comment>
   <projects>
   </projects>
   <buildSpec>
      <buildCommand>
         <name>com.ibm.sse.model.structuredbuilder</name>
         <arguments></arguments>
      </buildCommand>
   </buildSpec>
   <natures></natures>
</projectDescription>

现在您已经为您的插件准备好了所有必要的资产了,这时您会发现这些插件之间的从属关系需要您首先去开发一个概要文件,然后是模板和样例。您将会在下一个步骤:转换,中看到插件的作用。

最后使用plugin.xml文件将所有资产连接到一起。如列表6所示。模型模版的扩展名很简单:它只能用于识别RSA扫描下的.ve文件目录。另一方面,样例库的扩展名就要复杂的多,它需要在HTML页面和安装向导页面注册。

列表6:模版和样例 plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin
   id="com.ibm.xtools.sample.models.objectivec"
   name="Objective-C Models Plug-in"
   version="1.0.0"
   provider-name="IBM"
   class="com.ibm.xtools.sample.models.objectivec.ObjectivecPlugin">

   <runtime>
      <library name="objc-models.jar">
         <export name="*"/>
      </library>
   </runtime>

   <requires>
      <import plugin="org.eclipse.ui"/>
      <import plugin="org.eclipse.core.runtime"/>
      <import plugin="com.ibm.xtools.modeler.ui.wizards"/>
      <import plugin="com.ibm.xtools.sample"/>
   </requires>

   <!-- Add the model templates -->
   <extension
	  point="com.ibm.xtools.modeler.ui.wizards.template">
	  <directory path="templates"/>
   </extension>

   <!-- Add the example model -->
   <extension
  	  id="umlmodels.samples"
      point="com.ibm.samplegallery.sample">
      <sample
         href="examples/overview_simple.html"
         name="Objective-C Simple Example"
         category="com.ibm.samplegallery.category.technology.umlmodels"
         id="com.ibm.xtools.sample.models.objectivec.overview_simple">
      </sample>	
   </extension>
    
   <extension
	  id="simplewizard"
      point="com.ibm.samplegallery.importWizard">
      <wizard
         banner="com.ibm.xtools.sample.umlmodels/icons/model_wiz.gif"
         name="Sample Objective-C UML Model Project"
         class="com.ibm.xtools.sample.internal.InstallProjectWizard"
         finalPerspective=
         "com.ibm.xtools.modeler.internal.ui.perspectives.ModelingPerspective"
         id="com.ibm.xtools.sample.models.objectivec.simplewizard">
         <projectsetup
            pagetitle="Import Objective-C UML Model Project"
            name="SampleObjectiveCProject"
            label="Project Name:"
	        open="Objective-C Model.emx"
            pagedescription="Create a project with a sample Objective-C UML model.">
            <import
               dest=""
               src="examples/simple.zip">
            </import>
         </projectsetup>
      </wizard>
   </extension>

</plugin>

为了测试您的样例是否被成功添加到样例库中,首先您需要运行一个运行时工作台。一旦工作台启动到Help -> Samples Gallery,然后打开Technology Samples和UML模型文件夹。这时您应该看到Objective-C样例,在阅读完相关的文本后,您可以点击安装的链接。安装链接会调用向导帮助您一步一步建立新项目,并允许您将项目添加到工作台之前重命名项目名称。图10显示了您的样例已经成功地集成到了样例库中。

图10:样例库
Samples
Samples

开发UML到Objective-C的转换程序

转换程序是3个插件中唯一需要您编写代码的插件。代码将会通过UML2 API来读取模型,通过JET技术帮助您编写转换程序以及将文本导出到文件系统资产中。图11显示了插件使用单扩展点的情况。

图11:转换插件的依赖关系
Dependencies3
Dependencies3

创建转换插件

为了创建插件的框架,您需要使用先前两个插件的标准Eclipse插件向导。您需要选择一个内容向导。首先给插件命名为com.ibm.xtools.sample.transform.objectivec,然后转到模版列表。从列表中选择转换插件模版后,跳到下一页面。在New Transformation Provider页面中,保留包的原始名称,改变类的名称为UML2ObjCTransformationProvider,然后转到下一页面。这个页面中需要做一些改动:我们要让Name更具实际意义,并且将其作为Class的前缀名称,最后将Target Model Type改为Resource,如图12所示:

图12:新的转换的属性
Transformation
Transformation

现在让我们转到下一个向导页面,在这个页面中您将要输入新的规则定义。规则用来匹配原模型中的模型元素,并且触发正确的转换。请按照下面的步骤添加新的规则:

  • UML Element Type = Class
  • ID = com.ibm.xtools.sample.transforms.objectivec.transformation.classrule
  • Name = Class Rule
  • Class = UML2ObjCClassRule
  • Package = com.ibm.xtools.sample.transforms.objectivec.transformation.rules
  • UML Element Type = Interface
  • ID = com.ibm.xtools.sample.transforms.objectivec.transformation.interfacerule
  • Name = Interface Rule
  • Class = UML2ObjCInterfaceRule
  • Package = com.ibm.xtools.sample.transforms.objectivec.transformation.rules

我们已经完成了这个向导,代码和插件已经创建好了,生成的plugin.xml内容如列表7所示:

Listing 7. Transformation plugin.xml file
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin
   id="com.ibm.xtools.sample.transforms.objectivec"
   name="Objective-C Transformation Plug-in"
   version="1.0.0"
   provider-name="IBM"
   class="com.ibm.xtools.sample.transforms.objectivec.UML2ObjCPlugin">

   <runtime>
      <library name="objc.jar">
         <export name="*"/>
      </library>
   </runtime>

   <requires>
      <import plugin="org.eclipse.ui"/>
      <import plugin="org.eclipse.core.runtime"/>
      <import plugin="com.ibm.xtools.common.core"/>
      <import plugin="com.ibm.xtools.transform.core"/>
      <import plugin="com.ibm.xtools.transform.uml2"/>
      <import plugin="org.eclipse.uml2"/>
   </requires>

   <extension
      point="com.ibm.xtools.transform.core.transformationProviders">
      <TransformationProvider
         class="com.ibm.xtools.sample.transforms.objectivec.transformationProvider
			 .UML2ObjcCTransformationProvider">
         <Priority
            name="Highest">
         </Priority>
         <Transformation
            version="1.0.0"
            name="UML to Objective-C"
            groupPath="com.ibm.xtools.sample.transforms.objectivec"
            sourceModelType="UML2"
            targetModelType="Resource"
            id="com.ibm.xtools.sample.transforms.objectivec.transformation">
            <Property
               readonly="true"
               name="system.transformation.property"
               value="ClassName=
               com.ibm.xtools.sample.transforms.objectivec.transformationProvider
               .UML2ObjCTransformation;
               IsUMLKind=true;"
               id="system.transformation.property">
            </Property>
         </Transformation>
      </TransformationProvider>
   </extension>

</plugin>

将 JET 添加项目中

下一个步骤是在您的项目中添加JET特性,并且建立JET模版,用来产生原模型。首先,选择File -> New -> Other。从列表中选择Java Emitter Templates文件夹,然后选择 Convert Projects 到JET项目向导。这个向导将会列出您将要选择的转换项目,最后点击Finish。刚才的步骤不仅将JET特性(以及构建器)添加到了您的项目中,同时还在您的项目中建立了一个模版文件夹。最后——在编写您的第一个模版之前——您还需要最后一步配置工作,就是JET将它的原模型写入到什么位置。为了完成这个配置,我们需要选择项目,然后打开其属性,选择JET设置,这时请确保Source Container被配置成了src,如图13所示:

图13:配置JET设置
JET Settings
JET Settings

现在您可以创建您的模版了!首先需要创建的是JET用来封装每一个被产生的内部Java类的框架文件。请按照下面的步骤做,打开模版文件夹,建立一个新的文本文件,将其命名为JET.skeletonFile -> New -> File),然后在其中添加列表8所示的内容。

列表8:JET.skeleton 文件
/**
 * This class was generated automatically from a JET template.
 * Copyright (c) 2005,2006 IBM Corporation.
 *
 * @author Simon Johnston
 * @generated
 */
public class CLASS {

   /**
    * The body of this method is generated from a JET template and is intended to
    * be called by an RSA Transformation Framework Rule class.
    *  
    * @param argument a Map containing parameters required by the script.
    * @return the text of the expanded template.
    */
   public String generate(Object argument) {
      return "";
   }
}

现在您需要建立的是为UML类产生源代码的模版。这种情况一般发生在两个区域。第一,您将要创建一个新的JET模版,它稍后将会转变成一个Java类,第二,新的插件项目向导中创建的规则将会调用这个JET 生成的类。因此,让我们首先创建一个简单的空模版。创建一个名为ObjCClassHeadTemplate.javajet的文本文件,然后添加列表9中的内容到该文件中。

列表9:ObjCClassHeadTemplate.javajet模版
<%@ jet package="com.ibm.xtools.sample.transforms.objectivec.jetsrc" 
        imports="java.util.* org.eclipse.uml2.*" 
        class="ObjCClassHeadTemplate"
        skeleton="JET.skeleton" 
%>
<%
   /*
    * This template will either generate the header (.h file) or body (.m file)
    * consisting of theObjective-C representation of a given UML Class. The restriction
    * on classes in UML are only that only single inheritance is supported. Also,
    * Objective-C only supports public methods and so private and protected operations
    * in the model will not be generated. Instance variables (attributes/properties)
    * may be public, protected or private but may not be marked static.
    *
    * The UML Class may have the stereotype "category" applied
    * (or a corresponding keyword) in which case the class is restricted also to not
    * provide any additional instance variables.
    */
   final Map parameterMap = (Map) argument;
   if (parameterMap == null) {
      return stringBuffer.toString(); 
   }
%>

正如您看到的那样,当您保存这个文本文件时,一个新的类文件(ObjCClassHeadTemplate.java)就被添加到了jetsrc包中。在您详细察看转换之前,我们先要修正规则类,使其能够调用刚刚产生的类。请打开UML2ObjCClassRule类,然后添加列表10中所示的导入语句。前两个是简单的Java库类,第三个是一个Eclipse类,第四个是刚产生的JET模版类(这时我们先忽略最后一个)。

列表10:导入语句
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFolder;
import com.ibm.xtools.sample.transforms.objectivec.jetsrc.ObjCClassHeadTemplate;
import com.ibm.xtools.sample.transforms.objectivec.utils.*;

使用列表11中的方法代替createTarget方法。请注意这个代码调用了两次模版,并且它使用模版中的一个实用工具类在工作台中建立了一个文件。同时,它还使用了一个Java HashMap类来传递参数到JET模版;在上面的模版中,我们假设Java中的argument参数是一个Map。

列表11:UML2ObjCClassRule.java中的createTarget方法
   public Object createTarget(ITransformContext ruleContext) {
      /*
       * Extract the class and target from the rule context passed to us.
       */ 
      org.eclipse.uml2.Class theClass = (org.eclipse.uml2.Class)ruleContext.getSource();
		
      if (theClass != null) {
         if (ruleContext.getTargetContainer() !=
          null && ruleContext.getTargetContainer() instanceof IFolder) {
            final IFolder folder = (IFolder)ruleContext.getTargetContainer();

            /*
             * Set up the arguments in a map in preparation to create the header file.
             * Generate the header source using the JET template, passing in the map
             * object.
             */ 
            Map map = new HashMap();
            map.put("GEN_CLASS", theClass);
            map.put("GEN_BODY", new Boolean(false));
				
            ObjCClassHeadTemplate template = new ObjCClassHeadTemplate(); 
            String fileContent = template.generate(map);	
            ResourceUtils.createFile(folder, theClass.getName() 
            + ".h", fileContent, this.getProgressMonitor(ruleContext));
	
            /*
             * Set up the arguments in a map in preparation to create the body file.
             * Generate the body source using the JET template,passing in the map object.
             */ 
            map.remove("GEN_BODY");
            map.put("GEN_BODY", new Boolean(true));
				
            fileContent = template.generate(map);
            ResourceUtils.createFile(folder, theClass.getName() 
            + ".m", fileContent, this.getProgressMonitor(ruleContext));
				
            /*
             * Refresh the workspace.
             */
            try {
               folder.refreshLocal(1, this.getProgressMonitor(ruleContext));
            } catch (Exception ex) {
               System.out.println(
				   "ERROR UML2ObjCClassRule.createTarget: 
				   exception refreshing folder.");
               ex.printStackTrace();
            }		
         } else {
            System.out.println(
				"ERROR UML2ObjCClassRule.createTarget: 
				targetContainer is either null or not an IFolder.");
         }
      }		
      return null;
   }

类的转换

转换由两部分组成,一个产生了接口文件(.h),另一个产生了实现文件(.m)。在这篇文章中将不会包括完整的模版资源(您可以在下载资源中找到它),但是它们的逻辑关系是一样的。

  • 输出类头,包括导入语句和注释
  • 如果这是接口部分,那么输出所有类实例的变量
  • 输出所有的方法:
    • 如果这是接口部分,那么只输出定义
    • 如果这是实现部分,那么输出完整的声明和适当位置的方法体(下面的部分)
  • 如果遇到多次属性访问,输出访问器方法
    • 如果这是接口部分,那么只输出访问定义
    • 如果这是实现部分,那么根据标准模式输出完整的声明体(下面的部分)
  • 类的输出完成

专用的方法体

Objective-C中有几个专用的方法,这一点和Java或者C++不同。它们有专有的命名习惯。这些专用的方法包括对象的分配,存储单元分配以及初始化。伴随着这些专有的方法,在Objective-C中还有一些针对访问器的习惯用法。具体情况请看下面的列表12所示:

列表12:类接口的样例
@interface TestClass : NSObject
{
  @private
  NSString *name;
  NSArray *knownAs;
}

+(id)alloc;
-dealloc;
-init;

-(void)setName:(NSString *)newName;
-(NSString*)name;

-(void)addKnownAs:(NSString *)newKnownAs;
-(void)removeKnownAs:(NSString *)oldKnownAs;
-(NSArray *)knownAs;
@end

这个简单的类只有一个变量:name,还有访问器方法以及专有的allocdeallocinit方法(大多数的类不需要实现alloc或者dealloc)。分配和存储单元分配方法与Java/C++中的构造器类似;但是在Objective-C中,分配和初始化是在两个单独的方法(如列表13所示)中的,因此init方法就是设置实例变量默认值,以及其他初始化行为的地方。

列表13:类实现的样例
@implementation TestClass

+(id)alloc
{
  self = [super new];
  return self;
}

-(void)dealloc
{
  [name release];
  [knownAs release];
  [super dealloc];
}

- (id)init
{
  self = [super init];
  if (self) {
    name = @"";
    knownAs = [[NSArray alloc] init];
  }
  return self;
}

-(void)setName:(NSString *)newName
{
  [newName retain];
  [name release];
  name = newName;
}

-(NSString *)name
{ 
  return name; 
}

-(void)addKnownAs:(NSString *)newKnownAs
{
  [knownAs addObject: newKnownAs];
}

-(void)removeKnownAs:(NSString *)oldKnownAs
{
  [knownAs removeObject: oldKnownAs];
}

-(NSArray *)knownAs
{
  return knownAs;
}

Objective-C的访问器要比Java中同是从NSObject发展来的被所有对象实现的参考计数协议复杂的多。在这里我们不需要讨论这个协议,先前的实现方法非常好,并且我们也推荐这么做。

随着这些规则被编码到JET模版中,您已经很好的完成了这些工作,现在您可以测试这些转换了。

运行转换

想要运行转换,首先您要启动一个运行时工作台。一旦工作台运行起来,您就需要一个Objective-C模型。您可以从零开始创建模型,或者选择File -> New通过模版模型创建该模型,或者您也可以使用样例库中的样例模型!模型打开后,您可以通过右键点击一个类或者一个接口或者一个包来调用转换。Transformation菜单将会出现,首次运行该程序您要选择Run Transformation,之后再运行转换程序您可以在第一个菜单中选择它,如图14所示:

图14:Transform菜单
Transform menu
Transform menu

第一次运行转换时,需要设置一些参数。原模型在您右键点击它时已经设置好了,现在您需要选择一个原模型文件创建位置的目标文件夹(如图15所示)。当您点击Run按钮时,转换就开始运行了,它会输出单个资源,您的项目视图将会随之更新。

图15:Transformation 对话框
JET Settings
JET Settings

注意:如果您使用了建模(Modeling)视图,那么这些文件将不会出现在您的项目结构中。如果您想查看这些文件,那么请使用资源(Resource)视图,这些文件将会列在浏览器(Navigator)中。

收尾工作

我们可以很明显的看出上面的模版是不完整的,这篇文章中也没有涉及协议规则或者协议模版的内容。您可以参考转换项目中的资源。除此之外,转换项目还有很多未覆盖的内容没,例如:

  • 类之间的从属关系应该产生#import语句。
  • 在这个例子中,您没有在模型中产生任何文档:或许您应该产生注释。
  • 在RSA插件中有一些关于使用注释的惯例,例如:使用注释,原型化的(或者使用一个关键字)«body» 来捕获方法的主体,它对于这个例子是有帮助的。
  • 在Objective-C中有一个惯例通过在实现文件中定义一个类别为一个类提供了私有的方法;通过使用链式模版这将是相关简单的附加物。
  • Objective-C中的很多框架都提供了一个包装头,它可以导入所有附着在上面的类头;这通过开发一个包规则可以很容易的提供。
  • 最重要的——转换不会执行合并操作,因为一旦您重新运行转换,那么您添加在类中的代码都会被覆盖。

此外,还有很多附加的能力——您应该考虑什么时候需要开发一个语言特性——不仅限于从模型到代码的转换。

  • 如果没有对已有源代码的可视化或者获取模型的 — 并且没有对之前被识别的代码进行合并,就不会有双向工程的概念。
  • 这个转换只接受结构化的元素:从例如状态机或者活动的行为模型中不会发生任何生成的动作。
  • 现在,RSA可以使用C/C++ 编辑器来识别产生出的.h文件,但它至少它还不适用于@interface,@protocol和@end等关键字,.m文件被简单的当做文本文件处理。一个真正的语言特性应该包括一个编辑器——不论是一个现有的还是一个从文本编辑器框架产生的——来支持您的语言。
  • 这里没有做构建的集成,但是一旦您创建了一些源文件,您就想要构建并运行它。那么您就需要添加一个构建器,错误管理和首选项页面。

因此,正如您看到的,我们留给读者充足的练习。

创建特性和更新站点

现在您需要创建一个由三个插件组成的特性。这个过程很简单:首先创建一个新的特性项目(File -> New -> Feature Project),然后设置项目的名称为com.ibm.xtools.sample.language.objectivec,在特性中选择三个被包含的插件。一旦完成向导,您就可以编辑feature.xml文件以及添加一些关于描述等等的信息了。如列表14所示:

列表14:feature.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<feature
   id="com.ibm.xtools.sample.language.objectivec"
   label="com.ibm.xtools.sample.language.objectivec"
   version="1.0.0"
   provider-name="IBM">

   <install-handler/>

   <description>RSA Objective-C Language Modeling</description>

   <copyright>(C) Copyright IBM Corp. 2004.  All Rights Reserved.</copyright>

   <requires>
      <import plugin="org.eclipse.ui"/>
      <import plugin="org.eclipse.core.runtime"/>
      <import plugin="com.ibm.xtools.modeler.ui.wizards"/>
      <import plugin="com.ibm.xtools.sample"/>
      <import plugin="com.ibm.xtools.uml2.msl"/>
      <import plugin="com.ibm.xtools.emf.msl"/>
      <import plugin="com.ibm.xtools.common.core"/>
      <import plugin="com.ibm.xtools.transform.core"/>
      <import plugin="com.ibm.xtools.transform.uml2"/>
      <import plugin="org.eclipse.uml2"/>
   </requires>

   <plugin
      id="com.ibm.xtools.sample.models.objectivec"
      download-size="0"
      install-size="0"
      version="1.0.0"/>

   <plugin
      id="com.ibm.xtools.sample.profiles.objectivec"
      download-size="0"
      install-size="0"
      version="1.0.0"/>

   <plugin
      id="com.ibm.xtools.sample.transforms.objectivec"
      download-size="0"
      install-size="0"
      version="1.0.0"/>

</feature>

最后,您需要创建更新站点项目。这个过程同样的简单;将其命名为com.ibm.xtools.sample.language.objectivec.site,选择包括先前创建的特性。我们可以在站点中添加一个类别,这样用户可以更加容易的进行在配置中添加更新站点的操作。整个过程如列表15所示。为了描述您的特性,您可以将您的站点部署到Web,或者建立一个存档更新站点(这非常简单:将更新站点项目的内容压缩成zip文件,这样用户就可以通过zip文件直接安装站点了)。

列表15:site.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<site>
   <feature 
      url="features/com.ibm.xtools.sample.language.objectivec_1.0.0.jar" 
      id="com.ibm.xtools.sample.language.objectivec" version="1.0.0">
      <category name="Objective-C Language Feature"/>
   </feature>
   <category-def 
      name="Objective-C Language Feature" 
      label="language.objectivec">
      <description>
         Modeling profile, template and example for Objective-C with UML to source 
         transformation.
      </description>
   </category-def>
</site>

总结

总的来说,您通常会看到RSA框架——特别是带有JET的转换框架——为开发语言特性提供了一个极好的基础。您还会发现开发这样一个特性是有价值的,特别是当您需要获得一个合理的模型到代码转换能力时。除此之外,这篇文章还描述了一种方法,通过这种方法我们不仅可以从模型生成完整的编程语言,也完全可能生成部署描述符,配置,数据定义等等。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational
ArticleID=157182
ArticleTitle=为 Rational Software Architect 开发一种语言特性
publish-date=08312006