IBM Support

Replacing references to Rational Rose unique identifiers after the import of a model that used the Data Modeler add-in

Question & Answer


Question

How do you replace references to the IBM Rational Rose (Rose) unique identifiers (QUID) in a project that used the Data Modeler add-in and was then imported into IBM Rational Software Architect (RSA)?

Cause

If your Rose model was making use of the Data Modeler add-in, and if you checked the Import property sets as UML profiles checkbox on the Map Property Sets of the Import wizard when importing the model into RSA, all the data added to your Rose model by the Data Modeler add-in will be imported into RSA.

However, because the functionalities offered by the Data Modeler add-in are not present in RSA, some of the imported properties are a raw copy of the ones of the Data Modeler add-in.

This is certainly the case for primary keys, foreign keys or indices that were defined with references to other Rose elements. If you look, in RSA, at the KeyList property (from the applied Default stereotype of the Data_Modeler profile) of those elements, you will see that this property refers to a Rose QUID that is not easily readable.

Therefore, you may want to find a way to convert this information into the name of the matching RSA UML element.

Answer

You will find instructions below that let you create a complete pluglet to replace all the references to Rose QUID in the KeyList property with the qualified name of the corresponding UML element.

NOTE: The KeyList property is of type String, which means that none of the changes made to the name of the elements to which it refers will be taken into account after the pluglet has been run. Indeed, the pluglet only changes the content of the KeyList property but not its type. If you also want the type of the KeyList property to be changed, so that it can hold direct references to UML elements of the model, you will have to write additional code.


Disclaimer

All source code and/or binaries attached to this document are referred to here as "the Program". IBM is not providing program services of any kind for the Program. IBM is providing the Program on an "AS IS" basis without warranty of any kind. IBM WILL NOT BE LIABLE FOR ANY ACTUAL, DIRECT, SPECIAL, INCIDENTAL, OR INDIRECT DAMAGES OR FOR ANY ECONOMIC CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN IF IBM, OR ITS RESELLER, HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.




To build the pluglet:
  1. Start Rational Software Architect

  2. Select File > New > Pluglets Project

  3. Select File > New > Pluglet

  4. Select the type: UML Modeler > Model Enumeration

  5. Select the package: com.ibm.rational.support

  6. Select the name: DataModelerIdsConverter

  7. Replace the code of the class DataModelerIdsConverter with the following and save the changes:


package com.ibm.rational.support;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Stereotype;

import com.ibm.xtools.modeler.ui.UMLModeler;
import com.ibm.xtools.pluglets.Pluglet;

public class DataModelerIdsConverter extends Pluglet {

private static final String PK = "PK"; //$NON-NLS-1$
private static final String FK = "FK"; //$NON-NLS-1$
private static final String INDEX = "Index"; //$NON-NLS-1$
private static final String DATAMODELER = "Data_Modeler"; //$NON-NLS-1$
private static final String KEY_LIST = "KeyList"; //$NON-NLS-1$

/**
* Browse the selected elements and update any operation that has the PK, FK or Index keyword.
* @param args the arguments provided at launch
*/
public void plugletmain(String[] args) {

try {
TransactionalEditingDomain editDomain = UMLModeler.getEditingDomain();
editDomain.getCommandStack().execute(
// Create a RecordingCommand, and a menu entry in the Edit menu to "Undo DataModeler Id Conversion"
new RecordingCommand(editDomain, "DataModeler Id Conversion"){

protected void doExecute() {
final List<EObject> elements = UMLModeler.getUMLUIHelper().getSelectedElements();

if (elements.isEmpty()) {
out.println("Cannot perform enumeration on current selection."); //$NON-NLS-1$
out.println("Please select a UML Element from the Project Explorer or"); //$NON-NLS-1$
out.println("select a Notation element from a diagram."); //$NON-NLS-1$
}
for (EObject element : elements) {
if (!(element instanceof NamedElement))
// We are only accepting elements of type org.eclipse.uml2.uml.Element as they allow
// to easily get access to the model, but this is not strictly mandatory.
out.println("Element "+element+" is not valid: it should be a org.eclipse.uml2.uml.Element!"); //$NON-NLS-1$
else {
Element UMLElement = (Element)element;
// Update the element, based on a map that binds all rose ids to the name of the matching RSA UMLElement.
updateElementAndSubElements(UMLElement, mapRoseIdsAndNames(UMLElement.getModel()));
}
}
}
});
} catch (IllegalStateException e) {
out.println("The operation was interrupted"); //$NON-NLS-1$
}
}

/**
* Create a map that associates the former Rose ids of all the elements of the model that were imported form a Rose model
* with the fully qualified name of the matching elements. If no elements were imported from Rose, the map will be empty.
* @param model The model to analyze.
* @return a Hashmap where keys are the former Rose ids of all elements of the model that were imported from Rose and where
* values are the fully qualified names of those elements.
*/
protected Map<String, String> mapRoseIdsAndNames(Model model) {
Map<String, String> result = new HashMap<String, String>();
List <NamedElement> modelElements = getUMLSubElements(model);
for (NamedElement modelElement : modelElements){
String roseId = getRoseQUID(modelElement);
if (roseId != null)
// This will put the fully qualified name in the map. Use the following line instead, if you want the simple name:
//result.put(roseId, modelElement.getName());
result.put(roseId, modelElement.getQualifiedName());
}
return result;
}

/**
* analyze an element and all its sub-elements, and update the "KeyList" property of its stereotype of the Data_Modeler profile,
* replacing the former Rose unique ids of this list with the actual fully qualified name of the matching UML element.
* @param element the Eobject that is the root of the tree of EObjects to update
* @param elementsIds a map that associates elements to their former rose unique id and to which the ids will be compared.
*/
private void updateElementAndSubElements(EObject element, Map<String, String> elementsIds) {
if (null != element) {
// Only browse the elements of type operation
if (null!= element && element instanceof Operation){
Operation operation = (Operation)element;
// Only loook for primary keys, foreign keys or indices.
if (operation.getKeywords().contains(PK) || operation.getKeywords().contains(FK) || operation.getKeywords().contains(INDEX))
// Look for a stereotype of the "Data_Modeler" Profile
for (Stereotype stereotype : operation.getAppliedStereotypes())
if (DATAMODELER.equals(stereotype.getProfile().getName())){
out.println("Operation element updated: "+operation.getName()); //$NON-NLS-1$
out.println("Former value of the KeyList property: "+operation.getValue(stereotype, KEY_LIST)); //$NON-NLS-1$
operation.setValue(stereotype, KEY_LIST,convertIdsToRSANames((String)operation.getValue(stereotype, KEY_LIST), elementsIds));
out.println("New value of the KeyList property: "+operation.getValue(stereotype, KEY_LIST)); //$NON-NLS-1$
}
}
// Handle all the sub-elements.
for (EObject content : element.eContents())
updateElementAndSubElements(content, elementsIds); //$NON-NLS-1$
}
}

/**
* convert a list of former Rose unique ids into the name of the matching UML Named element in the model.
* @param idlist the comma separated list of all potential former Rose unique ids to analyze.
* @param elementsIds a map that associates elements to their former rose unique id and to which the ids will be compared.
* @return a comma separated list of all the former rose unique ids
*/
private String convertIdsToRSANames(String idlist, Map<String, String> elementsIds){
if (idlist == null)
return null;
List<String> roseIds = Arrays.asList(idlist.split(","));
for (int i = 0; i < roseIds.size(); i++)
for (String id: elementsIds.keySet())
if (id.equals(roseIds.get(i)))
roseIds.set(i, elementsIds.get(id));
return listToString(roseIds);
}

/**
* recursively list all UML named sub-elements of a given EObject.
* @param element the EObject to analyze.
* @return the list of all the UML named sub-elements of the element.
*/
private List<NamedElement> getUMLSubElements (EObject element){
List<NamedElement> result = new ArrayList<NamedElement>();
if (element != null)
if (element instanceof NamedElement){
result.add((NamedElement)element);
for (EObject subElement : element.eContents())
result.addAll(getUMLSubElements(subElement));
}
return result;
}


/**
* return the RSA unique Id of a given EObject element.
* @param element the EObject element for which the RSA unique Id must be returned.
* @return the RSA unique Id of the element.
*/
private String getId(EObject element) {
return element.eResource().getURIFragment(element);
}

/**
* return the former Rose unique Id (QUID) of a given EObject element that was imported from a Rose model.
* @param element the EObject element for which the former Rose unique Id must be returned.
* @return the former Rose unique Id of the element if it was imported from a Rose model, null otherwise.
*/
private String getRoseQUID(EObject element) {
String rsaId = getId(element);
// Unique ids of elements that were created within RSA are in mixed case
// whereas elements imported from Rose are in upper case.
if (null != rsaId && rsaId.toUpperCase().equals(rsaId))
// The former Rose unique ids of elements imported into RSA
// are stored as the last 12 characters of the RSA unique id (eg: 4378501B02AF).
return rsaId.substring(rsaId.length() - 12);
return null;
}

/**
* Concatenate a list of character strings into a comma separated list stored as a unique character string.
* @param strings the list of character strings to concatenate.
* @return the comma separated list stored as a unique character string.
*/
private String listToString (List<String> strings){
String result = "";
for (String string: strings)
result+=string+",";
// We remove the last comma that is superfluous.
return result.substring(0, result.length()-1);
}
}


Alternatively:

You can also directly import the following project:

DataModelerIdsConverter.zipDataModelerIdsConverter.zip

To run the pluglet:
  1. Select the model on which you want the pluglet to be executed

  2. Select the Run > Internal Tools > Pluglet Development Capability menu

  3. Wait for the execution of the pluglet to be complete. The pluglet output will be displayed in the console, listing all the affected elements, the former values and the new ones.
    Note: If the DataModelerIdsConverter element does not appear on Step 2, you need to add it to the Internal Tools Configurations:
    1. Select the Run > Internal Tools > Internal Tools Configuration

    2. Right-click on the pluglet element in the left pane

    3. Select New

    4. Locate the pluglet by browsing the workspace

    5. Give the pluglet the name DataModelerIdsConverter

    6. Click the Apply button

    7. Follow the instructions to run the pluglet from Step 1

[{"Product":{"code":"SSYK2S","label":"Rational Software Architect Designer"},"Business Unit":{"code":"BU053","label":"Cloud & Data Platform"},"Component":"Modeling","Platform":[{"code":"PF033","label":"Windows"},{"code":"PF016","label":"Linux"}],"Version":"8.5.1;8.0.4.2","Edition":"","Line of Business":{"code":"LOB45","label":"Automation"}},{"Product":{"code":"SS4JCV","label":"Rational Software Architect for WebSphere Software"},"Business Unit":{"code":"BU053","label":"Cloud & Data Platform"},"Component":"Modeling","Platform":[{"code":"PF016","label":"Linux"},{"code":"PF033","label":"Windows"}],"Version":"8.0.4.2;8.5.1","Edition":"","Line of Business":{"code":"LOB15","label":"Integration"}}]

Document Information

Modified date:
10 September 2020

UID

swg21626996