UML软件工程组织

 

 

在您的开发项目中使用 IBM Rational Software Modeler 和
IBM Rational RequisitePro 可视化跟踪能力
 
2008-05-13 作者:Steven Hovater 来源:IBM
 
本文内容包括:
这篇文章向您展示了如何使用 IBM®Rational®RequisitePro 和 IBM®Rational®Software Modeler 内置的可跟踪性能,用绘图的方式来显示可跟踪性,从需求到模型,到代码。

场景

突然出来这句话:有一个需求变更。您的老板或者您的同事问您:这个变更可能对什么有影响呢?难道不能相当快速而简洁地回答这个问题吗?

从可追溯性角度来看,您正问自己:“在客户/供应商的关系中我充当的究竟是什么角色呢?谁会依赖于我?如果我被替换掉,谁或者又有什么会受到影响呢?”

好消息:您可以通过使用 Rational Software Modeler 内置的可跟踪性性能来回答这些问题。如果您使用 Rational Software Modeler 来建模您的需求,您可以很有效地将需求和模型元素链接起来,然后再读取。

重点关注的问题

回到这个问题:谁会依赖于我?从一个模型元素的角度来看,您可以回答那个问题。现在您应该能够以图形的方式来描绘,或者可视化那个跟踪特性。

您如何在每个领域询问问题?

从一个需求的观点来看,您可以使用 IBM®Rational®RequisitePro (hereafter, RequisitePro) 中的交付功能,然后问:“追溯到这个请求的必要条件是什么?”

从一个建模的观点来看,有许多不同种类的关系十分有趣:所有关系,直接的关系,联合,等等。尽管在这种情况下,您只需要寻找将您的模型元素当作目标之间的直接关系。然而,无论您何时对这些关系有需要时,您还是可以将它扩展开来并寻找这些关系的。

这些领域是怎样配合在一起的呢?

RequisitePro的角度来看,如果您有一个代理需求,那么当链接到一个模型元素时就会在这个需求类型上有一个隐藏的特性,它将与这个模型的 Uniform Resource Identifier (URI) 和这个模型中模型元素的片段 URI 一起组装。

要切记的是,模型是不能分辨关于请求的任何关系的I。那些信息存在于 RequisitePro 模型。

Eclipse 中 JFace Tree Viewer 的回顾

在 Eclipse 中,您可以使用许多不同的可视化技术。其中最简单最有力的是 JFace Tree Viewer。任何您看到 Eclipse 中表达的等级关系时,都会看到一个 Tree Viewer,比如 在IBM®Rational®Project Explorer 中,在 Eclipse 继承等级中,以及其它很多时候。

您已经完成了您在 Tree Viewer 中展示的组件的调节装置。一个树中的最高节点是由 Tree Viewer's Content Provider 得 getElements 程序提供的。子节点是由 Content Provider 的getChildren程序提供的。 The icons and text that are shown in the Tree Viewer 中显示的图标和文本是由 Label Provider 的 getImage 和 getText 程序提供的。

您将怎样去实现它?

到目前为止,在 IBM Rational 的一个产品中(比如, IBM®Rational®Software Architect, Rational Software Modeler,IBM®Rational®Systems Developer 等等--简单地说,我们只需要使用 Rational Software Modeler 来代表这套建模产品继续向前发展)应用这个扩展性最简单的方法是使用一个插件。

您可能已经注意到 Eclipse 工作平台扩展的方式都是通过使用插件。也就是说,如果您想要一个新的菜单,一个新的编辑器,一个新的视图等等,您都需要执行一个插件。

然而,在 Rational Software Modeler 中,这个插件性能可以让您同时执行它自己的和 Eclipse 的 API (只要您不修改这个现存的工作平台)。 您可以通过使用插件迅速测试您的想法。

提示:
由于插件与运行着的 Eclipse 工作平台式相互关联的,因此当您正构建插件时,正如您所想要的那样不需要重新引进一个特殊的运行时间工作台。

更多的好消息:如果您在您的插件设计中十分小心,您在创建插件时将会平衡所有的工作,万一您做出来某种决定,就一直做下去,将您的新的创造添加到您的 Eclipse 工作平台中。

在这个演习中,您将使用一个插件来创建一个 Tree Viewer。一旦启动,它将获取 RequisitePro Requirement Explorer (RequistePro Eclipse集成) 中挑选的请求并在 Tree Viewer 中显示。然后,当您询问这个问题,“谁会依赖于我”时,您将执行 Tree Viewer 的 Content Provider 的这个getChildren程序。对于请求,您将找到跟踪到那些请求的必要条件。

如果您遇到一个模型元素代理请求或者一个直接连接着模型元素(先前描述过它的特性)的请求,您需要将这个模型元素链接到那个代理或者直接请求,然后为那个在直接关系中把模型元素当作供应者的源元素寻找模型元素。 (在某种意义上,这是请求跟踪关系的建模等价物。)

的确,这听起来很复杂,但是如果您一步一步地来,将会变得十分清晰而简单。

创建并配置您的插件项目

  1. 因为您将在 Modeling 透视图中工作,那就要提前将您的屏幕设置为这个透视图并开始,这对您的插件开发很有利。
  2. 同样,打开您的 Requirements Explorer,因为您在开发这个插件时将与 RequisitePro 一起操作。
图 1. 到 Requirement Explorer 的屏幕路径
图 1. 到 Requirement Explorer 的屏幕路径
  1. 将它从您的 Project Explorer 下面拖走,只需最佳编辑空间并给您的桌面一个有序的外观(图 2)。
图 2. Project Explorer 视图
图 2. Project Explorer 视图
  1. 现在,只是为了更好的开发,您将使用这个默认的与 RequisitePro 一起装载的样例需求项目。双击 Open Requirements Project 文件夹并导航道(代表性地):
    C:\Program Files\Rational\RequisitePro\samples\Learning_Project-Use_Cases
  1. 打开 LEARNING - USE CASES.RQS

当您在您的测试需求之间设置可追溯性时,您稍后在在这里将会使用 RequisitePro 工具一起工作。

  1. 创建一个插件(参见图 3)。
图 3. 创建一个新的插件项目
图 3. 创建一个新的插件项目
  1. 将您的新插件项目命名为 TraceView,正如您在图 4中所看到的。
图 4.为您的插件项目命名
图 4.为您的插件项目命名
  1. 点击 Finish

Project Explorer 中,屏幕显示的应该如您在图 5中所看到的那样。

图 5. Updated Project Explorer 视图
图 5. Updated Project Explorer 视图
  1. 现在,修改这个 pluglets.xml 文件并确保您能看到统一建模语言 (UML) 建模 API 和 RequisitePro API。

当您创建完这个项目以后,这个 pluglets.xml 文件中的内容看起来应该如列表 1中的代码一样。

列表 1. pluglets XML 文件的代码
 
                
<?xml version="1.0" encoding="UTF-8"?>
<pluglets>
   <require>
      <import plugin="com.ibm.xtools.pluglets"/>
   </require>
</pluglets>
  1. 现在,添加适当的实用程序(请看列表 2)。
列表 2. 将实用程序添加到 pluglets XML 文件中的代码
 
                
<?xml version="1.0" encoding="UTF-8"?>
<pluglets>
   <require>
      <import plugin="com.ibm.xtools.pluglets"/>
      <import plugin="com.ibm.xtools.modeler.ui"/>
      <import plugin="com.ibm.xtools.reqpro.dataaccess"/>
      <import plugin="com.ibm.xtools.reqpro.ui"/>
   </require>
</pluglets>

创建一个样例插件

现在您已经拥有一个插件项目并且将它配置完全,您已经准备好创建这个插件了。

  1. 您可以使用这个插件向导使它变得更简单(请看图 6)。
图 6. 为创建插件选择向导
图 6. 为创建插件选择向导
  1. 下一步,为这个样例插件选择一个模版(图 7)。
图 7. 选择一个插件模版
图 7. 选择一个插件模版
  1. New Pluglet > Create a pluglet 视图中:
    1. 通过在 Package 栏中输入路径给它一个程序包。
    2. 通过在 SourceName 栏中输 TraceView,为它适当地命名。(请看图 8)。
图 8. 为这个插件键入包信息
图 8. 为这个插件键入包信息

在这个时候,插件并没有起到很大的作用,如您在列表 3中所看到的。

列表 3. 屏幕的输入显示表明这个插件还没有被执行
 
                
package com.ibm.field.traceview;

import com.ibm.xtools.pluglets.Pluglet;

public class TraceView extends Pluglet {

	public void plugletmain(String[] args) {
		out.println(
			"Pluglet \"TraceView\" is not yet implemented.");
	}

}

运行这个插件并对它进行测试

如果之前您还没有运行过插件,那么您可以用以下三种方式中的任意一种来运行:

  • 通过选择插件本身(在这种情况下 TraceView.java
  • 弹出菜单中,选择 Run as,然后选择 Run as pluglet
  • 工具条中(参见图 9)
图 9. 选择您想要运行的插件
图 9. 选择您想要运行的插件

暂停一会,来调用最初的策略:您想要构建一个 Tree Viewer 并与您的插件一起显示。因此, 您现在需要添加一个很小的代码来创建和显示一个新的 Tree Viewer (请看 图 10)。

图 10. 创建一个新的 Tree Viewer 的 Traceview.java 代码的屏幕显示
图 10. 创建一个新的 Tree Viewer 的 Traceview.java 代码的屏幕显示

注意这些类中有些是不可见的。

  1. 调用 Source: Organize Imports 行为,当有提示时确保您选择了这些类中的 SWT definition。(这个行为会在您的插件前端自动安装适当的输入声明。) 也就是说,Point 应该解析为 org.eclipse.swt.graphics.Point,那也是唯一含混不清之处。
  2. 保存并尝试运行这个插件。您应该可以看到图 11所显示的内容。
图 11. 在这个阶段运行插件的结果
图 11. 在这个阶段运行插件的结果
  1. 通过点击屏幕边角的 X 将它关闭,回到插件源代码
  2. 您接下来的步骤是添加 Tree Viewer 到您的插件源代码中,如列表 4所示。
列表 4. 添加 Tree Viewer 到这个插件的源代码
 
                
public void createTreeViewer(Shell shell) {
	final TreeViewer tv = new TreeViewer(shell, 
              SWT.SINGLE | SWT.FULL_SELECTION
		| SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
	Tree tree = ((TreeViewer) tv).getTree();
	// Ensure the tree headers are visible
	tree.setHeaderVisible(true);
	tree.setLinesVisible(true);
	// Add column headers to the tree
	String headers[] = { "Element", "Owner", "Visibility" };
	for (int i = 0; i < headers.length; i++) {
		TreeColumn tc = new TreeColumn(tree, SWT.LEFT);
		tc.setText(headers[i]);
		tc.pack();
		// add some extra space
		tc.setWidth(tc.getWidth() + 100);
	}
}

注意:

  • Tree Viewer 应该解析为 org.eclipse.jface.viewers.TreeViewer
  • Tree 应该解析为 org.eclipse.swt.widgets.Tree
  1. 保存您的插件,再次运行它。您应该可以看到 Trace View Prototype 视图,如图 12所示,但是还没有输入项。
图 12. 运行这个插件之后的 Trace View Prototype 视图
图 12. 运行这个插件之后的 Trace View Prototype 视图
  1. 如果您看到了这个,您一定会做的很好!关闭这个窗口,这样您可以继续。
  2. 您的下一个任务是:通过使用列表 5中的代码来指示 Tree Viewer 关于 ContentLabel 的供应者。
列表 5. 添加 Content 和 Label 供应者信息到 Tree Viewer 代码中
 
                
  public void createTreeViewer(Shell shell) {
	final TreeViewer tv = new TreeViewer(shell, 
            SWT.SINGLE | SWT.FULL_SELECTION
		| SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
	Tree tree = ((TreeViewer) tv).getTree();
	// Ensure the tree headers are visible
	tree.setHeaderVisible(true);
	tree.setLinesVisible(true);
	// Add column headers to the tree
	String headers[] = { "Element", "Owner", "Visibility" };
	for (int i = 0; i < headers.length; i++) {
		TreeColumn tc = new TreeColumn(tree, SWT.LEFT);
		tc.setText(headers[i]);
		tc.pack();
		// add some extra space
		tc.setWidth(tc.getWidth() + 100);
	}

	tv.setContentProvider(new TraceContentProvider());
	tv.setLabelProvider(new TraceLabelProvider());

	tv.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
	tv.setInput(shell);
}

注意 TraceContentProvider 和 TraceLabelProvider 现在并不存在 -- 那是您下一步的任务。

Create the Content Provider class

  1. Project Explorer 中的 com.ibm.field.traceview 包裹中创建一个新的类,并用它执行 org.eclipse.jface.viewers.ITreeContentProvider。 (请看 图 13。)
图 13. 创建 Content Provider
Figure 13. Create the Content Provider

当您完成之后,您应该可以看到一个新的类,TraceContentProvider,附有列表 6中所示的内容。


列表 6. 新 TraceContentProvider 类中的内容
 
                
public class TraceContentProvider implements ITreeContentProvider {
	public Object[] getChildren(Object parentElement) {
		// TODO Auto-generated method stub
		return null;
	}

	public Object getParent(Object element) {
		// TODO Auto-generated method stub
		return null;
	}

	public boolean hasChildren(Object element) {
		// TODO Auto-generated method stub
		return false;
	}

	public Object[] getElements(Object inputElement) {
		// TODO Auto-generated method stub
		return null;
	}

	public void dispose() {
		// TODO Auto-generated method stub
	}

	public void inputChanged(Viewer viewer, 
                     Object oldInput, Object newInput) {
		// TODO Auto-generated method stub
	}
}

按照现在的情况,这个 Content Provider 并没有提供任何事物,但是您要对此进行转换。Content Provider 的 getElements 程序是您要使用它来开始的程序之一,并且您会将从 Requirements Explorer 中选择的需求作为这个树的根元素。

  1. 修改 getElements 程序并表现它,如列表 7所示。
列表 7. 修改 getElements 程序的代码
 
                
public Object[] getElements(Object inputElement) {
		
	// get the selections from the RequirementExplorer
	ISelection selection = RequirementExplorer.getCurrentExplorer()
				.getSelection();

	// the user could have selected other things in the Requirements
	// Explorer - We're only interested in the requirements, so
	// we keep only those
		
	List selections = ((IStructuredSelection) selection).toList();
	List reqs = new ArrayList();
	// keep only the requirements!
	for (Iterator iter = selections.iterator(); iter.hasNext();) {
		Object obj = iter.next();
		if (obj instanceof RpRequirement)
			reqs.add(obj);
	}
	// return the selected requirements
	return reqs.toArray();
}

注意:

  • Iterator 解析为 java.util.Iterator
  • List 解析为 java.util.List

创建 Label Provider 类

现在您已经有了提供的 Tree Viewer 所需的基本内容,是创建 Label Provider 的时候了。

  1. 创建一个可以扩展 Label Provider 和执行 com.ibm.field.traceview 中 ITableLabelProvider 的新类。
  2. 调用它 TraceLabelProvider (请看 图 14)。
图 14. 创建这个 Label Provider
图 14. 创建这个 Label Provider

最初,这个代码看起来应该如列表 8所示。

列表 8. 创建 Label Provider 类的代码
 
                
public class TraceLabelProvider extends LabelProvider implements
		ITableLabelProvider {

	public Image getColumnImage(Object element, int columnIndex) {
		// TODO Auto-generated method stub
		return null;
	}

	public String getColumnText(Object element, int columnIndex) {
		// TODO Auto-generated method stub
		return null;
	}
}
  1. 那是一个很好的开始,但是当为了一个特殊的条目,Tree Viewer 需要栏目文本时,您需要提供一些内容,而不仅仅是一个值。

注意:

  • Content Provider 中,您要提供RpRequirements(请看 列表 9)。因此,至少提供一个值得考虑的框架执行也是很有意义的。
  • Tree Viewer 中,您确实在执行有些开发人员所称的 表格树-- 也就是,一个树,但是它有表格一样的专栏。当您获取这些内容和提供与那个树的栏目中相关的图片和文本时,要认真考虑那些栏目。
列表 9. 在 Content Provider 中提供 RpRequirements 的代码
 
                
public String getColumnText(Object element, int columnIndex) {
	if (element instanceof RpRequirement) {
		RpRequirement req = (RpRequirement) element;			
		switch (columnIndex) {
		case 0:
			return req.getTag();
		case 1:
		case 2:
		default:
			return "";
		}
	}		
	return "";
}

当您改进的时候可以获得一点兴趣。

  1. 保存您所有的操作,选择一对需求,然后再次开始运行这个插件。

您所看到的应该类似于图 5。

图 15. 运行这个插件以后 Updated Trace View Prototype 的结果
图 15. 运行这个插件以后 Updated Trace View Prototype 的结果

现在,您可以充分发挥您的想象力!

Iterate

进度报告
让我们回顾以下您到目前位置所做的一切工作。您创建了一个插件,一个 Tree Viewer, Content Provider 和 Tree Viewer 的 Label Provider,并显示了基本的功能。

您的下一步是添加细节。

  1. 通过这个问题开始,“追溯这个元素的必要条件是什么?”

IBM Rational RequisitePro 工具包括关于 COM API 的文件。您将要对 RequisitePro 工具使用一个未出版的 Java API 来进行您的工作。在很多方面,它与 RequisitePro 方法十分接近。有些工具编辑者使用未出版的 APIs 十分不合意。在这种情况下,您没有别的选择。因此,要仔细考虑,并谨慎地向前进行。

Tip:
然而,要记住的是:如果那个内部界面更改,那么您就要更改您的应用软件。同时也要注意那些界面的使用是基于一个原来的基础的。

这个询问关于追溯它的必要条件的请求的程序性方式可以十分直接地表达,如图 10所示。


列表 10. 决定追溯一个特殊请求必要条件的其它请求的代码
 
                
public static List relatedObjects(RpRequirement rp) {
	List requirements = new ArrayList();
	EList reqts = rp.getFromTraces();
	for (Iterator it = reqts.iterator(); it.hasNext();) {
		RpRelationship rl = (RpRelationship) it.next();
		RpRequirement tr = (RpRequirement) rl.getFromRequirement();
		requirements.add(tr);
	}
	return requirements;
}

它有利于为这些种类的助手程序创建有效的类。

  1. 创建一个新的类,并称它为 TraceUtil。
  2. 然后在上面添加这个程序。
  3. 现在,在您的 Content Provider 的 getChildren 程序中使用它。默认的 getChildren 代码看起来应该如列表 11所示。
列表 11. 默认的 getChildren 代码
 
                
public Object[] getChildren(Object parentElement) {
	// TODO Auto-generated method stub
	return null;
}
  1. 您需要提供一个特定请求的子请求,因为这些是您的树中追溯一个特定请求的必要条件。为了达到这一目的,可以修改 getChildren 代码使它看起来如列表 12所示。
列表 12. 修改这个 getChildren 代码来详细说明下一个层次的目标
 
                
public Object[] getChildren(Object parentElement) {
	List children = new ArrayList();
	if (parentElement instanceof RpRequirement) {
		RpRequirement req = (RpRequirement) parentElement;
		children.addAll(TraceUtil.relatedObjects(req));
	}
	return children.toArray();
}
  1. 现在可以尝试运行这个插件,然后您将看到这个子层次的请求,如图 16 所示。 (也就是所说,只要有实际跟踪被选择请求的必要条件。)
图 16. 跟踪最新的 Trace View Prototype 视图来显示子层次的请求
图 16. 跟踪最新的 Trace View Prototype 视图来显示子层次的请求

在这个示范请求项目中,UC2UC2.2 都能够追溯到 FEAT4

正如您可以尽早取消这里的讨论一样,这有两种可以在 RequisitePro 请求与 Rational Software Modeler 的模型元素之间建立的链接:直接的代理的。两者都包含一些相同的基本信息。那就是,无论在直接的还是模型元素代理请求上,都有一个隐藏的字符串属性,AssociatedElementUri

如果您看到一个与模型元素相关联的特定请求的属性值,您就会看见一个看起来如列表 13所示的值。

列表 13. AssociatedElementUri 属性的屏幕输出

                uml:platform:/resource/UML%20models/TestAbstr.emx>
_5z4KwLx1Edug6OWRp7wLNw?TestAbstr::Marksmanship

这个 URI 不仅仅包括 UML 模型元素,还包括这个模型以及您工作平台中与它相关的路径 -- 还句话说,这个是 URI 是服务于 UML 模型本身的:

platform:/resource/UML%20models/TestAbstr.emx

这个与模型中元素相应的片段是这样的:

_5z4KwLx1Edug6OWRp7wLNw

要找到(即便要)与一个请求相连接的元素,您可以解析那个 URI,然后使用 Rational Software Modeler 的应用程序将这个 URIs 分解为适当的元素。

  1. 在您的有效类中创建一个有利于必要解析工作的新的 助手 功能。(请看 列表 14)。
列表 14. 在有效类中添加一个助手的代码
 
                
private static NamedElement linkedElement(String euri) {
	// bail if there's a problem with the input
	if ((euri == null) || 
                (euri.length() == 0) || 
                (euri.indexOf(">") <0)) {
		return null;
	}
	NamedElement ne = null;

	// split the string on the model/fragment delimiter
	String[] sp1 = euri.split(">");
	String uristring = sp1[0];
	// whack the leading uml:
	uristring = uristring.replaceFirst("uml:", "");
	
	// now get the fragment from the second part 
	String elemURI = sp1[1];
	// split the string on the fragment delimiter
	String[] sp2 = elemURI.split("\\?");
	// we just want the first part (the fragment itself)
	String elemid = sp2[0];

	Model theModel = null;
	try {
		// open the UML model based on the parsed URI 
		URI modelURI = URI.createURI(uristring);
		theModel = UMLModeler.openModel(modelURI);
	} catch (IOException e) {
		e.printStackTrace();
	}
	if (theModel != null) {
		// it should be safe to cast this since RP only links to
		// named elements
ne = (NamedElement)  
              UMLModeler.getUMLHelper().findElementById(
				theModel, elemid);
	}

	return ne;
}

}

注意:
 

  • URI 应该解析为 org.eclipse.emf.common.util.URI
  • Model 应该解析为 org.eclipse.uml2.uml.Model.
  1. 现在您需要从这个请求中获得一个 AssociatedElementUri 特性的值 -- 另一个有效功能(列表 15)。
列表 15. 获得 AssociatedElementUri 值的代码
 
                
public static NamedElement assocElement(RpRequirement rp) {
	RpReqType irqt = rp.getReqType();
	try {
		if (RpReqTypeUtil.attrExists(irqt, "AssociatedElementUri")) 
			String euri = RequirementUtil.getAttrValue(rp,
					"AssociatedElementUri");
			return (linkedElement(euri));
		}
	} catch (RpException e) {
		e.printStackTrace();
	} catch (Exception e) {
		e.printStackTrace();
	}
	return null;
}
  1. 既然您可以询问这个请求,看它是否与一个模型元素相互关联,那么就说明您已经准备好充实这个 TraceUtil.relatedObjects 程序来使用这个新性能了(请看 列表 16)。
列表 16. 完成 TraceUtil.relatedObjects 程序的代码
 
                
public static List relatedObjects(RpRequirement rp) {
	List requirements = new ArrayList();
	// the requirement is "directly linked" instead of a proxy,
	// (such as for a UseCase) then you'll need to navigate through
	// the incoming requirement's AssociatedElementUri property.
	try {
		NamedElement ne = null;
		ne = assocElement(rp);
		if (ne != null) {
			requirements.add(ne);
		} else {
			EList reqts = rp.getFromTraces();
			for (Iterator it = reqts.iterator(); it.hasNext();) {
				RpRelationship rl = (RpRelationship) it.next();
				RpRequirement tr = 
                            (RpRequirement) rl.getFromRequirement();

				NamedElement fromElement = assocElement(tr);
				if (fromElement != null) {
					requirements.add(fromElement);
				} else {
					requirements.add(tr);
				}
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
	return requirements;
}
  1. 然而,在您运行您的 Tree Viewer 之前,您需要更新这个 Label Provider 的 getText 程序,并考虑这个元素应该是一个请求还是一个模型元素(请看列表 17)。
列表 17. 更新这个 Label Provider 的 getText 程序
 
                
public String getColumnText(Object element, int columnIndex) {
	if (element instanceof RpRequirement) {
		RpRequirement req = (RpRequirement) element;			
		switch (columnIndex) {
		case 0:
			return req.getTag();
		case 1:
		case 2:
		default:
			return "";
		}
	}
	if (element instanceof NamedElement) {
		NamedElement ne = (NamedElement) element;
		switch (columnIndex) {
		case 0:
			return ne.getName();
		case 1:
		case 2:
		default:
			return "";
		}
	}
	return "";
}
  1. 此时,带着这些从 Requirement Explorer 中挑选的相同元素一起再次运行这个插件是十分有帮助的。您应该可以看到与以前相同的输出,除非在您的工作平台上它们已经链接到模型元素上。
  2. 然而,在您离开 TraceUtil 程序之前,要添加另外一个有效功能,通过一个直接的关系恢复与一个特定模型元素相关的模型元素列表。(请看列表 18)。

提示:
您可以简单地通过增加这个程序的代码添加您所感兴趣的模型元素关系。

列表 18. 添加一个有效程序来获取相关模型元素
 
                
//	 return the elements related to the element
public static List relatedElements(Element element) {
	List rlist = new ArrayList();
	List targetDRs = element.getTargetDirectedRelationships();
	for (Iterator iter = targetDRs.iterator(); iter.hasNext();) {
		DirectedRelationship dr = 
                  (DirectedRelationship) iter.next();
		rlist.addAll(dr.getSources());
	}
	return rlist;
}

接下来,使用所有的功能再次访问这个 Content Provider。这个树上的特定节点要么是一个请求要么时一个模型元素。您已经在 getChildren 程序中添加了这个代码来显示这个跟踪——从请求开始(只要这个节点是一个请求)。

  1. 当然,这个节点可以是一个模型元素。在那种情况下,当您需要它的子节点时要说明这种可能性。
列表 19. 如果这个节点是一个模型元素就编码来请求子节点
 
                
public Object[] getChildren(Object parentElement) {
	List children = new ArrayList();
	if (parentElement instanceof RpRequirement) {
		RpRequirement req = (RpRequirement) parentElement;
		children.addAll(TraceUtil.relatedObjects(req));
	}
	if (parentElement instanceof NamedElement) {
		NamedElement ne = (NamedElement) parentElement;
		children.addAll(TraceUtil.relatedElements(ne));
	}
	return children.toArray();
}

注意:
添加这个相当不值得。

为 Content 和 Label 供应者添加详细内容

注意这个表格中的每一行,要么是一个请求要么是一个 NamedElement。但是,您可以通过修改这个 Label Provider,充分使用 Tree Viewer 中其它栏。同时还要注意 TraceLabelProviderimplementsITableLabelProvider。因此,当您为每个请求或者 NamedElement 显示您想要的栏的时,这些附加的自变量, columnIndex 就应该开始活动了。这样,在 getColumnText 程序中您就可以看到列表 20中所显示的内容。

列表 20. 注意这个 getColumnText 程序
 
                
if (element instanceof RpRequirement) {
	RpRequirement req = (RpRequirement) element;			
	switch (columnIndex) {
	case 0:
		return req.getTag();
	case 1:
// here’s where we return the information to be shown 
// in the second column
	case 2:
// the information for the third column
	default:
		return "";
	}
}
  1. 对于一个请求,要查找 RequisitePro 项目中的包裹
  2. 对于元素,要查找它们的属主。对于NamedElements,您将会获得它们的可见度级别(公开的,私有的等等),如您在列表 21中所见到的。

列表 21. 询问关于请求和元素的代码

                
public String getColumnText(Object element, int columnIndex) {
	if (element instanceof RpRequirement) {
		RpRequirement req = (RpRequirement) element;			
		switch (columnIndex) {
		case 0:
			return req.getTag();
		case 1:
			RpPackage pkg = req.getPackage();
			if (pkg != null)
				return pkg.getName();
		case 2:
		default:
			return "";
		}
	}
	if (element instanceof NamedElement) {
		NamedElement ne = (NamedElement) element;
		switch (columnIndex) {
		case 0:
			return ne.getName();
		case 1:
			Element owner = ne.getOwner();
			if (owner instanceof NamedElement) {
				NamedElement neo = (NamedElement) owner;
				return neo.getName();
			}
		case 2:
			return ne.getVisibility().toString();
		default:
			return "";
		}
	}
	return "";
}

此时您所拥有的应该完全功能化。(您稍后可以用图片修饰您的显示页面。)

测试您的插件

现在,您可以创建一个充分利用您的新可视化性能的测试案例。这包括创建一个测试情景,可以通过以下几项任务来完成:

  1. 在请求之间创建链接。
  2. 从请求到 UML NamedElement 创建一个直接的链接。
  3. 从请求到 UML NamedElement 创建一个代理链接。
  4. 从其它 NamedElements 中为这个元素创建从属关系。
  5. 为从 NamedElements 到其它元素创建从属关系。
  6. 创建由 UML NamedElement 产生的 Java 代码。

能够浏览这些可追溯性树是非常有意思的事,因为它从请求开始一直到代码。

从请求到代码

  1. 通过使用 RequisitePro 客户端 打开 Learning Project - Use Cases 项目。
  2. 创建一个新的可跟踪性矩阵,建立从 FEAT3 到 FEAT2,从 FEAT2 到 FEAT1 的跟踪性。假设 Eclipse Requirement Explorer 中的 FEAT1 已经被选定,您所期望看到的跟踪性看起来应该是这样的(同样请看图 17):
FEAT1
FEAT2
FEAT3

图 17. RequisitePro 跟踪性视图
图 17. RequisitePro 跟踪性视图

  1. 现在,离开 RequisitePro 客户端,在 Rational Software Modeler 的工作平台中,选择 Requirement Explorer 中的 RequisitePro项目
  2. 右键点击并调用 Refresh 操作。这样迫使 RequisitePro 项目重新与这个项目同步化(请看图 18)。
图 18. Requirement Explorer 视图
图 18. Requirement Explorer 视图

现在,您可以创建一个用于示范的新模型。

  1. 首先,创建一个 UML 项目
  2. 然后创建一个空白模型,并命名为 M1。
图 19. 创建一个新的 UML 项目称作 MI
图 19. 创建一个新的 UML 项目称作 MI
  1. 接下来,在这个模型中创建三个类,将它们与从属关联起来,如图 20所示。
图 20. 创建类和从属关系
图 20. 创建类和从属关系
  1. 现在您将 Class1 与请求 FEAT3 链接起来。要大到这一目的,选择 FEAT3 并将它拖拽到 Class1
  2. 最后,创建一个新的 UML > Java 转换配置,将 Class3 转换为一个 Java 类。(参见图 21)。
图 21. 创建一个转换配置(屏幕路径)
图 21. 创建一个转换配置(屏幕路径)

您应该看到图 22所示的对话框。

图 22. 新的转换框架视图
图 22. 新的转换框架视图
  1. 接下来,设置目的地

图 23. 设置源和目标
图 23. 设置源和目标

  1. 创建一个新的Java 项目作为一个目标集装箱,称它为 J1(图 24)。
图 24. 创建一个新的 Java 项目称作 J1
图 24. 创建一个新的 Java 项目称作 J1
  1. 点击 Finish
  2. 然后在 New Transformation Configuration 对话框中选择 J1 作为目标。
  3. New Transformation Configuration 向导中一直点击 Next,直到您到达 Common 页面(参见图 25),此时您要确保选定 Create Source to Target Relationships
图 25. New Transformation Configuration 向导中的 Common 页面
图 25. New Transformation Configuration 向导中的 Common 页面
  1. 然后,点击 Finish
  2. 您可以通过简单地选择 转换配置和调用 Transform: UML to Java5Option 菜单操作来运行这个转换。

图 26. 运行这个转换的屏幕路径
图 26. 运行这个转换的屏幕路径

注意这个 Java 项目现在有一个新的 Java 类,Class3(参见图 27)。

图 27. 新的 Class 3 Java 类出现位置的屏幕截图
图 27. 新的 Class 3 Java 类出现位置的屏幕截图
  1. 最后,确保您激活了 参数选择 中的 Java 建模(图 28)。

图 28. 激活 Preferences 视图中的 Java 建模
图 28. 激活 Preferences 视图中的 Java 建模

  1. 打开 Advanced 键符,然后确保激活 Java 建模(参见图 29)。
图 29. Preferences 视图中 Capabilities 部分的 Advanced 标签
图 29. Preferences 视图中 Capabilities 部分的 Advanced 标签

到现在为止,您已经构建了一个跟踪性情景,看起来应该如列表 22所示。

列表 22. 跟踪性情景
 
                
  FEAT1

     FEAT2

       FEAT3

          Class1

            Class2

              Class3

                (Java) Class3
  1. 此时应该再次运行您的插件,看看您可以得出什么结果(参见图 30)。
图 30. 更新 Trace View Prototype 视图
图 30. 更新 Trace View Prototype 视图

还过得去!注意上面的图 30,显示的 CLASS7 是代表您的模型中 Class1 的一个代理请求。

  1. 接下来,使用列表 23中的代码执行 Label Provider 来提供图片。
列表 23. 执行 Label Provide 来提供图片
 
                
public Image getColumnImage(Object dataObject, int columnIndex) {
	Image image = null;
	if (dataObject instanceof RpRequirement) {
		RpRequirement req = (RpRequirement) dataObject;
		switch (columnIndex) {
		case 0:
			// is it a proxy or direct link?
			String imageKey;
			NamedElement ne = TraceUtil
					.assocElement(req);
			if (ne == null) {
				// there's no associated element
				imageKey = ISharedImages.IMG_OBJ_FILE;
			} else {
				imageKey = ISharedImages.IMG_OBJ_ELEMENT;
			}
                  return  PlatformUI.getWorkbench().
                     getSharedImages().getImage(imageKey);			
		case 1:
			RpPackage pkg = req.getPackage();
			if (pkg != null) {
				String pkgKey = ISharedImages.IMG_OBJ_FOLDER;
				return PlatformUI.getWorkbench().
                           getSharedImages().getImage(pkgKey);
			}
		case 2:
			return null;
		}
	}
	if (dataObject instanceof NamedElement) {
		NamedElement ne = (NamedElement) dataObject;
		switch (columnIndex) {
case 0:				
	image = IconService.getInstance()
.getIcon(new EObjectAdapter(ne),
IconOptions.GET_STEREOTYPE_IMAGE_FOR_ELEMENT
	.intValue());
	return image;
	
case 1:				
image = IconService.getInstance()
.getIcon
( new EObjectAdapter(ne.getOwner()),
IconOptions.GET_STEREOTYPE_IMAGE_FOR_ELEMENT.                         
intValue());
return image;
		case 2:
			return null;
		default:
			break;
		}
	}
	return image;
}

这使整个图片集中到一点:跟踪性,从请求到模型,到代码(参见图 31)。

图 31. 跟踪性完整的图片
图 31. 跟踪性完整的图片

参考资料

学习 获得产品和技术 讨论
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号