Visualize traceability in your development projects using IBM Rational Software Modeler and IBM Rational RequisitePro

This article shows you how you can graphically display traceability, from requirements through model to code, using IBM® Rational® RequisitePro and the built-in traceability capabilities of IBM® Rational® Software Modeler.

Share:

Steven Hovater (svhovater@us.ibm.com), IBM Rational Tech Rep, EMC

Steven HovaterSteve is an IBM Rational Lab Services (ISSR) technical representative in the NorthEast. He is a 14 year Rational veteran, and has supported Raytheon, Lockheed-Martin, General Dynamics, BAE Systems, Naval Undersea Warfare Center, among other mil-aero customers over the past 10 years on a variety of products including Rational Apex (Ada), Rational Rose, and most recently Rational Software Architect (RSA). Steve holds a Masters Degree in Physics from the University of Alabama. He has been a developer at Rational, sales technical representative, and is the principal designer and author of the IBM Rational DoDAF plugin. Steve has specialized in the extension of Rational tools to meet customer requirements and drive sales, whether it's writing embedded Ada network drivers, extending Rational Rose, or writing custom Eclipse plugins. He currently resides in Portsmouth, New Hampshire.


developerWorks Contributing author
        level

05 June 2007

Also available in Chinese

Scenario

The word just came down: There's a requirement change. Your boss or your colleagues ask you: What could this possibly affect? Wouldn't it be nice to be able to answer that question fairly quickly and easily?

From a traceability perspective, you're really asking, "Whom am I supplying in a client/supplier relationship? Who depends on me? If I change, who or what could be affected?

Good news: You can answer these questions by using the built-in traceability capabilities of Rational Software Modeler. If you're using Rational Software Modeler to model your requirements, and you're actively linking requirements to model elements, then read on.

Focus on the questions

Back to this question: Who depends on me? From a model element perspective, you can ask that question. From a requirements perspective, you can ask that question. Now you'd like to be able to graphically represent, or visualize, that traceability.

How do you ask the question in each domain?

From a requirements point of view, you can exploit the as-delivered functionality that's in IBM® Rational® RequisitePro (hereafter, RequisitePro) and ask: "What are the requirements that trace to that requirement?"

From a modeling point of view, there are many different kinds of relationships that could be interesting: ownership, directed relationships, associations, and so on. In this case, though, you're going to ask just for the directed relationships that have your model element as the target of that relationship. However, you can also expand this to ask for other relationships whenever you want to do that.

How do the domains tie together?

From the RequisitePro perspective, if you have a proxy requirement, there's a hidden property on the requirement type that when linked to a model element, is populated with the Uniform Resource Identifier (URI) of the model and the fragment URI of the model element in that model.

It's important to remember that the model doesn't recognize anything about relationships to requirements. That information resides in the RequisitePro model.

Review of the JFace Tree Viewer in Eclipse

In Eclipse, you can use many different visualization techniques. One of the simplest and most powerful is the JFace Tree Viewer. You've seen a Tree Viewer whenever you saw a hierarchical relationship expressed in Eclipse, such as in the IBM® Rational® Project Explorer, in the Eclipse inheritance hierarchy, and many other times.

You have complete control of what you show in a Tree Viewer. The top-level nodes in the tree are provided by the Tree Viewer's Content Provider's getElements method. Child nodes are provided by the Content Provider's getChildren method. The icons and text that are shown in the Tree Viewer are provided by the Label Provider's getImage and getText methods.

How are you going to do it?

By far, the simplest way to exercise the extensibility in one of IBM Rational's modeling products (for example, IBM® Rational® Software Architect, Rational Software Modeler, IBM® Rational® Systems Developer, etc.--for simplicity, we will just reference Rational Software Modeler to represent this set of modeling products going forward) is to use a pluglet.

You may already be aware that the way the Eclipse workbench is extended is through the use of plugins. That is, if you want a new menu, a new editor, a new view, and so forth, you need to implement a plug-in.

In Rational Software Modeler, however, the pluglet capability allows you to exercise both its own and Eclipse APIs (as long as you don't try to modify the existing workbench). You can rapidly test your ideas by using pluglets.

Tip:
A pluglet connects to the running Eclipse workbench, so you don't need to bring up a special runtime workbench, as you would if you were building a plug-in.

More good news: If you're careful in the design of your pluglet, you will be able to leverage all of the work that you're doing in creating a plug-in, in case you decide, down the road, to add your new creation to your Eclipse workbench.

In this exercise, you'll use a pluglet to create a Tree Viewer. Upon startup, it will take the selected requirements in the RequisitePro Requirement Explorer (the RequistePro Eclipse integration) and display them in the Tree Viewer. Then, when you're asking the question, "What depends on me," you'll implement the getChildren method of the Tree Viewer's Content Provider. For requirements, you'll find the requirements that trace to those requirements.

If you encounter a model element proxy requirement or a requirement directly linked to a model element (distinction described previously), you'll get the model element linked to that proxy or direct requirement, and then ask that model element for the source elements that use that element as a supplier in a directed relationship. (This is the modeling equivalent, in one sense, of the requirement trace-to relationship.)

Yes, it sounds complex, but if you take it step-by-step, it becomes clearer and easier.

Create and configure your pluglet project

  1. Because you're going to be working in the Modeling perspective, go ahead and set your screen to that perspective to start. It will be fine for pluglet development.
  2. Also, open your Requirements Explorer, because you'll be working with RequisitePro as you develop this pluglet.
Figure 1. Screen path to the Requirement Explorer
Figure 1. Screen path to the Requirement Explorer
  1. Drag it over beneath your Project Explorer, just to maximize editing space and to give your desktop an orderly appearance (Figure 2).
Figure 2. Project Explorer tab
Figure 2. Project Explorer tab
  1. Now, just for development, you'll use the default sample requirements project that ships with RequisitePro. Double-click the Open Requirements Project folder and navigate to (typically):
    C:\Program Files\Rational\RequisitePro\samples\Learning_Project-Use_Cases
  1. Open LEARNING - USE CASES.RQS.

You'll be working with the RequisitePro tool a little later here, when you set up traceability between your test requirements.

  1. Creating the pluglet (see Figure 3).
Figure 3. Create a new pluglet project
Figure 3. Create a new pluglet project
  1. Name your new pluglet project TraceView, as you see in Figure 4.
Figure 4. Name your pluglet project
Figure 4. Name your pluglet project
  1. Click Finish.

In the Project Explorer, the screen should now look like what you see in Figure 5.

Figure 5. Updated Project Explorer view
Figure 5. Updated Project Explorer view
  1. Now, modify the pluglets.xml file and make sure that you can see the Unified Modeling Language (UML) modeling API and the RequisitePro API.

When you have just created the project, the content of the pluglets.xml file looks like the code in Listing 1.

Listing 1. Code for the pluglets XML file
<?xml version="1.0" encoding="UTF-8"?>
<pluglets>
   <require>
      <import plugin="com.ibm.xtools.pluglets"/>
   </require>
</pluglets>
  1. Now add the appropriate imports (see Listing 2).
Listing 2. Code for adding imports to the pluglets XML file
<?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>

Create a simple pluglet

Now that you have a pluglet project and have it configured, you're ready to create the pluglet.

  1. You can use the pluglet wizard to make it easier (see Figure 6).
Figure 6. Select the wizard for creating a pluglet
Figure 6. Select the wizard for creating a pluglet
  1. Next, choose a template for a simple pluglet (Figure 7).
Figure 7. Select a pluglet template
Figure 7. Select a pluglet template
  1. In the New Pluglet > Create a pluglet view:
    1. Give it a package by typing the path into the Package field.
    2. Name the pluglet appropriately by typing the name, TraceView, into both the Source and Name fields (see Figure 8).
Figure 8. Enter the package information for the pluglet
Figure 8. Enter the package information for the pluglet

Well, at this point, the pluglet doesn't do very much, as you can see in Listing 3.

Listing 3. Screen output shows that the pluglet is not yet implemented
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.");
	}

}

Run the pluglet to test it

If you haven't run a pluglet before, you can run it any of these three ways:

  • By selecting the pluglet itself (in this case TraceView.java)
  • From the pop-up menu, where you select Run as and then Run as pluglet
  • From the toolbar (see Figure 9)
Figure 9. Select the pluglet that you want to run
Figure 9. Select the pluglet that you want to run

Take a break for a moment to recall the initial strategy: You want to build a Tree Viewer and display it with your pluglet. Therefore, now you need to add a little code to create and display a new Tree Viewer (see Figure 10).

Figure 10. Screen display of the Traceview.java code for creating a new Tree Viewer
Figure 10. Screen display of the Traceview.java code for creating a new Tree Viewer

Note that some of these classes are not visible.

  1. Invoke the Source: Organize Imports action, and ensure that, when prompted, you select the SWT definition of these classes. (This action automatically sets up the appropriate import statements at the front of your pluglet.) That is, Point should be resolved as org.eclipse.swt.graphics.Point, and that should be the only ambiguity.
  2. Save and try running this pluglet. You should see what Figure 11 shows.
Figure 11. Result of running the pluglet at this stage
Figure 11. Result of running the pluglet at this stage
  1. Dismiss this by clicking the X in the corner of the screen, and go back to the pluglet source code.
  2. Your next step is to add the Tree Viewer to your pluglet source code, as Listing 4 shows.
Listing 4. Add the Tree Viewer to the pluglet source code
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);
	}
}

Note:

  • Tree Viewer should be resolved as org.eclipse.jface.viewers.TreeViewer.
  • Tree should be resolved as org.eclipse.swt.widgets.Tree.
  1. Save your pluglet, and run it again. You should see the Trace View Prototype view, as Figure 12 shows, but without any entries yet.
Figure 12. Trace View Prototype view after running the pluglet
Figure 12. Trace View Prototype view after running the pluglet
  1. If you see this, then you're doing very well! Close this window, so you can continue.
  2. Your next task is to instruct the Tree Viewer about the Content and Label providers by using the code in Listing 5.
Listing 5. Add Content and Label provider information to the Tree Viewer code
  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);
}

Note that TraceContentProvider and TraceLabelProvider do not exist yet -- that's your next task.

Create the Content Provider class

  1. In the Project Explorer, create a new class in the com.ibm.field.traceview package, and have it implement org.eclipse.jface.viewers.ITreeContentProvider. (See Figure 13.)
Figure 13. Create the Content Provider
Figure 13. Create the Content Provider

When you finish, you should see a new class, TraceContentProvider, with the contents that Listing 6 shows.

Listing 6. Contents of the new TraceContentProvider class
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
	}
}

As it stands, this Content Provider doesn't provide anything, but you're going to change that. The Content Providerr's getElements method is the one that you're going to start with, and you'll use the requirements selected from the Requirements Explorer as the root elements of the tree.

  1. Modify the getElements method to reflect this, as Listing 7 shows.
Listing 7. Code for modifying the getElements method
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();
}

Note:

  • Iterator resolves to java.util.Iterator
  • List resolves to java.util.List

Create the Label Provider class

Now that you have basic content being provided to the Tree Viewer, it's time to create the Label Provider.

  1. Create a new class that extends the Label Provider and implements ITableLabelProvider in com.ibm.field.traceview.
  2. Call it TraceLabelProvider (see Figure 14).
Figure 14. Creating the Label Provider
Figure 14. Creating the Label Provider

The code looks like Listing 8, initially.

Listing 8. Code for creating the Label Provider class
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. This is a good start, but you need to provide something other than a null value when the Tree Viewer asks for the column text for a particular item.

Note:

  • In the Content Provider, you're providing RpRequirements (see Listing 9). It makes sense, therefore, to provide at least a barebones implementation that takes that into consideration.
  • In the Tree Viewer, you're really implementing what some developers call a table tree -- that is, it's a tree, but it has table-like columns. You need to take those columns into account when you get the content and also when you provide the images and text that pertains to that tree column.
Listing 9. Code that provides RpRequirements in the Content Provider
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 "";
}

You can get a little fancier as you refine.

  1. Save all of your work, select a couple of requirements, and try running the pluglet again.

You should see something like Figure 15.

Figure 15. Updated Trace View Prototype results after running the pluglet
Figure 15. Updated Trace View Prototype results after running the pluglet

Now, get fancy!

Iterate

Progress report
Let's go over what you've done so far. You've created a pluglet, a Tree Viewer, the Content Provider and the Label Provider for the Tree Viewer, and shown basic functionality.

Your next step is to add detail.

  1. Start by asking the question, "What requirements trace to this element?"

The IBM Rational RequisitePro tool includes documentation on the COM API. You're going to be using an unpublished Java API to the RequisitePro tool to do your work. In many respects, it closely follows the RequisitePro approach. Some tool builders are uncomfortable using unpublished APIs. In this case, though, you have no choice. So, taking this into consideration, just cautiously forge ahead.

Tip:
Bear in mind, however, that if that internal interface changes, then you'll need to change your application. Also note that the use of those interfaces is on an as-is basis.

The programmatic way of asking a requirement about the requirements that trace to it can be expressed fairly straightforwardly, as Listing 10 shows.

Listing 10. Code to determine what other requirements trace to a specific requirement
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;
}

It is expedient to create a utility class for these kinds of helper methods.

  1. Create a new class, and call it TraceUtil.
  2. Then add the method above.
  3. Now, use this in your Content Provider's getChildren method. The default getChildren code looks like Listing 11.
Listing 11. Default getChildren code
public Object[] getChildren(Object parentElement) {
	// TODO Auto-generated method stub
	return null;
}
  1. You need to provide the children of a given requirement, because these are the requirements that trace to a given requirement in your tree. To do that, modify the getChildren code to look like Listing 12.
Listing 12. Modify the getChildren code to specify child-level objects
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. Try running the pluglet now, and you'll see the child-level requirements, as Figure 16 shows. (That is, as long as there are requirements that actually trace to the selected requirement.)
Figure 16. Trace View Prototype view updated to show child-level requirements
Figure 16. Trace View Prototype view updated to show child-level requirements

Why bother doing this?

You may be asking yourself: Why bother with recreating the traceability view that you can create in RequisitePro? Good question. However, if you were to stop right now, you'd have no more functionality than the out-of-the-box RequisitePro traceability view would have (and significantly less, even). The goal is to bridge the relationship between requirements and model elements and show that in your view, as well. To do that, you need a little more information, such as whether a given requirement is related to a model element.

In the demonstration requirements project, UC2 and UC2.2 both trace to FEAT4.

As you may recall from the discussion here earlier, there are two kinds of links that can be established between the RequisitePro requirements and Rational Software Modeler's model elements: direct and proxy. Both involve somewhat the same basic information. That is, on either a direct or model element proxy requirement, there's a hidden string attribute, AssociatedElementUri.

If you were to look at the value of that attribute for a given requirement related to a model element, you would see a value that looks like Listing 13.

Listing 13. Screen output for the AssociatedElementUri attribute
uml:platform:/resource/UML%20models/TestAbstr.emx>
_5z4KwLx1Edug6OWRp7wLNw?TestAbstr::Marksmanship

This URI contains not only the UML model element, but also the model and its relative path within your workspace -- in other words, the URI for the UML model itself:

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

The fragment that corresponds to the element in the model) is this:

_5z4KwLx1Edug6OWRp7wLNw

To find the element (if any) that a requirement is linked to, you can parse that URI, and then employ Rational Software Modeler's utility routines to resolve the URIs to the proper elements.

  1. Create a new helper function in your utility class that takes care of the necessary parsing (see Listing 14).
Listing 14. Code for adding a helper to the utility class
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;
}

}

Note:

  • URI should resolve to org.eclipse.emf.common.util.URI
  • Model should resolve to org.eclipse.uml2.uml.Model.
  1. Now you need to get the value of the AssociatedElementUri property from the requirement -- another utility function (Listing 15).
Listing 15. Code for a utility to get the value of AssociatedElementUri
public static NamedElement assocElement(RpRequirement rp) {
	RpReqType irqt = rp.getReqType();
	try {
		if (RpReqTypeUtil.attrExists(irqt, "AssociatedElementUri")) 
			String euri = RpRequirementUtil.getAttrValue(rp,
					"AssociatedElementUri");
			return (linkedElement(euri));
		}
	} catch (RpException e) {
		e.printStackTrace();
	} catch (Exception e) {
		e.printStackTrace();
	}
	return null;
}
  1. Now that you can ask a requirement whether it's linked to a model element, you're ready to flesh out the TraceUtil.relatedObjects method to use this new capability (see Listing 16).
Listing 16. Code to complete the TraceUtil.relatedObjects method
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. However, before you can run your Tree Viewer, you're going to need to update the Label Provider's getText method to take into consideration that the element could be either a requirement or a model element (see Listing 17).
Listing 17. Update the Label Provider's getText method
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. It's helpful to try running the pluglet again now, with the same elements selected in the Requirement Explorer. You should see the same output as before, unless they're already linked to model elements in your workspace.
  2. Before you leave the TraceUtil method, however, add another utility function to return a list of the model elements that relate to a given model element through a directed relationship (see Listing 18).

Tip:
You can add model element relationships that you're interested in simply by augmenting the code in this routine.

Listing 18. Add a utiility to get related model elements
//	 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;
}

Next, take all of this and revisit the Content Provider. A given node in the tree can be either a requirement or a model element. You've already added the code in the getChildren routine to show the traced-from requirements (as long as the node is a requirement).

  1. Of course, the node could be a model element. In that case, you need to account for the possibility when you ask for it's children.
Listing 19. Code to request children if the node code me a model element
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();
}

Note:
Adding this is rather trivial.

Add detail to the Content and Label providers

Notice that each row in the table is either a requirement or a NamedElement. You can make good use of the other columns in the Tree Viewer, though, by modifying the Label Provider. Also notice that the TraceLabelProvider implements ITableLabelProvider. Therefore, the additional argument, columnIndex, should come into play now when you are displaying the desired columns for reach requirement or NamedElement. Thus, in the getColumnText method , you see what Listing 20 shows.

Listing 20. Notice the getColumnText method
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. For a requirement, ask for its package in the RequisitePro project.
  2. For elements, ask for their owners. For NamedElements, you'll get their visibility level (public, private, and so on), as you see in Listing 21.
Listing 21. Code for queries about requirements and elements
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 "";
}

What you have should be completely functional at this point. (You can dress up the display with images later.)

Test your pluglet

Now, create a test case that takes advantage of your new visualization capability. This involves creating a test scenario by completing these tasks:

  1. Create links between requirements.
  2. Create a direct link from the requirement to the UML NamedElement.
  3. Create a proxy link from the requirement to the UML NamedElement.
  4. Create dependencies for the element from other NamedElements.
  5. Create dependencies for those other NamedElements to other elements.
  6. Create Java code generated from the UML NamedElement.

Being able to walk this traceability tree would be very interesting, because it goes from requirement to code.

From requirements to code

  1. Open the Learning Project - Use Cases project by using the RequisitePro client.
  2. Create a new traceability matrix, and establish traceability from FEAT3 to FEAT2, and from FEAT2 to FEAT1. Aassuming that FEAT1 was selected in the Eclipse Requirement Explorer, the traceability that you would expect to see would look something like this (also see Figure 17):
  FEAT1
FEAT2
FEAT3
Figure 17. RequisitePro traceability view
Figure 17. RequisitePro traceability view
  1. Now, leave the RequisitePro client, and in Rational Software Modeler's workbench, select the RequisitePro project in the Requirement Explorer.
  2. Right-click and invoke the Refresh action. This forces the RequisitePro project to resynchronize with the project (see FIgure 18).
Figure 18. Requirement Explorer view
Figure 18. Requirement Explorer view

Now create a new model that you can use for demonstration.

  1. First, create a UML project.
  2. Then create a blank model, and name it M1.
Figure 19. Create a new UML project called MI
Figure 19. Create a new UML project called MI
  1. Next, create three classes in the model, and relate them with dependencies, as Figure 20 shows.
Figure 20. Create classes and dependencies
Figure 20. Create classes and dependencies
  1. Now you'll link Class1 to requirement FEAT3. To do this, select FEAT3 and drag it onto Class1.
  2. Finally, create a new UML > Java transformation configuration, and transform Class3 into a Java class. (See Figure 21.).
Figure 21. Creating a transformation configuration (screen path)
Figure 21. Creating a transformation configuration (screen path)

You should see the dialog that Figure 22 shows.

Figure 22. New Transformation Configuration view
Figure 22. New Transformation Configuration view
  1. Next, set the source and destination.
Figure 23. Set the source and the target
Figure 23. Set the source and the target
  1. Create a new Java project as a target container, and call it J1 (Figure 24).
Figure 24. Create a new Java project called J1
Figure 24. Create a new Java project called J1
  1. Click Finish.
  2. Then select J1 as the target in the New Transformation Configuration dialog.
  3. Keep clicking Next in the New Transformation Configuration wizard until you get to the Common page (Figure 25), where you need to be sure to select Create Source to Target Relationships.
Figure 25. The Common page in the New Transformation Configuration wizard
Figure 25. The Common page in the New Transformation Configuration wizard
  1. Then, click Finish.
  2. You can run this transformation simply by selecting the transformation configuration and invoking the Option menu action of Transform: UML to Java5.
Figure 26. Screen path for running the transformation
Figure 26. Screen path for running the transformation

Notice that the Java project now has a new Java class, Class3 (Figure 27).

Figure 27. Screen capture of where the new Class 3 Java class appears
Figure 27. Screen capture of where the new Class 3 Java class appears
  1. Finally, make sure that you enable Java modeling in Preferences (Figure 28).
Figure 28. Enable Java modeling in the Preferences view
Figure 28. Enable Java modeling in the Preferences view
  1. Open the Advanced tab, and then ensure that Java modeling is enabled (Figure 29).
Figure 29. Advanced tab of the Capabilities section of the Preferences view
Figure 29. Advanced tab of the Capabilities section of the Preferences view

By now, you have built up a traceability scenario that should look like Listing 22.

Listing 22. Traceability scenario
  FEAT1
FEAT2
FEAT3
Class1
Class2
Class3
(Java) Class3
  1. It's time to run your pluglet again, and see what you get now (Figure 30).
Figure 30. Updated Trace View Prototype view
Figure 30. Updated Trace View Prototype view

Not bad! Note that Figure 30, above, shows CLASS7 as a proxy requirement representing Class1 from your model.

  1. Next, use the code in Listing 23 to implement the Label Provider for supplying images.
Listing 23. Implement the Label Provide to supply images
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;
}

This makes the whole picture comes into focus: Traceability, from requirement through model to code (Figure 31).

Figure 31. The complete picture of traceability
Figure 31. The complete picture of traceability

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Rational software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Rational, Architecture
ArticleID=227483
ArticleTitle=Visualize traceability in your development projects using IBM Rational Software Modeler and IBM Rational RequisitePro
publish-date=06052007