Reverse
engineering is a
processus to rebuild UML model from language implementation. In Java,
some
information can be retrieved easily by parsing syntaxly Java source
code or by
decoding class binary file structure, such as:
Others need a
complex code
semantic analysis, such as:
Precisely, it is necessary to analyze method implementation content to capture them. EclipseUML Personal/Professional/Studio Edition provides a very powerful reverse engineering engine to meet these requirements based on the byte-code analysis. Additionally, it provides following features:
EclipseUML
Personal/Professional/Studio Edition uses the reverse engineering
engine in two
ways:
UML Reverse
engineering
detects automatically the getter/setter method of an attribute. It
takes into
account the prefix and suffix preferences of JDT.
The name without prefix and suffix will be used as model name, named as
property.
If methods
have the
implementations, the UML reverse engineering analyse the code to make
sure that
the getter method returns this attribute value and the setter changes
this
attribute value.
For example,
with the
prefix setting of "f", in the following Java class that contains an
attribute name = fCompany, the property
name will be "company"
public class Employee
{
private Company fCompany;
public Company getCompany()
{
return fCompany;
}
public void setCompany(Company company)
{
fCompany = company;
}
}
public class Company
{
...
}
After the
reverse
engineering process, we find UML role name = company, instead of fCompany.

public class Employee
{
/**
* @uml property=company associationEnd={multiplicity={(0 1)} ordering=ordered
* elementType=company.Company}
*/
private Company fCompany;
/**
* @uml property=company
*/
public Company getCompany()
{
return fCompany;
}
/**
* @uml property=company
*/
public void setCompany(Company company)
{
fCompany = company;
}
}
In case of the
abstract
method: class abstract method or interface method, there is no code to
analyse.
So the only solution is to analyse the method name and signature
according to
JDT definition. The setter method is ignored if a getter method is
missing in the
class/interface.
EclipseUML Reverse Engineering
Engine is capable to detect two groups of associations:
To classify an attribute type to one
of the two groups; the key issue is container detection. In Java, there
are two
categories of containers: java.util.Collection and java.util.Map.
EclipseUML
reverse engineering implements a mechanism to recognize not only the
interface
java.util.Collection and java.util.Map, but also their subtypes or
implementation classes such as java.util.List, java.util.Vector,
java.util.Hashtable.
For a simple reference, if the attribute is always initialised, the cardinality will be 1. Otherwise, it will be 0..1.
Examples of cardinality 0..1
|
Before Reverse Engineering |
After Reverse Engineering |
|
public
class Employee extends Person {
private Company company;
public Company() { } } |
public
class Employee extends Person {
/**
* @uml property=company
associationEnd={multiplicity={(0 1)}
* }
*/
private
Company company;
public Company() { } } |
Example of cardinality 0..1
|
Before Reverse Engineering |
After Reverse Engineering |
|
public
class Employee extends Person {
private Company company;
public Company(Company company)
{
this.company
= company; } } |
public
class Employee extends Person {
/**
* @uml property=company associationEnd={multiplicity={(1
1)}
* }
*/
private
Company company;
public Company(Company company) { this.company = company; } } |
A container has always the
cardinality 0..*.
Example of collection
|
Before Reverse Engineering |
After Reverse Engineering |
|
public
class Company {
private Collection employees
= new ArrayList(); … } |
public
class Company {
/**
* @uml property=employees associationEnd={multiplicity={(0
-1)}
* inverse={company:model.Employee}
* }
*/
private Collection employees
= new ArrayList(); … } |
EclipseUML Reverse engineering
engine captures the element type in a collection by analysing content
accessing
code. For example, the argument of collection method add() may indicate
the
element type:
public
class Company
{
private List employees
= new ArrayList();
public Company(List
employees)
{
this.employees
= employees;
}
public
void addEmployee(Employee
employee)
{
employees.add(employee);
}
public
Collection getEmployees()
{
return
employees;
}
}
The idea is very simple. But the real use cases are mush more complex. The same method may have different implementations.
|
Using local variable |
public
void
addEmployee(Employee employee) {
Collection localVariable = employees; localVariable.add(employee); } |
|
Clall getter |
public
void
addEmployee(Employee employee) { getEmployees().add(employee); } |
|
Using getter and
local variable |
public
void
addEmployee(Employee employee) { Collection localVariable = getEmployees(); LocalVariable.add(employee); } |
Of
course, these codes
can be used outside of the class Company.
According
the
accessing type of this attribute, we distinguee the analyze mechanism
in two
categories:
· Getter category
· Setter category
The first
one allows the forward analyze, the
analyzer can follow the execution order. The typical example is the
iteration
method (see below). It is much easy than Setter category,
which needs backward analyze mechanism.
Each
accessing method may have a specific way
to capture the element type. So we
classify all relevant methods as following groups according to the
model capture mechanism:
This group includes following
methods:
Class |
Method |
|
java.util.Collection |
boolean add(Object object) |
|
java.util.Vector |
void addElement(Object object) |
|
java.util.Vector |
void add(int index, Object object) |
|
java.util.Vector |
Object set(int index, Object object) |
|
java.util.Vector |
Object elementSet (Object object, int index) |
|
java.util.Vector |
void setElementAt (Object object, int index) |
|
java.util.Vector |
void insertElementAt (Object object, int index) |
Class |
Method |
|
java.util.Collection |
boolean remove(Object object) |
|
java.util.Vector |
boolean removeElement(Object object) |
|
java.util.Vector |
void removeElement (Object object) |
Class |
Method |
|
java.util.List |
int indexOf (Object object) |
|
java.util.List |
int lastIndexOf(Object object) |
|
java.util.Vector |
int lastIndexOf (Object object, int index) |
|
java.util.Collection |
boolean contains (Object object) |
The capture mechanism of this group
is simplest one comparing others. We just need identify the method call
and
capture argument type. For
example:
public
void
removeEmployee(Employee employee)
{
Collection localVariable = employees;
localVariable.remove(employee);
}
or
public
int
getEmployeeIndex(Employee employee)
{
return
employees.indexOf(employee);
}
This group consists of following
methods:
Class |
Method |
|
java.util.List |
Object get(int index) |
|
java.util.Vector |
Object elementAt(int index) |
|
java.util.Vector |
Object firstElement() |
|
java.util.Vector |
Object lastElement() |
This group is a little bit difficult
than previous one since we need analyse the next statements to capture
the
element type in type casting or the operator instanceof. For example,
public
Employee
getEmployeeAt(int index)
{
return
(Employee)
employees.get(index);
}
or
public
boolean
hasEmployeeAt(int index)
{
Object
element
= employees.get(index);
return
(element instanceof
Employee);
This group consists of following methods
Class |
Method |
|
java.util.Collection |
Iterator iterator () |
|
java.util.List |
Iterator listIterator () |
|
java.util.List |
Iterator listIterator (int index) |
|
java.util.Vector |
Enumeration elements () |
This group is more difficult than
previous one again since it is necessary to analyse the following cast
and instanceof statements only after
element
retrieve call such as next() or
nextElement(). For example,
Iterator iterator
= company.getEmployees().iterator();
while
(iterator hasNext())
{
Employee employee = (Employee)
iterator.next();
}
or
for
(Iterator iterator = company.getEmployees().iterator(); iterator
hasNext();)
{
Employee employee = (Employee)
iterator.next();
}
All casting and operator instanceof
must be ignored without calling next(). Otherwise, the result will be
wrong:
Iterator iterator
= company.getEmployees().iterator();
Address address =
(Address)
getAddress();
while
(iterator hasNext())
{
Employee employee = (Employee)
iterator.next();
}
When the
code assigns a filled container to
this attribute, for example,
List employees = new
ArrayList();
employees.add(new
Employee());
Company company =
new Company
(employees);
or
List employees = new
ArrayList();
employees.add(new
Employee());
company.setEmployees(employees);
it is
necessary to perform the semantic analyze
following inverse execution order. EclipseUML
Personal/Professional/Studio
edition implements this sophistic mechanism. It is used only when the
first
category calls are missing.
EclipseUML Reverse engineering uses
following rules to resolve inverse associations:
For example:
public
class Project
{
private HashMap
teams =
new HashMap ();
public
void addTeamMember(String
name, Employee
member) {
Collection team =
(Collection) teams.get(name);
if (team ==
null) {
team =
new ArrayList();
teams.put(name, team);
}
team.add(member);
member.setProject(this);
}
}
The
attribute accesses are in bold. So the
associations implemented by the attribute teams and
project are
inverse associations.
EclipseUML Reverse engineering
engine can recognize the java.util.Map and its implementation classes such
as java.util.HashMap and
java.util.Hashtable.
It can capture not only the key
and value type, but also the element type in the associated value if it
is a
collection.
In the
following illustration, we use the
Company and Project class as example:
public
class Company
{
private Hashtable
projects =
new Hashtable
();
public Company(Hashtable
projects)
{
this.projects =