Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Javadoc comments #91

Merged
merged 10 commits into from
Jan 23, 2024
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Start by reading up on Exercism analyzers:

To learn how the Java Analyzer works:

- Read the [project documentation][project-documentation].
- Browse [existing exercise analyzer implementations][browse-analyzers].

### Writing comments
Expand All @@ -46,3 +47,4 @@ See [the Analyzer comment guidelines][analyzer-comments-guidelines] for guidance
[analyzer-docs-interface]: https://exercism.org/docs/building/tooling/analyzers/interface
[community-docs]: https://exercism.org/docs/community/being-a-good-community-member
[contributing-docs-github]: https://exercism.org/docs/building/github
[project-documentation]: https://exercism.github.io/java-analyzer/
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

The Java analyzer uses [Abstract Syntax Trees][ast-wiki] (ASTs) to analyze submitted solutions using the [`javaparser`][javaparser] library.

The documentation for this project can be found at [exercism.github.io/java-analyzer/][documentation].

## Contributing

If you want to contribute to the Java analyzer, please refer to the [Contributing Guide][contributing-guide].
Expand Down Expand Up @@ -62,4 +64,5 @@ bin/run-tests-in-docker.sh

[ast-wiki]: https://en.wikipedia.org/wiki/Abstract_syntax_tree
[contributing-guide]: https://github.com/exercism/java-analyzer/blob/main/CONTRIBUTING.md
[javaparser]: https://github.com/javaparser/javaparser
[documentation]: https://exercism.github.io/java-analyzer/
[javaparser]: https://github.com/javaparser/javaparser
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {

group = "org.exercism"
version = "1.0-SNAPSHOT"
mainClassName = "analyzer.Main"
mainClassName = "analyzer.AnalyzerCli"

repositories {
mavenCentral()
Expand Down
6 changes: 6 additions & 0 deletions src/doc/docs/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Welcome to exercism/java-analyzer

Welcome to the Exercism Java analyzer documentation! 👋

!!! note
This documentation is still under construction.
In the meantime, we suggest having a look at the [Javadoc][javadoc].

[javadoc]: /java-analyzer/api/
39 changes: 39 additions & 0 deletions src/main/java/analyzer/Analysis.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,72 @@
import java.util.Set;

/**
* This class is used to collect analysis results in the form of comments, tags and an optional summary.
*
* @see <a href="https://exercism.org/docs/building/tooling/analyzers/interface">The analyzer interface in the Exercism documentation</a>
*/
public class Analysis {
private String summary;
private final Set<Comment> comments = new LinkedHashSet<>();
private final Set<String> tags = new LinkedHashSet<>();

/**
* The summary is a short description of the complete analysis result.
* It is {@code null} by default.
*
* @return The summary if set, {@code null} otherwise.
*/
public String getSummary() {
return summary;
}

/**
* Set the summary of the analysis.
* If the summary was set previously, setting it again will overwrite it.
* The summary can be cleared by setting it to {@code null}.
*
* @param summary The summary to set.
*/
public void setSummary(String summary) {
this.summary = summary;
}

/**
* Retrieve a copy of the comments added to this analysis.
* The resulting list is guaranteed to contain no duplicates.
*
* @return List of comments.
*/
public List<Comment> getComments() {
return List.copyOf(comments);
}

/**
* Retrieve a copy of the tags added to this analysis.
* The resulting list is guaranteed to contain no duplicates.
*
* @return List of tags.
*/
public List<String> getTags() {
return List.copyOf(tags);
}

/**
* Add a new comment to the analysis.
* This does nothing if a comment with the same values was added previously.
*
* @param comment The comment to add.
*/
public void addComment(Comment comment) {
comments.add(comment);
}

/**
* Add a new tag to the analysis.
* This does nothing if the same tag was added previously.
*
* @param tag The tag to add.
*/
public void addTag(String tag) {
tags.add(tag);
}
Expand Down
18 changes: 13 additions & 5 deletions src/main/java/analyzer/Analyzer.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package analyzer;

import com.github.javaparser.ast.CompilationUnit;

import java.util.List;

/**
* The {@code Analyzer} interface is used to implement both global and exercise-specific analyzers.
*/
public interface Analyzer {
void analyze(List<CompilationUnit> compilationUnits, Analysis analysis);
/**
* Analyze the given solution and append analysis results to the given analysis.
* The {@code analyze} method of each analyzer is invoked once for the whole submitted solution.
*
* @param solution The solution that should be analyzed.
* @param analysis The analysis instance used to collect results.
* This instance is shared across all analyzers, and should be used to add comments and tags,
* or set a summary.
*/
void analyze(Solution solution, Analysis analysis);
}
50 changes: 50 additions & 0 deletions src/main/java/analyzer/AnalyzerCli.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package analyzer;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;

/**
* The main entrypoint to the Java analyzer from the command-line.
* The CLI expects three arguments and is used like this:
*
* <pre>
* java -jar java-analyzer.jar exercise-slug /path/to/input/ /path/to/output/
* </pre>
*/
public class AnalyzerCli {

private static boolean isNotValidDirectory(String p) {
return !p.endsWith("/") || !new File(p).isDirectory();
}

public static void main(String... args) throws IOException {
if (args.length < 3) {
System.err.println("Invalid arguments. Usage: java-analyzer <exercise slug> <exercise directory> <output directory>");
System.exit(-1);
}

String slug = args[0];
String inputDirectory = args[1];
String outputDirectory = args[2];

if (isNotValidDirectory(inputDirectory)) {
System.err.println("Invalid input directory. Must be a valid directory and end with a slash.");
System.exit(-1);
}
if (isNotValidDirectory(outputDirectory)) {
System.err.println("Invalid output directory. Must be a valid directory and end with a slash.");
System.exit(-1);
}

var solution = new SubmittedSolution(slug, Path.of(inputDirectory));
var analysis = AnalyzerRoot.analyze(solution);

try (var analysisWriter = new FileWriter(Path.of(outputDirectory, "analysis.json").toFile());
var tagsWriter = new FileWriter(Path.of(outputDirectory, "tags.json").toFile())) {
var output = new OutputWriter(analysisWriter, tagsWriter);
output.write(analysis);
}
}
}
22 changes: 17 additions & 5 deletions src/main/java/analyzer/AnalyzerRoot.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,33 @@
import analyzer.exercises.lasagna.LasagnaAnalyzer;
import analyzer.exercises.leap.LeapAnalyzer;
import analyzer.exercises.twofer.TwoferAnalyzer;
import com.github.javaparser.ast.CompilationUnit;

import java.util.ArrayList;
import java.util.List;

/**
* The {@code AnalyzerRoot} is the initial entrypoint when analyzing a solution.
* Its job is to delegate the analysis of the parsed Java files to the global and exercise-specific analyzers.
*/
public class AnalyzerRoot {

public static Analysis analyze(String slug, List<CompilationUnit> compilationUnits) {
private AnalyzerRoot() {
}

/**
* Perform the analysis of a solution.
*
* @param solution The solution being analyzed.
* @return The aggregated analysis of all applicable analyzers.
*/
public static Analysis analyze(Solution solution) {
var analysis = new Analysis();

for (Analyzer analyzer : createAnalyzers(slug)) {
analyzer.analyze(compilationUnits, analysis);
for (Analyzer analyzer : createAnalyzers(solution.getSlug())) {
analyzer.analyze(solution, analysis);
}

if (analysis.getComments().stream().anyMatch(x -> x.getType() != CommentType.CELEBRATORY)) {
if (analysis.getComments().stream().anyMatch(x -> x.getType() != Comment.Type.CELEBRATORY)) {
analysis.addComment(new FeedbackRequest());
}

Expand Down
68 changes: 66 additions & 2 deletions src/main/java/analyzer/Comment.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,82 @@
import java.util.Objects;

/**
* The {@link Comment} class models a single comment in the analysis output.
* Each comment has a unique key that translates to a Markdown template in the
* <a href="https://github.com/exercism/website-copy">exercism/website-copy</a> repository.
* <p>
* If the Markdown template contains any parameters, classes inheriting from {@link Comment} should override the
* {@link Comment#getParameters()} method to return the parameter keys and values specific to that template.
* <p>
* Override the {@link Comment#getType()} method to change the {@link Type} associated to the comment.
*
* @see <a href="https://exercism.org/docs/building/tooling/analyzers/interface">The analyzer interface in the Exercism documentation</a>
* @see <a href="https://github.com/exercism/website-copy/tree/main/analyzer-comments/java">Analyzer comments for the Java track in the website-copy</a>
*/
public abstract class Comment {

/**
* The type of comment.
* Note that the order defined here corresponds to the order in which comments are sorted in the analyzer output.
*
* @see <a href="https://exercism.org/docs/building/tooling/analyzers/interface#h-type-optional">Documentation on comment types</a>
*/
public enum Type {
ESSENTIAL,
ACTIONABLE,
INFORMATIVE,
CELEBRATORY
}

/**
* The comment key is a {@link String} that uniquely identifies the comment.
* <p>
* Comment keys use the format {@code "java.<scope>.<name>"}.
* The {@code <scope>} can be either {@code general} for general comments,
* or the slug of the exercise for exercise-specific comments.
* The {@code <name>} specifies the name of the comment.
* <p>
* The combination of {@code <scope>} and {@code <name>} must be unique, and defines the location of the
* Markdown template in the <a href="https://github.com/exercism/website-copy">exercism/website-copy</a> repository.
* <p>
* For example, the comment key {@code "java.hello-world.foo_bar"} would translate to the Markdown file at
* {@code analyzer-comments/java/hello-world/foo_bar.md}.
*
* @return The unique comment key.
*/
public abstract String getKey();

/**
* Each parameter in the Markdown template should have a corresponding parameter in the comment.
* Parameters in Markdown templates are of the form {@code %<parameterName>s}.
* <p>
* For example, if the Markdown template contains a parameter {@code %<methodName>s},
* the implementation of this method could look like this:
* <pre>{@code
* public Map<String, String> getParameters() {
* return Map.of("methodName", "theNameOfTheMethod");
* }
* }</pre>
*
* @return The parameters for the comment.
*/
public Map<String, String> getParameters() {
return Map.of();
}

public CommentType getType() {
return CommentType.INFORMATIVE;
/**
* <ul>
* <li>Use {@link Type#ESSENTIAL} to instruct students that they <b>must</b> address it.</li>
* <li>Use {@link Type#ACTIONABLE} to instruct students that they could improve their solution by addressing it.</li>
* <li>Use {@link Type#INFORMATIVE} to give students extra information without expecting them to use it.</li>
* <li>Use {@link Type#CELEBRATORY} to tell students that they did something right.</li>
* </ul>
*
* @return The type of the comment.
* @see <a href="https://exercism.org/docs/building/tooling/analyzers/interface#h-type-optional">Documentation on comment types</a>
*/
public Type getType() {
return Type.INFORMATIVE;
}

@Override
Expand Down
11 changes: 0 additions & 11 deletions src/main/java/analyzer/CommentType.java

This file was deleted.

68 changes: 0 additions & 68 deletions src/main/java/analyzer/Main.java

This file was deleted.

Loading