内容


使用转换配置层级结构

通过标记转换配置层级结构来优化过程

Comments

引言

本文的写作目的,是向您展示怎样为包含在 IBM®Rational®Software Architect 中的转换框架使用转换配置继承性。转换配置继承性是在 Rational Software Architect 7.5.2 版本中引入的。在 Rational Software Architect 7.5.3 版本中又引入了一些其他的升级工具。您必须安装 Software Architect 7.5.3,以使用本文中所描述的所有功能,但是其中有些功能也可以从 Rational Software Architect 7.5.2 版本中获得。

本文概述了转换配置继承性是如何在转换框架中执行的,并描述了您将会使用到的程序编程界面(API)。注意在 Software Architect 7.5.3 版本中,并没有转换配置之间的管理继承性。您可以使用转换配置框架中的 API 来完成这项工作。

在本文的开始处,将会展现关于转换配置的一些环境信息,并解释了它们是如果改进的以支持继承性的。如果有读者只是想简单地知道怎样使用继承性的人,可以直接跳到“管理继承性的 API”部分。

转换配置与环境

一个转换配置包含了转换在运行时所使用到的所有属性。每一个属性都有一个 ID,它是一个字符串,而某个值可以是任意的对象。属性的范例可以是源和目标。为了存储属性转换配置会使用一个转换配置。尽管环境要比 java.util.Map 更加复杂,您还是可以想到它,因为它的主要功能是存储属性 ID 和值的密钥/值。

转换环境从它的第一个版本起就位于一个框架中,并且只用于运行一次转换。环境是层级结构的。在您运行转换并使用转换的元素时,转换中的每一个提取器都会创建一个子环境。

在 Rational Software Architect 的 7.0 版本中,会引入转换配置文件,这样属性值就可以在没有运行转换时维护了。转换环境就是完成这项任务的一个很好的工具。因此,每一个转换配置都包含了一个转换环境,而每一个转换环境又包含了所有的属性值。

因为转换环境已经可以支持层级结构,所以当需要转换配置继承性时,就可以使用 System Architect 7.5.2 版本中转换配置之间的继承性了。

什么时候才应该使用继承性

假设您拥有多个转换配置,它们对一个或者多个属性拥有相同的值。当这些属性中有一个需要更改时,那么您可以需要将它们全部改变。没有继承性的帮助,您需要手动地将 它们逐次更改。但是如果您使用了继承性,那么您就可以在一个所有子类共享的父类中进行定义了。当父类中的属性发生变动时,所有的子类都会自动随之而变化。

例如,假设多个转换配置使用的转换支持在静声模式下运行。有一个拜伦逻辑值就是指定是否需要使用静声模式的。您可以创建一个父类的转换配置,它将属性设置为真,则会让所有的子类都静声地运行。

转换配置层级结构

假设您需要创建转换配置之间的层级结构关系,如下面所示。

图 1. 转换配置层级结构的一个范例
显示三种层次的层级结构的图
显示三种层次的层级结构的图

转换配置层级结构定义了一个属性值的查找顺序。如果 IS_SILENT 属性的属性值需要 TransformationConfigurationD,那么它会首先查找自己的表格。如果不能在表中找到,那么就会查询它的第一个父类,也就是 TransformationConfigurationB。TransformationConfigurationB 使用相同的运算规则,所以如果没有在本地上定义 IS_SILENT,那么它就会查询它的第一个父类,TransformationConfigurationA。TransformationConfigurationA 并没有一个父类,所以如果没有在本地上定义 IS_SILENT,那么查找运算规则就会回到层级结构上。TransformationConfigurationB 只有一个父类,所以我们可以直接跳到 TransformationConfigurationD,它有第二个父类,也就是 TransformationConfigurationC。属性值需要 TransformationConfigurationC。如果它得到了定义,那么如上所示,就会返回值,否则就会返回 null。

简要来说,查找的顺序依次是 TransformationConfigurationD,TransformationConfigurationB,TransformationConfigurationA,最后是 TransformationConfigurationC。只有有一个转换配置有一个 IS_SILENT 属性,那么查找就会结束并将改值返回。所以如果 TransformationConfigurationB 定义了属性,那么就不会再去查找 TransformationConfigurationA 与 TransformationConfigurationC 了。

转换环境层级结构

在转换配置与转换环境之间有一对一的映射关系。因此,您可能会转换环境的层级结构与转换配置的层级结构相类似。实际上并不是这样,主要是因为:转换环境并不像转换配置那样可以支持多个继承性。

通过在查找属性值顺序的基础之上创建一个层级结构,转换配置可以克服这种缺陷。当一个转换配置得到实例化之后,相关的转换环境会随着合适的父类集一起创建。使用前面部分中所举的例子,转换环境层级结构如下所示:

图 2. 转换环境层级结构的范例
四阶转换环境层级结构

共享父类

假设您有一个这样的转换配置继承性结构:

图 3. 共享父类的转换配置继承性
带有普通父类的转换配置
带有普通父类的转换配置

开始时这样做可能看起来没问题,那是实际上并不是这样,因为它可能会导致产生一个重复性很强的转换环境层级结构。下面是 TransformationConfigurationD 与 TransformationConfigurationE 的转换环境层级结构:

图 4. 相应的转换环境层级结构
转换环境层级结构
图 4. 相应的转换环境层级结构
转换环境层级结构

问题是上面的两个 TransformationContextAs 都是相同的实例,这意味着 TransformationContextA 一直是 TransformationContextC 的子类。TransformationContextC 一般并不位于 TransformationContextD 的层级结构中,但是您需要让其位于层级结构中,以满足 TransformationContextE 的需要。

对这个问题的解决方案是拥有 TransformationContextA 的两个实例。一个事例没有父类,并在 TransformationContextD 的层级结构中使用。另外一个事例是 TransformationContextC 的子类,并用于 TransformationContextE 的层级结构中。这种情况不是理想的,因为如果您已经有了一个转换环境的实例,那么创建另外一个实例看起来就很怪了。尽管如此,您需要避免偶然地向层级结构引入意料之外的转换环境。

管理继承性的 API

让我们查看一下新的 API,然后接下来是一些范例代码。转换框架的公用 API 会添加一个界面,叫做 IInheritingTransformConfig。您可以在 com.ibm.xtools.transform.core.config 包的 com.ibm.xtools.transform.core 插件中找到它。下面是一些对其方法的描述:

表 1. InheritingTransformConfig 的方法
方法名描述
addParent(ITransformConfig)向父类的结尾添加给定的转换配置。
addParent(ITransformConfig, int)在指定的索引中向父类的列表中添加给定的转换配置。如果索引要比零还小,那么在列表的开始处将会添加转换配置。如果它要比列表的规模大,那么它就会出现了。
getParents()返回父类转换配置列表的拷贝。这对于决定将新的父类插入到列表中的什么地方十分有用。
isParent()如果转换配置已经是另一个转换配置的父类,则会返回真。转换配置的实例不能用作多个转换配置的父类。尅有在调用 addParent 方法之前使用这种方法,以决定转换配置是否适用于某个转换配置。
removeParent(ITransformConfig)从父类的列表中删除给定的转换配置。

使用继承性 API

下面是一些范例代码,它们演示了可以使用 API 的方式。在这个范例中,childConfigFile 与 parentConfigFile 是 IFile 的实例,它引用了父类与子类转换配置的文件。

 ITransformConfig parent = null;
   IInheritingTransformConfig child = null;
        
   try {
      parent= TransformConfigUtil.loadConfiguration(parentConfigFile);
      child = (IInheritingTransformConfig)
               TransformConfigUtil.loadConfiguration(childConfigFile); 
   }
   catch (IOException ioEx) {
      // Could not read from one or more of the files.
   }

   if (parent != null && child != null) {
      // Set the property value in the parent.
      parent.getSavedContext().setPropertyValue("X", "ABC");

      // propertyValue will be null because it is set in the parent but
      // the inheritance has not yet been established.
      Object propertyValue = child.getSavedContext().getPropertyValue("X");

      // Now create the inheritance and ask for the property value again.
      child.addParent(parent);
      propertyValue = child.getSavedContext().getPropertyValue("X");
      // propertyValue will now be "ABC".
   }

这个例子向您展示了,属性值是如何继承自父类转换配置的。您需要注意的一个重要的事情是,当您在载入子转换配置时,loadConfiguration 方法返回的值,会传递给 IInheritingTransformConfig。这样做很安全,因为实现转换配置的内部类也会实现 IInheritingTransformConfig。因为大多数的 TransformConfigUtil 已经定义为返回 ITransformConfig,所以不能在不损坏 API 的前提下将它们更改为返回 IInheritingTransformConfig。

让我们查看一个更加复杂的例子。在转换配置实例的上面,您已经知道了父类并没有一个子类,所以您可以安全地调用 addParent(ITransformConfig) 方法。记住在本文的前面,转换配置的实例只能有一个子类。拥有多个子类可能会偶然地向层级结构中引入转换配置。下面是一个范例,向您展示怎样去处理这种场景:

 public void addParent(
    		IInheritingTransformConfig parent,
    		IInheritingTransformConfig childA,
    		IInheritingTransformConfig childB) {
 
    	// If parent is already a parent of another transformation
    	// configuration we must create a copy of it.
    	if (parent.isParent()) {
    		childA.addParent(TransformConfigUtil.copyConfig(parent));
    	}
    	else {
    		childA.addParent(parent);
    	}

    	childB.addParent(TransformConfigUtil.copyConfig(parent));
    }

在这个例子中,在使父类转换配置成为第一个子类的父类之前,我们必须检查以确定,它是否已经是另一个转换配置的父类。如果是,我们就要实现父类转换配置的另一个事例,并将新事例作为第一个子类的父类添加。对于第二个事例,我们知道父类转换配置已经是一个父类,所以我们必须创建一个新事例。

管理本地属性的 API

ITransformContext 的 getPropertyValue(String) 方法并不是指示,返回的属性值是否会存储在转换环境或者父类转换环境中。如果这对您很重要,那么界面 ITransformationConfigurationContext 提供的 API 可以起很大的帮助作用。转换框架提供的转换环境的内部实现使用的就是这个界面。因此,除非您实现的是自己的转换环境,对该界面使用 ITransformContext 的实例也是很安全的。在调用 ITransformConfig.getSavedContext() 方法获取转换配置的转换环境之后,您很有可能会执行该操作。接下来是对 ITransformationConfigurationContext 界面的描述,以及它可以怎样使用的范例。

表 2. ITransformationConfigurationContext 的方法
方法名描述
getLocalPropertyIds(boolean) 返回所有属性的 ID,这些 ID 会在转换环境中得到清晰的设置。这意味着来自父类转换环境或者转换描述器的 ID 就不会返回了。

在转换环境中设置的转换内核会管理一些属性。如果您想将一些 ID 排除在返回的 ID 值之外,那么您可以将拜伦逻辑值参数设置为否。
getLocalPropertyValue(String) 返回属性的值,这些属性的 ID 在转换环境中得到了清晰的定义。如果没有在转换环境中设置属性值(属性值来自父类转换环境或者转换描述器),那么将会返回 null。
removePropertyValue(String) 从那些带有给定 ID 属性的转换属性中删除属性值。如果属性值在父类转换环境或者转换描述器中得到了定义,那么调用这个方法不会起任何作用。举个例子,当您调用方法时,可能您不想使用子转换环境中的值,并开始从父类中使用值。

使用用户界面中的继承性

如果与转换一起使用的层级结构转换配置,对新的转换配置向导和编辑器添加了页面,您需要变动这些页面以支持继承性。

转换用户界面允许转换作者向新转换配置向导和转换配置编辑器添加页面。如果您想这样做,那么您必须创建一个 AbstractTransformConfigTab 的子类,并执行一些它的方法。您需要对用户需要在什么时候执行更改负责,这样活性转换配置就可以得到更改了。一般来说,这是通过从用户界面控件上的更改监视器调用 setDirty() 方法而实现的。这种方法对更新转换配置负责,用户界面上已经显示了关于该转换配置的信息。

这个工作流程运转良好,除了需要转换配置继承性的情况之外。populateContext(ITransformContext) 方法一般会为出现在用户界面中的所有属性都设置一个值。因此,当用户更改任意的单个属性值时,所有的属性值都应该在转换配置中进行设置。

解决这个问题的方案,是添加一个带有 String 参数的 setDirty 方法的新变量。这个参数是已经变动属性的 ID。您不应该调用 setDirty() 方法,而是调用能够通过更改属性 ID 的 setDirty(String) 方法。在 populateContext(ITransformContext) 方法中,您需要决定哪些属性值应该更改,并只更改这些属性值。您可以通过调用 getChangedPropertyId(ITransformContext) 方法来完成这项工作。它将会返回发送至 setDirty(String) 的 ID。

下面是 AbstractTransformConfigTab 相关方法应该如何实现的一个范例:

public Control createContents(Composite parent) {
	Composite contents = new Composite(parent, SWT.None);
	contents.setLayout(new GridLayout());
	text = new Text(contents, SWT.None);
	text.addModifyListener(new ModifyListener() {
		public void modifyText(ModifyEvent e) {
			setDirty("MyPropertyId");
		}
	});
		
	return contents;
}

public void populateContext(ITransformContext context) {
	String id = getChangedPropertyId(context);
	if ("MyPropertyId".equals(id)) {
		context.setPropertyValue("MyPropertyId", text.getText());
	}
}

总结

现在您已经完成了上文中所总结的内容,也就可以创建转换配置之间的继承性了,并为支持继承性的转换实现一个用户界面。有了这些操作技巧,您的过程能够得到很大的优化,而您的团队就可以利用 Rational System Architect 环境中的自动化方案了,以帮助您找到更加有效的开发方法。


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational
ArticleID=631331
ArticleTitle=使用转换配置层级结构
publish-date=04152010