manucapo / Gardener

A project to automatically generate plantUML sequence diagrams from java methods.
MIT License
3 stars 2 forks source link

Update On program progess. #12

Open manucapo opened 2 years ago

manucapo commented 2 years ago

Here is an overview of the changes that have occurred since the last version of the program:

1) Project capabilities

The parser has been simplified so that now only one method can take care of both single method calls and nested method calls. As an example, the following two method calls can now be handled by the same function

     test1("2",1,true);
    s.toString().split(" ").clone().toString();

The parser has also been made more powerful by delaying the conversion of the method calls to strings as long as possible. The parser now uses the actual AST nodes to extract more context information from the method calls and only convers the calls to Strings when necessary.

The current dataflow inside the default parser looks like this:

Get Method call -> Extract all method calls nested inside the original call -> Extract the names of every nested method call and store in a list -> For every nested method call try to resolve the call target and store in a list ( Check if the call is a method defined in the original class ->
Attempt to isolate the most relevant scope information -> Check if scope is a parameter defined inside a catch statement -> Check if scope is a parameter defined in the method definition -> Check if scope is a variable defined inside the method -> Check if scope is a variable defined inside the original class -> Check if scope is a field access expression ( i.e System.out ) -> Check if method is defined in any of the packages provided by the user as project dependencies -> Check if scope is defined in any of the packages provided by the user as project dependencies ->
If last method call targeted a collection (i.e list) try to find class contained in collection -> Check if method is defined in any of the packages provided by the user as project dependencies -> If every check failed then deal with the failure ) -> Save the list of method names and corresponding targets so they can be added to the diagram.

2) Project structure

I restructured the project to be more "Object oriented". In order to better conform to a "three tier architecture" ( since we are supposed to). I tried to decouple the UI-Algorithm-Database layers as much as possible by applying some OOD patterns.

I have to admit that its probably over kill for the current size of the program and the codebase is definitely rather bloated. I only did the extra effort this since this is supposed to be for an OOD class after all.

Some of the changes I implemented:

The user interacts with the SequenceDiagramGenerator class. The user can configure this class as desired and ask it to generate a sequence diagram.

The diagram generator is decoupled from the parser layer by a strategy pattern (https://www.tutorialspoint.com/design_pattern/strategy_pattern.htm). This allows us to quickly implement new parsing algorithms and to easily swap between them at runtime.

To do this all we have to do is make a new class that extends an existing parser class. The user only needs to know the members of the enum ParserType and pass one to the diagram generator in order to configure the desired parsing algorithm using a factory. (https://www.tutorialspoint.com/design_pattern/factory_pattern.htm)

The parsing layer is decoupled from the DiagramStructure (database) by an event/messaging system.

Information is passed to the database by broadcasting messages. messages can be broadcasted by any class that implements the IMessageSender interface. The structure must first be subscribed to the broadcasting object using the addObserver() method.

Information is broadcasted by the structure to any subscribed listeners automatically whenever it gets updated by implementing an observer pattern (https://www.tutorialspoint.com/design_pattern/observer_pattern.htm)

The last of the new classes added are the ones that implement INodeExplorer. This interface decouples some of the required operations on the AST from the parsing of the actual method call nodes. The decoupling is made again using a factory pattern.

3) known bugs

There are some bugs on the main branch with dealing with the parsing of nested method calls. For example the call:

        System.out.println(test1(s.toString(),Integer.parseInt(f),bool));

wont be parsed correctly. This has to do with a bug in extracting the actual scope of nested method calls. This bugs are fixed in the newest Dev version but more testing needs to be done before merging.

The program currently cant resolve the type of objects returned by generic methods. Sometimes the type can still be recovered from other context. The test Method 19 for example shows this behaviour:

public String test19(String s, int j, boolean bool){
    Node node = null;
    if (node.findAncestor(NameExpr.class).isPresent()){
        s.equals(node.findAncestor(NameExpr.class).get().toString());
    }

    return "26";
}

As can be seen on the sequence diagram test19.png . The type returned by the Optional.get() is resolved as a generic N. In this case the target of the equals() method can still be resolved using the scope.

This could prove to be a limitation of the ast method. Honestly i don't know enough about java generics.

4) Things that could still be implemented

The option to detect code blocks.

For example IF blocks, Switch blocks, loop blocks, etc. I think detecting the blocks is not that hard using the AST. The question is how to properly translate this information to the plantUML syntax.

Recursive Method Call parsing.

Currently the parser only looks at one layer of method calls. It should in theory be possible to parse several layers of calls.