Skip to content

Commit

Permalink
feat(visualisation): Spoon visualiser improvement for accessing JavaD…
Browse files Browse the repository at this point in the history
…oc (#3101)
  • Loading branch information
arnobl authored and nharrand committed Sep 15, 2019
1 parent c3af255 commit a7b130a
Show file tree
Hide file tree
Showing 18 changed files with 169 additions and 44 deletions.
7 changes: 7 additions & 0 deletions spoon-visualisation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Then go into the `target/modules` folder and launch: `java --module-path . --add
- [implicit elements masking](#feature-implicit-elements-masking)
- [export as text](#feature-export-as-text)
- [analysis level](#feature-analysis-level)
- [open JavaDoc](#feature-open-javadoc)
- [one argument to load a Java file](#feature-one-argument-to-load-a-java-file)

## Feature: selection highlighting
Expand Down Expand Up @@ -69,6 +70,12 @@ The `auto` level tries to detect the level automatically by starting parsing the
![auto](doc/appAuto.png)


## Feature: Open Javadoc

A user can click on class names in the Spoon AST view to open the corresponding Spoon Javadoc.

![auto](doc/openDoc.png)


## Feature: one argument to load a Java file

Expand Down
Binary file modified spoon-visualisation/doc/appAuto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified spoon-visualisation/doc/appClassElt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified spoon-visualisation/doc/appExp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified spoon-visualisation/doc/appFeat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified spoon-visualisation/doc/appFeat2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified spoon-visualisation/doc/appStatement.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added spoon-visualisation/doc/openDoc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions spoon-visualisation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
<configuration>
<testFailureIgnore>false</testFailureIgnore>
<useModulePath>false</useModulePath>
<argLine>-Xmx4000m
<argLine>-Xmx2000m
-Dglass.platform=Monocle
-Dheadless.geometry=1280x1024-32
-Djava.awt.headless=true
Expand Down Expand Up @@ -204,7 +204,7 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.1</version>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -225,6 +225,12 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<repositories>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.List;
import javafx.application.Platform;
import javafx.scene.control.TreeView;
import javafx.scene.text.TextFlow;
import org.jetbrains.annotations.NotNull;
import spoon.visualisation.spoon.SpoonTreeItem;

Expand All @@ -34,13 +35,13 @@
*/
public class SelectCodeItem extends CommandImpl {
final int caretPosition;
final TreeView<String> spoonTree;
final TreeView<TextFlow> spoonTree;

/**
* @param caretPosition The current caret position of the code view
* @param spoonTree The tree view of the Spoon AST
*/
public SelectCodeItem(final int caretPosition, final @NotNull TreeView<String> spoonTree) {
public SelectCodeItem(final int caretPosition, final @NotNull TreeView<TextFlow> spoonTree) {
super();
this.caretPosition = caretPosition;
this.spoonTree = spoonTree;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
package spoon.visualisation.command;

import javafx.scene.control.TreeView;
import javafx.scene.text.TextFlow;
import org.jetbrains.annotations.NotNull;
import spoon.visualisation.spoon.SpoonElementVisitor;
import spoon.visualisation.spoon.TreePrinter;
Expand All @@ -31,15 +32,15 @@
*/
public class UpdateSpoonTree extends SpoonTreeCmdBase {
/** The tree widget that shows the Spoon tree. */
private final @NotNull TreeView<String> spoonAST;
private final @NotNull TreeView<TextFlow> spoonAST;

/**
* @param spoonAST The tree view of the Spoon AST
* @param hideImplicit Hide implicit elements?
* @param code The code to analyse
* @param treeLevel The tree level analysis to use
*/
public UpdateSpoonTree(final @NotNull TreeView<String> spoonAST, final boolean hideImplicit, final @NotNull String code,
public UpdateSpoonTree(final @NotNull TreeView<TextFlow> spoonAST, final boolean hideImplicit, final @NotNull String code,
final @NotNull TreeLevel treeLevel) {
super(hideImplicit, code, treeLevel);
this.spoonAST = spoonAST;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import javafx.scene.control.TextArea;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeView;
import javafx.scene.text.TextFlow;
import javafx.stage.FileChooser;
import spoon.visualisation.command.SaveTreeText;
import spoon.visualisation.command.SelectCodeItem;
Expand All @@ -47,7 +48,7 @@
*/
public class SpoonCodeInstrument extends JfxInstrument implements Initializable {
@FXML private TextArea spoonCode;
@FXML private TreeView<String> spoonAST;
@FXML private TreeView<TextFlow> spoonAST;
@FXML private CheckBox hideImplicit;
@FXML private ComboBox<TreeLevel> treeLevel;
@FXML private Button save;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
package spoon.visualisation.spoon;

import java.util.List;
import javafx.scene.text.TextFlow;
import org.jetbrains.annotations.NotNull;

/**
Expand All @@ -35,5 +36,5 @@ public abstract class SpoonElementVisitor {
this.levelsToIgnore = levelsToIgnore;
}

public abstract void accept(final int level, final @NotNull String label, final @NotNull List<Integer> linesPosition);
public abstract void accept(final int level, final @NotNull TextFlow label, final @NotNull List<Integer> linesPosition);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
package spoon.visualisation.spoon;

import javafx.scene.control.TreeItem;
import javafx.scene.text.TextFlow;

/**
* As JavaFX tree item cannot embed data, this tree item class embeds the code position
* of the corresponding code element.
*/
public class SpoonTreeItem extends TreeItem<String> {
public class SpoonTreeItem extends TreeItem<TextFlow> {
public final int startPosition;
public final int endPosition;

Expand All @@ -36,7 +37,7 @@ public class SpoonTreeItem extends TreeItem<String> {
* @param startPosition The starting position in the code of the corresponding code element
* @param endPosition The ending position in the code of the corresponding code element
*/
public SpoonTreeItem(final String text, final int startPosition, final int endPosition) {
public SpoonTreeItem(final TextFlow text, final int startPosition, final int endPosition) {
super(text);
this.startPosition = startPosition;
this.endPosition = endPosition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@
*/
package spoon.visualisation.spoon;

import io.github.interacto.command.library.OpenWebPage;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Tooltip;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import spoon.reflect.code.CtAbstractInvocation;
Expand Down Expand Up @@ -81,9 +88,32 @@ public void scan(final CtRole role, final CtElement element) {
protected void enter(final CtElement elt) {
level++;

// Removing the trail Impl. Make the assumption that any implementation class
// has its interface (same name without the trailing 'Impl'
String label = elt.getClass().getSimpleName().replaceAll("Impl$", "");
final String simpleName = elt.getClass().getSimpleName();
// We assume that the Spoon API follows this rule:
// the implementation class name is the interface name plus 'Impl'
// We want to display the main interface corresponding to the given class.
// So looking for this interface. If not found, the current class is used.
final Class<?> cl = Arrays
.stream(elt.getClass().getInterfaces())
.filter(interf -> simpleName.equals(interf.getSimpleName() + "Impl"))
.findFirst()
.orElse(elt.getClass());

// We want to be able to click on the interface name to open its JavaDoc
final Hyperlink label = new Hyperlink(cl.getSimpleName());
final TextFlow flow = new TextFlow(label);
final String url = "http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/" + cl.getName().replace('.', '/') + ".html";
Tooltip.install(label, new Tooltip(url));

// Clicking on the link opens the doc
// 'new Thread' because otherwise the app freezes (run in the UI thread)
label.setOnAction(evt -> new Thread(() -> {
final OpenWebPage cmd = new OpenWebPage();
cmd.setUri(URI.create(url));
if(cmd.canDo()) {
cmd.doIt();
}
}, "OPEN_SPOON_DOC_THREAD").start());

final SourcePosition pos = elt.getPosition();
final List<Integer> lines;
Expand All @@ -95,62 +125,70 @@ protected void enter(final CtElement elt) {
}

if(elt.isImplicit()) {
label += " (implicit)";
flow.getChildren().add(new Text("(implicit)"));
}

if(currRole != null) {
label += " (role: " + currRole + ")";
flow.getChildren().add(new Text("(role: " + currRole + ")"));
}

if(elt instanceof CtType<?>) {
printer.accept(level, label + ": " + ((CtType<?>) elt).getSimpleName(), lines);
flow.getChildren().add(new Text(": " + ((CtType<?>) elt).getSimpleName()));
printer.accept(level, flow, lines);
return;
}

if(elt instanceof CtNamedElement) {
printer.accept(level, label + ": " + ((CtNamedElement) elt).getSimpleName(), lines);
flow.getChildren().add(new Text(": " + ((CtNamedElement) elt).getSimpleName()));
printer.accept(level, flow, lines);
return;
}

if(elt instanceof CtReference) {
printer.accept(level, label + ": " + ((CtReference) elt).getSimpleName(), lines);
flow.getChildren().add(new Text(": " + ((CtReference) elt).getSimpleName()));
printer.accept(level, flow, lines);
return;
}

if(elt instanceof CtVariableAccess<?>) {
final CtVariableAccess<?> varaccess = (CtVariableAccess<?>) elt;
final String txt = ": " + ((varaccess.getVariable() != null) ? varaccess.getVariable().getSimpleName() : "(null)");
printer.accept(level, label + txt, lines);
flow.getChildren().add(new Text(txt));
printer.accept(level, flow, lines);
return;
}

if(elt instanceof CtTypeAccess<?>) {
final CtTypeAccess<?> typeaccess = (CtTypeAccess<?>) elt;
final String txt = ": " + ((typeaccess.getAccessedType() != null) ? typeaccess.getAccessedType().getSimpleName() : "(null)");
printer.accept(level, label + txt, lines);
flow.getChildren().add(new Text(txt));
printer.accept(level, flow, lines);
return;
}

if(elt instanceof CtLiteral<?>) {
printer.accept(level, label + ": " + ((CtLiteral<?>) elt).getValue(), lines);
flow.getChildren().add(new Text(": " + ((CtLiteral<?>) elt).getValue()));
printer.accept(level, flow, lines);
return;
}

if(elt instanceof CtAbstractInvocation<?>) {
final CtAbstractInvocation<?> invoc = (CtAbstractInvocation<?>) elt;
final String txt = ": " + ((invoc.getExecutable() != null) ? invoc.getExecutable().getSimpleName() : "(null)");
printer.accept(level, label + txt, lines);
flow.getChildren().add(new Text(txt));
printer.accept(level, flow, lines);
return;
}

if(elt instanceof CtAnnotation<?>) {
final CtAnnotation<?> annot = (CtAnnotation<?>) elt;
final String txt = ": " + ((annot.getAnnotationType() != null) ? annot.getAnnotationType().getSimpleName() : "(null)");
printer.accept(level, label + txt, lines);
flow.getChildren().add(new Text(txt));
printer.accept(level, flow, lines);
return;
}

printer.accept(level, label, lines);
printer.accept(level, flow, lines);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import java.io.PrintStream;
import java.util.List;
import javafx.scene.text.TextFlow;
import org.jetbrains.annotations.NotNull;

/**
Expand All @@ -41,7 +42,7 @@ public StreamPrinter(final @NotNull PrintStream printer, final int levelsToIgnor
}

@Override
public void accept(final int level, final @NotNull String label, final @NotNull List<Integer> lines) {
public void accept(final int level, final @NotNull TextFlow label, final @NotNull List<Integer> lines) {
if(level <= levelsToIgnore) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,24 @@
import java.util.List;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.text.TextFlow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* The printer that prints the Spoon AST into a JavaFX tree view.
*/
public class TreePrinter extends SpoonElementVisitor {
private final @NotNull TreeView<String> tree;
private @Nullable TreeItem<String> currItem;
private final @NotNull TreeView<TextFlow> tree;
private @Nullable TreeItem<TextFlow> currItem;
/** The current depth level in the tree view */
private int currLevel;

/**
* @param tree The tree view to use to print the Spoon AST
* @param levelsToIgnore The number of tree levels to ignore before starting printing
*/
public TreePrinter(final @NotNull TreeView<String> tree, final int levelsToIgnore) {
public TreePrinter(final @NotNull TreeView<TextFlow> tree, final int levelsToIgnore) {
super(levelsToIgnore);
this.tree = tree;
this.tree.setRoot(null);
Expand All @@ -49,7 +50,7 @@ public TreePrinter(final @NotNull TreeView<String> tree, final int levelsToIgnor
}

@Override
public void accept(final int level, final @NotNull String label, final @NotNull List<Integer> lines) {
public void accept(final int level, final @NotNull TextFlow label, final @NotNull List<Integer> lines) {
// level > 1 because the root element must be created to be then masked as several real tree roots may exist
// Example: three statements with the statement level.
// level <= levelsToIgnore: depending on the analysis level, some root elements must be hidden
Expand All @@ -68,7 +69,7 @@ public void accept(final int level, final @NotNull String label, final @NotNull
if(currLevel < level) {
currItem.getChildren().add(item);
}else {
TreeItem<String> parent = currItem.getParent();
TreeItem<TextFlow> parent = currItem.getParent();

while(currLevel > level) {
currLevel--;
Expand Down
Loading

0 comments on commit a7b130a

Please sign in to comment.