HashMaps - the old solution
Each Analyzer
calculates metrics for a number of files, or an entire project. In AuTA 1.0, the results were saved as a HashMap<String, Object>
. An example of an old Results object:
{
"id": "1936b31cabdd630d716f988243d8c6ad3c302603",
"dynamic": [],
"static": [
{
"filename": "home/tim/programming/trampoline/run_trampoline.py",
"results": [{
"name": "method length",
"results": {
"randomString( stringLength = 64 )": 3,
"login( )": 6,
"create_assignment( token )": 9,
"generate_sha( random_string )": 3
}
},
{
"name": "cyclomatic complexity",
"results": {
"randomString( stringLength = 64 )": 2,
"login( )": 1,
"create_assignment( token )": 1,
"generate_sha( random_string )": 1
}
}]
}
]
}
The reasoning behind using a HashMap
that maps a String
to an arbitrary Object
was that each analyzer could produce results in an arbitrary format. The results object had no fixed schema, which made it a lot easier to add new Analyzer results to the final results object.
However, the absence of a fixed schema made parsing the Results object to create a Report for GitLab or Benchmarking very difficult, as adding new Analyzers that produced a different results format meant that code had to be modified in a number of different places.
Entity - Our new solution
We have devised a new solution to store results for different analyzers. This is a tree-like object called an Entity
. An Entity has an EntityLevel
, which can be one of the following: PROJECT
,FILE
,METHOD
,CLASS
. Each entity has a parent Entity, a list of children and Metrics which are coupled to that Entity.
Each analyzer creates a ProjectEntity with a number of children that can be associated with different files, methods or classes in the project. The checker merges each ProjectEntity into a single ProjectEntity by comparing the names of each child entity (which includes the name of the parent entities) and transferring the metrics to the correct entity.
Metrics
A new Metric<T>
class has been created, which contains a MetricName
enum (the name of the metric) and the value of that Metric. Metrics of the same type are grouped using the type of the metric value (Integer, String, etc.)
Storing the type of the metric (e.g. IntegerMetric(MetricName.CYCLOMATIC_COMPLEXITY, 10)
) makes serialization easier, and makes it easier to compare Metrics when benchmarking. Serialization is required to send the metric from the Worker to the Core, and is handled by a custom GsonTypeAdapter
which adds the class to the JSON object which is then used to rebuild the correct type when deserializing the Metric.