---
tags: Object-oriented
title: Metrics Document
---
# Object-oriented Mechanism
Create a minimal directory with 2/3 files, which contain our classes, in order to solve these problems. Let's use Java as reference programming language.
## Questions (how to do in rca)
1. How can we identify the relatioships among classes scattered through different files?
2. How can we know if a class is inside another class?
3. How can we know the parent of a class?
4. Do we need to consider only user-defined classess or just the standard ones?
## How can we identify the relatioships among classes scattered through different files?
It could be useful to scan all the source file to collect all the classes data we need to compute the metrics. For each class we should store:
- The parent class (if present)
- The class properties names and their types
- The class methods names and their return types (WMC)
Inheritance information could be stored in a tree structure, in order to be easily navigated (NOC and DIT). For each method of a class we then want to store:
- The names of the classes the method interacts with through method calls, field accesses, arguments, return types, and exceptions (CBO)
- The distinct methods invoked inside the method (RFC)
- The properties (names and types) accessed inside the method (LCOM)
We could save all these class methods information inside each belonging class information struct.
@marcoballario Construct a graph of relation using draw.io, in this way it is much simpler to figure out how classes interact among ourselves.
## How can we know if a class is inside another class?
With the information collected above we should be able to know all direct interactions between all the classes.
## How can we know the parent of a class?
If we supposing to use Jave that the parent of a class can be obtained thanks to the `extend` keyword of java. If the `extend` keyword is not present we can assume the parent id Object (or null if we do not want to consider Object).
## Do we need to consider only user-defined classess or just the standard ones?
We could store a list of all standard Java packages and their classes. Once we have this list we can decide if the standard classes should be ignored or included in the metrics computation.
We are not interested in collecting information on Java standard classes, but we **just** want to collect information of user-defined classes.
On the other hand though, we **want** to know when we call Java standard classes methods **inside** our user-defined classes.
## UML
<iframe frameborder="0" style="width:100%;height:838px;" src="https://viewer.diagrams.net/?tags=%7B%7D&highlight=0000ff&edit=_blank&layers=1&nav=1&title=CK_UML.drawio#R7Ztbd6I6FMc%2FjY91cRV9rNqb085x6kzb8xghQk4D8UCo2k8%2F4X4L1FbldE7p6mrNJgTY%2Fx9h7x3syRN7e%2BWCtXVHDIh7kmBse%2FK0J0miIA%2FZv8CyiyyqpkQG00VG3CkzLNArTPaMrT4yoFfoSAnBFK2LRp04DtRpwQZcl2yK3VYEF4%2B6BiasGBY6wFXrIzKoFVmHkpbZryEyreTI4mAUbbFB0jm%2BEs8CBtnkTPJFT564hNDok72dQBw4L%2FHL483uEd8%2BD65mP7x%2Fwa%2Fxt5%2FfH86iwS7fs0t6CS506IeHfn1eXV4%2FKP88reeXPzbC1flydhbvIrwA7Mf%2BmmDgeVNAQXzRdJd40tsgGwOHtcYr4tBFvIX5YQwwMh32WWcnCF1meIEuRUyE83gDJWtm1S2EjVuwI35wGR4F%2BnPSGlvERa9sWIDZJpEZ2GaXxjxJg0KPRbAnMwvM6kKP9ZknvhFT0y3waNxHJxiDtYeW4QkHXWzgmsgZE0qJnQxEfMeARtxKxQ4b1CXPKT7B%2FnsqEisXeANuczzGCl1BYkPq7liXeKskD%2FuaGu0V33DyIBZpk%2BErq7HNyqGbGkF8y5jp8OkR79ktBhyT%2BSF3SKFwPFGpHk8ccI7HRCkcDmCmvQMoHAee9PIwsg%2B5q81MIaLvwFWs4NqTxpkQ7LaUz9nfBXWRY4ZN1kdw%2FGDkEs1MEJojF8MVreXWWwOdDXgb9pkqmeU%2B9kZgImzfFQ6ZsZBhQCdkirI7KcIuAGlNkENDd6lj9sucOhH6ak9lJzRhbTFrs9%2Bgu0snxGH4ARRyBhnTGxhwXcK1ymPj7f42j7uixm%2BxV2Yhj14BgvcqLtUp7pJ1oBV7tCSqXwPPugPrnjTAgYMcYMMKEZPg8bNbVzcMTJq68X9LSWFSs6iNT0yPKv3H9Mg19LDRLWK8H51ov3TTXdiMHpYdQCcASBu2B9D2Hi1ftrc7okBhqunT%2BdSQax84OvHXGBphsFSaghaQNnLUgVIFYF92akERBaU9UuzRAjzN7cX1WPSVHdTnd8ubM7WGFANlcYk%2FUNKgRPjSBBx%2FqhDlPcPkkxEwqCHAIXpHQBsEqC0Gq1wCtBoCNnZHQCsEDFsMOLkEDGsIcFcdAW0QIAktRoyP1lb660aUHi9tVb16VhFyN5yKWkQA1ondIdAGAnKLoSAXgdqkYUk6AtogYN%2BK6cnqDgqHgJLEGIWV9NgdIrfK%2FIb%2BNlMyGC4R%2FGfAw%2FRMrEAhV6GQOQBgsIR4TjxEEQnGd6O%2BJTA%2BSWFyuGewJ0uHa%2Fx0P7vyFg%2F3YDZb%2FcL2jKKLBWeiz5eD9ls7EXrd2knvWGsnYmnhZCipFUQUTeurVUjS9Y33LZ2oCXCfa%2BmEi2tdIZ2BhqFx11wQ1YM61%2FfmqujHqmBdNaxwJzTONIes2tSAf4wnIPec6yrvvgeNebd28%2FkZ4qzdtM0Qr6DWRVHHFJmzvlIjcpJUHV1kkfdgKqkMHeM8eBWItRxSFpy5xN09BY5i5x03%2F85vm25jL0atXdIyTJjEYUwIi5jEAfgis76pQVPY4hHf1eHbWrFAzYRNmoqxRMHpNkqa05AXByc2F2JA0Uvx7aiGsGceYJuFPIok9AVllP0UY66RWpoMIj%2FEg2SoVMbVpOJAWoJmMlDkqMpAjAuwy3WL77L68xeV4nHkQQnhaMSjxl5i9WnI1BD6ffbHqXtu5RB%2FM0VI57AwfK99HAWTDvAp8aLMQDyQ8f1Dc0UcFb0%2B4kQovDi5ISw%2FbNapZuji1xBCKwqhihUhpFaF4FXMSzq4FrGXvldx0gphPCGYuGE3WQh%2FTug8eVSiWOTUmXgliCM479wxz6aT1%2B%2FO8mKlOs%2Fb2dk3v%2BntTeYXzBLWIATZsxbRvcd5RFSkgVp%2Bj1NUq7RIyTxYKEYIHypGfNL3OLng1lbIjWyZbN%2BUMKxNpFv07PXlL5EWVgFunCkOKSFwYT1G8sc9466EfmqJORk%2Bfz4aHi4xd52M98rUn5%2F55TM67mV%2FsoROlPvaqDahk9VRccSPJnRykuifOKGTtMLXYA5O6LgSVitTXzufkzkv350qn%2BMKUn3z6kumc9KwuuR1qnSuaW77M7K5yletJE6IfqSEjjWzbwpG01D2fUv54jc%3D"></iframe>
## Pseudocode
### Global data
```
let classDataCollection = ClassDataCollection::new();
let cdc = classDataCollection.cdc;
cdc = HashMap::new();
... // fill cdc hashmap with classes data
```
### WMC (Weighted Methods per Class)
The sum of the Cyclomatic Complexities of the methods of a class.
```
for cd in cdc.values() {
for m in cd.methods.values() {
let cc = ... ; // get cyclomatic complexity from rca
cd.wmc += cc;
}
}
```
```rust
/// We are computing the wmc metric for EVERY class contained in a SINGLE file.
pub fn wmc(source: Vec<u8>, path: &Path) {
let parser = JavaParser::new(source, &path);
wmc(&parser, &path)
}
pub fn wmc<'a, T: ParserTrait>(parser: &'a T, path: &'a Path) -> Option<FuncSpace> {
let code = parser.get_code();
let node = parser.get_root();
let mut cursor = node.object().walk();
let mut stack = Vec::new();
let mut children = Vec::new();
let mut state_stack: Vec<State> = Vec::new();
let mut last_level = 0;
stack.push((node, 0));
while let Some((node, level)) = stack.pop() {
if level < last_level {
finalize::<T>(&mut state_stack, last_level - level);
last_level = level;
}
let kind = T::Getter::get_space_kind(&node);
let class_space = T::Checker::is_class(&node);
let new_level = if class_space {
let state = State {
space: FuncSpace::new::<T::Getter>(&node, code, kind),
};
state_stack.push(state);
last_level = level + 1;
last_level
} else {
level
};
if let Some(state) = state_stack.last_mut() {
let last = &mut state.space;
T::Cyclomatic::compute(&node, &mut last.metrics.cyclomatic);
}
cursor.reset(node.object());
if cursor.goto_first_child() {
loop {
children.push((Node::new(cursor.node()), new_level));
if !cursor.goto_next_sibling() {
break;
}
}
for child in children.drain(..).rev() {
stack.push(child);
}
}
}
finalize::<T>(&mut state_stack, std::usize::MAX);
state_stack.pop().map(|mut state| {
state.space.name = path.to_str().map(|name| name.to_string());
state.space
})
}
```
### DIT (Depth of Inheritance Tree)
The max length between a class node and his root node in a class hierarchy.
The default value is 0 or 1 if we decide to count the java.lang.Object class.
#### Describe what we do
1. While parsing the AST, we save for each class whether it has a parent
2. Save inside an HashMap `class_name` and `Option<parent>`. `None` if we are at the root.
3. For each entry of the HashMap, we find the hierarchy tree
#### Minimal Test
```java
class M {}
class A extends M {}
class B extends A {}
```
HashMap structure for the example above:
```
("M", None),
("A", "M"),
("B", "A")
```
#### Output:
- class M: 0
- class A: 1
- class B: 2
```rust
/// Tree
pub enum Hiearchy {
Parent,
Tree(Vec<String>),
}
struct Output {
/// Class name
name: String,
/// Dit value
dit: usize,
/// Hierarchy tree
hieararchy_tree: Hierarchy,
}
/// Single Dit
pub fn dit(source: Vec<u8>, path: &Path) -> Vec<Output>;
```
#### Pseudocode
```
for cd in cdc.values() {
cd.dit = climb(cdc, cd);
}
fn climb(ClassDataCollection cdc, ClassData cd) -> u64{
if cd.parentClass == null {
return 0;
} else {
return 1+climb(cdc, cdc.get(&ancestor.parentClass));
}
}
```
### NOC (Number Of Children)
The number of immediate children classes of a class.
#### Minimal Test
```java
class M {}
class A extends M {}
class B extends A {}
class C extends M {}
```
HashMap structure for the relations among classes above:
```
("M", None),
("A", "M"),
("B", "A"),
("C", "M")
```
HashMap structure for the number of *immediate* children of a class:
```
(M, +1+1),
(A, +1),
```
#### Output:
- class M: 2
- class A: 1
- class B: 0
- class C: 0
```
for cd in cdc.values() {
if (cd.parentClass != null) {
cdc.get(c.parentClass).noc++;
}
}
```
## DO NOT DO THESE METRICS
### LCOM (Lack of Cohesion in Methods)
The original LCOM defines 2 sets:
- P = pairs of methods that use different attributes
- Q = pairs of methods that use the same attributes
LCOM = |P| - |Q| if |P| > |Q|
otherwise 0
#### Minimal Test
```java
class M {
private int p1, p2, p3;
public void m1(int p1) {
p1 = 1;
println(p2);
}
public void m2() {
p2 = 2;
}
}
(m1, [p1])
(m2, [p2])
```
```
for cd in cdc.values() {
let mdv = cd.methods.values().collect();
let p = 0; // cardinality of the set P
let q = 0; // cardinality of the set Q
// combination of methods
for i in 1..(mdv.len()-1) {
for j in i..(mdv.len()-1) {
// check intersection of used attributes
let intersect = false;
for a in mdv[j].usedAttributes.values() {
if mdv[i].usedAttributes.get(a) != null {
intersect = true;
q++;
break;
}
}
if intersect == false {
p++;
}
}
}
// compute lcom
if p > q {
cd.lcom = p - q;
} else {
cd.lcom = 0;
}
}
```
### RFC (Response For a Class)
It's the sum of the number of methods defined in a class plus all the distinct methods invoked inside the methods of a class. Methods with the same name but different parameters inside a single class are an issue.
*Doubt*: Should we count only the calls to our class methods? I think no.
Or should we count also calls to other classes' methods? I think yes.
- First part
1. For each class in a code, find all **defined** methods
2. We will have an HashMap: `HashMap<class_name, Vec<methods_name>`
- Second part
1. For each **invoked** methods
#### Minimal Test
```java
class M {
public void m1() { }
public void m2(A) {
A = new A();
A.a1("Hello");
A.a2("Hello");
A.a3().a1();
System.out.println("Hello");
}
private A k = new A(); // WE DO NOT CONSIDER THIS ONE!!!!
}
class A {
public void a1(String a) {}
public void a2() {}
public static A a3() { return new A(); }
}
```
- First part
1. (M, [m1, m2])
2. (A, [a1, a2])
```=
{program:123} from (1, 1) to (16, 1)
├─ {class_declaration:196} from (1, 1) to (11, 2)
│ ├─ {class:59} from (1, 1) to (1, 6) : class
│ ├─ {identifier:1} from (1, 7) to (1, 8) : M
│ ╰─ {class_body:204} from (1, 9) to (11, 2)
│ ├─ {{:63} from (1, 9) to (1, 10) : {
│ ├─ {method_declaration:240} from (2, 3) to (2, 23) : public void m1() { }
│ │ ├─ {modifiers:197} from (2, 3) to (2, 9) : public
│ │ │ ╰─ {public:97} from (2, 3) to (2, 9) : public
│ │ ├─ {void_type:117} from (2, 10) to (2, 14) : void
│ │ ├─ {identifier:1} from (2, 15) to (2, 17) : m1
│ │ ├─ {formal_parameters:234} from (2, 17) to (2, 19) : ()
│ │ │ ├─ {(:13} from (2, 17) to (2, 18) : (
│ │ │ ╰─ {):15} from (2, 18) to (2, 19) : )
│ │ ╰─ {block:152} from (2, 20) to (2, 23) : { }
│ │ ├─ {{:63} from (2, 20) to (2, 21) : {
│ │ ╰─ {}:64} from (2, 22) to (2, 23) : }
│ ├─ {method_declaration:240} from (3, 3) to (8, 4)
│ │ ├─ {modifiers:197} from (3, 3) to (3, 9) : public
│ │ │ ╰─ {public:97} from (3, 3) to (3, 9) : public
│ │ ├─ {void_type:117} from (3, 10) to (3, 14) : void
│ │ ├─ {identifier:1} from (3, 15) to (3, 17) : m2
│ │ ├─ {formal_parameters:234} from (3, 17) to (3, 25) : (A aaa1)
│ │ │ ├─ {(:13} from (3, 17) to (3, 18) : (
│ │ │ ├─ {formal_parameter:235} from (3, 18) to (3, 24) : A aaa1
│ │ │ │ ├─ {type_identifier:271} from (3, 18) to (3, 19) : A
│ │ │ │ ╰─ {identifier:1} from (3, 20) to (3, 24) : aaa1
│ │ │ ╰─ {):15} from (3, 24) to (3, 25) : )
│ │ ╰─ {block:152} from (3, 26) to (8, 4)
│ │ ├─ {{:63} from (3, 26) to (3, 27) : {
│ │ ├─ {local_variable_declaration:239} from (4, 7) to (4, 24) : A cons = new A();
│ │ │ ├─ {type_identifier:271} from (4, 7) to (4, 8) : A
│ │ │ ├─ {variable_declarator:221} from (4, 9) to (4, 23) : cons = new A()
│ │ │ │ ├─ {identifier:1} from (4, 9) to (4, 13) : cons
│ │ │ │ ├─ {=:16} from (4, 14) to (4, 15) : =
│ │ │ │ ╰─ {object_creation_expression:140} from (4, 16) to (4, 23) : new A()
│ │ │ │ ├─ {new:55} from (4, 16) to (4, 19) : new
│ │ │ │ ├─ {type_identifier:271} from (4, 20) to (4, 21) : A
│ │ │ │ ╰─ {argument_list:145} from (4, 21) to (4, 23) : ()
│ │ │ │ ├─ {(:13} from (4, 21) to (4, 22) : (
│ │ │ │ ╰─ {):15} from (4, 22) to (4, 23) : )
│ │ │ ╰─ {;:62} from (4, 23) to (4, 24) : ;
│ │ ├─ {expression_statement:153} from (5, 7) to (5, 24) : cons.a1("Hello");
│ │ │ ├─ {method_invocation:144} from (5, 7) to (5, 23) : cons.a1("Hello")
│ │ │ │ ├─ {identifier:1} from (5, 7) to (5, 11) : cons
│ │ │ │ ├─ {.:58} from (5, 11) to (5, 12) : .
│ │ │ │ ├─ {identifier:1} from (5, 12) to (5, 14) : a1
│ │ │ │ ╰─ {argument_list:145} from (5, 14) to (5, 23) : ("Hello")
│ │ │ │ ├─ {(:13} from (5, 14) to (5, 15) : (
│ │ │ │ ├─ {string_literal:11} from (5, 15) to (5, 22) : "Hello"
│ │ │ │ ╰─ {):15} from (5, 22) to (5, 23) : )
│ │ │ ╰─ {;:62} from (5, 23) to (5, 24) : ;
│ │ ├─ {expression_statement:153} from (6, 7) to (6, 21) : A.a2("Hello");
│ │ │ ├─ {method_invocation:144} from (6, 7) to (6, 20) : A.a2("Hello")
│ │ │ │ ├─ {identifier:1} from (6, 7) to (6, 8) : A
│ │ │ │ ├─ {.:58} from (6, 8) to (6, 9) : .
│ │ │ │ ├─ {identifier:1} from (6, 9) to (6, 11) : a2
│ │ │ │ ╰─ {argument_list:145} from (6, 11) to (6, 20) : ("Hello")
│ │ │ │ ├─ {(:13} from (6, 11) to (6, 12) : (
│ │ │ │ ├─ {string_literal:11} from (6, 12) to (6, 19) : "Hello"
│ │ │ │ ╰─ {):15} from (6, 19) to (6, 20) : )
│ │ │ ╰─ {;:62} from (6, 20) to (6, 21) : ;
│ │ ├─ {expression_statement:153} from (7, 7) to (7, 35) : System.out.println("Hello");
│ │ │ ├─ {method_invocation:144} from (7, 7) to (7, 34) : System.out.println("Hello")
│ │ │ │ ├─ {field_access:142} from (7, 7) to (7, 17) : System.out
│ │ │ │ │ ├─ {identifier:1} from (7, 7) to (7, 13) : System
│ │ │ │ │ ├─ {.:58} from (7, 13) to (7, 14) : .
│ │ │ │ │ ╰─ {identifier:1} from (7, 14) to (7, 17) : out
│ │ │ │ ├─ {.:58} from (7, 17) to (7, 18) : .
│ │ │ │ ├─ {identifier:1} from (7, 18) to (7, 25) : println
│ │ │ │ ╰─ {argument_list:145} from (7, 25) to (7, 34) : ("Hello")
│ │ │ │ ├─ {(:13} from (7, 25) to (7, 26) : (
│ │ │ │ ├─ {string_literal:11} from (7, 26) to (7, 33) : "Hello"
│ │ │ │ ╰─ {):15} from (7, 33) to (7, 34) : )
│ │ │ ╰─ {;:62} from (7, 34) to (7, 35) : ;
│ │ ╰─ {}:64} from (8, 3) to (8, 4) : }
│ ├─ {field_declaration:211} from (10, 5) to (10, 27) : private A k = new A();
│ │ ├─ {modifiers:197} from (10, 5) to (10, 12) : private
│ │ │ ╰─ {private:99} from (10, 5) to (10, 12) : private
│ │ ├─ {type_identifier:271} from (10, 13) to (10, 14) : A
│ │ ├─ {variable_declarator:221} from (10, 15) to (10, 26) : k = new A()
│ │ │ ├─ {identifier:1} from (10, 15) to (10, 16) : k
│ │ │ ├─ {=:16} from (10, 17) to (10, 18) : =
│ │ │ ╰─ {object_creation_expression:140} from (10, 19) to (10, 26) : new A()
│ │ │ ├─ {new:55} from (10, 19) to (10, 22) : new
│ │ │ ├─ {type_identifier:271} from (10, 23) to (10, 24) : A
│ │ │ ╰─ {argument_list:145} from (10, 24) to (10, 26) : ()
│ │ │ ├─ {(:13} from (10, 24) to (10, 25) : (
│ │ │ ╰─ {):15} from (10, 25) to (10, 26) : )
│ │ ╰─ {;:62} from (10, 26) to (10, 27) : ;
│ ├─ {comment:122} from (10, 28) to (10, 62) : // WE DO NOT CONSIDER THIS ONE!!!!
│ ╰─ {}:64} from (11, 1) to (11, 2) : }
╰─ {class_declaration:196} from (12, 1) to (15, 2)
├─ {class:59} from (12, 1) to (12, 6) : class
├─ {identifier:1} from (12, 7) to (12, 8) : A
╰─ {class_body:204} from (12, 9) to (15, 2)
├─ {{:63} from (12, 9) to (12, 10) : {
├─ {method_declaration:240} from (13, 5) to (13, 32) : public void a1(String a) {}
│ ├─ {modifiers:197} from (13, 5) to (13, 11) : public
│ │ ╰─ {public:97} from (13, 5) to (13, 11) : public
│ ├─ {void_type:117} from (13, 12) to (13, 16) : void
│ ├─ {identifier:1} from (13, 17) to (13, 19) : a1
│ ├─ {formal_parameters:234} from (13, 19) to (13, 29) : (String a)
│ │ ├─ {(:13} from (13, 19) to (13, 20) : (
│ │ ├─ {formal_parameter:235} from (13, 20) to (13, 28) : String a
│ │ │ ├─ {type_identifier:271} from (13, 20) to (13, 26) : String
│ │ │ ╰─ {identifier:1} from (13, 27) to (13, 28) : a
│ │ ╰─ {):15} from (13, 28) to (13, 29) : )
│ ╰─ {block:152} from (13, 30) to (13, 32) : {}
│ ├─ {{:63} from (13, 30) to (13, 31) : {
│ ╰─ {}:64} from (13, 31) to (13, 32) : }
├─ {method_declaration:240} from (14, 5) to (14, 24) : public void a2() {}
│ ├─ {modifiers:197} from (14, 5) to (14, 11) : public
│ │ ╰─ {public:97} from (14, 5) to (14, 11) : public
│ ├─ {void_type:117} from (14, 12) to (14, 16) : void
│ ├─ {identifier:1} from (14, 17) to (14, 19) : a2
│ ├─ {formal_parameters:234} from (14, 19) to (14, 21) : ()
│ │ ├─ {(:13} from (14, 19) to (14, 20) : (
│ │ ╰─ {):15} from (14, 20) to (14, 21) : )
│ ╰─ {block:152} from (14, 22) to (14, 24) : {}
│ ├─ {{:63} from (14, 22) to (14, 23) : {
│ ╰─ {}:64} from (14, 23) to (14, 24) : }
╰─ {}:64} from (15, 1) to (15, 2) : }
```
HashMap structure for the relations among classes above:
```
("M", None),
("A", "M"),
("B", "A"),
("C", "M")
```
HashMap structure for the number of *immediate* children of a class:
```
(M, +1+1),
(A, +1),
```
#### Output:
- class M: 3 (m1, m2, A.m1)
- class A: 2 (a1, a2)
```
// iterate over all the classes data
for cd in cdc.values() {
cd.rfc = cd.methods.len();
dc = HashSet::new(); // distinct calls
// get methods data
for md in cd.methods.values() {
// get called methods grouped by classes
for (className , calledMethods) in md.calledMethods.iter() {
dc.insert(className, HashSet::new());
// add called methods in the set to avoid duplicates
for m in calledMethods.values() {
cd.insert(className, dc.get(className).insert(m));
}
}
}
for methods in dc.iter() {
cd.rfc += methods.len();
}
}
```
### CBO (Coupling Between Objects)
The number of class dependencies (coupling) a class has. This is done by analyzing the types used in the entire class. An object is coupled to another object if one of them acts on the other. Coupling can happen through:
- method calls
- inheritance
- properties
- parameters
- method arguments
- return types
- variables in methods
- exceptions
Doubt: should we ignore Java standard classes? I think it won't be useful to consider coupling with Java standard classes (e.g. java.lang.String). In fact those classes are widely used and at the end I think it's not useful to know how much our classes use the Java standard classes.
This metric should be computed using the AST generated for a class. We can find the types used by our class from the tokens recognized by our AST. We can then increment our CBO counter if we find a type that is not a Java standard class or standard type and that has been declared in the analyzed project.
The CBO counter could be incremented during the data collection phase.
# Schemas and images
## WMC

# Java DIT & NOC analysis
## Premises
- Java packages are path-like strings, each word is separated from the other by a dot. A star wildcard can be used at the end of an import statement.
- Java packages definition is the first line of a file. If missing the package selected is the default one.
- You cannot nest packages.
- Classes defined in a Java file have visibility modifiers applied to them.
- Wildcard operator is used just at the end of a package name (Tested on eclipse).
- You can avoid imports of classes. One can specify the full name of a class (full package name + class name). What is useful is that you either specify the class name or it's full name with the package and no middle names are allowed.
## A possible design
Based on these assumptions we can try to implement DIT and NOC for Java.
The algorithm could be divided into 2 phases:
- Data collection, for each file collect:
- Package of the file
- Classes imported in the file
- Classes declared in the file (public, protected, private, nested)
- Metrics calculation
## Problem 1: import class with same name
In a file a programmer can import multiple classes with the same name. I can extend one of these imported class but which one? The choice is ambiguous.
Example:
- File p1/A.java
```
package p1;
class A{}
```
- File p2/A.java
```
package p2;
class A{}
```
- File p3/B.java
```
package p3;
import p1.A;
import p2.A;
class B extends A{}
```
Which A am I extending?
## Problem 1: SOLVED. Solution:
Java does not allow that. You can't import two classes with the same name. The code would not compile. One solution allowed is to remove the conflicting imports and specify the full class name:
- File p3/B.java
```
package p3;
class B extends p1.A{}
class C extends p2.A{}
```
## Problem 2: ambiguous imports with stars wildcards
Actually not really recommended but largely used. It's a problem because it makes class name resolution ambiguous. For example:
- File p1/A.java
```
package p1;
class A{}
```
- File p2/A.java
```
package p2;
class A{}
```
- File p3/B.java
```
package p3;
import p1.*;
import p2.*;
class B extends A{}
```
Same thing as above. What A am I extending?
## Problem 2: SOLVED. Solution:
Java does not allow that and the code won't compile. A is ambiguous so we are forced to import the correct class we want to extends or to clarify the full path for our class (2 solutions):
- File p3/B.java
```
package p3;
class B extends p1.A{}
class C extends p2.A{}
```
- File p3/B.java
```
package p3;
import p1.*;
import p2.*;
import p1.A;
class B extends A{} // p1.a
```
## Problem 3: what about Java modules?
Modules are an abstraction placed above the packages. I've never seen or used them. I think for the moment I should focus on packages as higher form of abstraction.
## Problem 4: class visibility and nested classes?
- File A1.java
```
class A1{
class B1{}
}
class B1{}
class A2 extends A1{
class B2 extends B1{} // it's A1.B1
}
```
- File A1.java
```
class A1{
private class B1{}
}
class B1{}
class A2 extends A1{
class B2 extends B1{} // it's p1.B1
}
```
How to know where the B1 class is defined?
## Results - Updated at 05 April 2022
For now the an implementation seems very complex and hard. The main disadvantages of an implementation are:
- Data structures and collection phase very language-dependant
- All the implementations found on GitHub are using very language specific tricks to compute DIT and NOC easily (e.g. Bytecode analysis, Eclipse plugin, Java Symbol Solver library, Visitor pattern, etc. ).
- An implementation would take a lot of time and be very complex.
- An implementation would not be totally accurate and cover all the possible future cases. For now this implementation covers just the main cases but as the Java language updates our implementation may not work anymore (e.g. Java modules).