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

Refactor to use the Spoon library #109

Merged
merged 33 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1d3a839
Fix errors in output caused by log4j in offline mode
sanderploegsma Jan 23, 2024
35c7156
Remove unused class
sanderploegsma Jan 23, 2024
865c2a2
Migrate to JUnit 5
sanderploegsma Jan 24, 2024
b7d7e33
Refactor
sanderploegsma Jan 25, 2024
7d0afbf
Reformat and clean up normalizers
sanderploegsma Jan 26, 2024
4abc399
Write to given output directory instead of input directory
sanderploegsma Jan 26, 2024
d36ece5
Fix recursive placeholder generation
sanderploegsma Jan 26, 2024
c7f1c8d
Add CONTRIBUTING.md
sanderploegsma Jan 26, 2024
2ed234b
Update README.md
sanderploegsma Jan 26, 2024
743b7dd
Remove unused files
sanderploegsma Jan 26, 2024
6b1d5ab
Update .dockerignore
sanderploegsma Jan 26, 2024
b2f183b
Update unit tests after fixing recursion bug
sanderploegsma Jan 26, 2024
e54f4dc
Make logging level configurable through `LOGGING_LEVEL` env var
sanderploegsma Jan 26, 2024
a8842a4
Order mapping.json keys alphabetically
sanderploegsma Jan 26, 2024
9d11791
Set up test coverage reporting
sanderploegsma Jan 26, 2024
fe4f4d3
Add coverage badge to README
sanderploegsma Jan 26, 2024
a59ee95
Rewrite using https://github.com/INRIA/spoon
sanderploegsma Jan 26, 2024
45c4ede
Add unit tests
sanderploegsma Jan 26, 2024
7fd3b71
Use approvals for snapshot testing
sanderploegsma Jan 26, 2024
6ef6f7f
Add unit tests for OutputWriter
sanderploegsma Jan 29, 2024
1a212dc
Add Javadoc to processor classes
sanderploegsma Jan 29, 2024
62e94ff
Merge smoke test scenarios into one
sanderploegsma Jan 29, 2024
016c78e
Set compliance level to 19, add test cases for record classes and swi…
sanderploegsma Jan 29, 2024
187961b
Log placeholders while renaming
sanderploegsma Jan 29, 2024
7819b0e
Do not replace placeholders with new placeholders
sanderploegsma Jan 31, 2024
fcd53f9
Fix renaming of record components
sanderploegsma Jan 31, 2024
7b21674
Mark snapshot files as binary to preserve line endings
sanderploegsma Jan 31, 2024
a94cf80
Merge branch 'main' into spoon
sanderploegsma Feb 1, 2024
ce115ec
Fix enum formatting
sanderploegsma Feb 4, 2024
73624d3
Consistent formatting using google-java-format
sanderploegsma Feb 4, 2024
a6df3c6
Change printing and formatting options
sanderploegsma Feb 4, 2024
1a50777
Merge branch 'main' into spoon
sanderploegsma Mar 5, 2024
514d9b0
Merge branch 'main' into spoon
sanderploegsma Mar 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
.appends
.git
.github
.gitignore
.gitattributes
.gradle
.idea
build
gradle
tests
.dockerignore
.gitattributes
.gitignore
Dockerfile
bin/generate-test-data.sh
bin/run-in-docker.sh
bin/run-tests-in-docker.sh
bin/run-tests.sh
src/test/
tests/
bin/run-tests-in-docker.sh
gradlew
gradlew.bat
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.approved.* binary
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:
distribution: "temurin"
- name: Test with Gradle
run: ./gradlew --no-daemon --continue test
- name: Upload test coverage
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949
- name: Create test summary
uses: test-summary/action@fee35d7df20790255fe6aa92cf0f6d28092ecf2f
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.settings/
.project
.classpath
src/test/resources/**/*.received.txt
tests/**/*/representation.txt
tests/**/*/representation.json
tests/**/*/mapping.json
Expand Down
36 changes: 36 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Contributing

Thank you so much for contributing! 🎉

We welcome contributions of all sorts and sizes, from reporting issues to submitting patches, as well as joining discussions on the [Java forum][java-forum].

Unprompted (without an issue) PRs with small bugfixes are welcome.
However, if you want to propose bigger changes, make sure to post on the [forum][java-forum] first so that we can discuss it.

## Code of Conduct

Help us keep Exercism welcoming.
Please read and abide by the [Code of Conduct][coc].

## Contributing to Exercism

If you are new to contributing to Exercism, please read:

- [Being a good community member][community-docs]
- [Contributing via GitHub][contributing-docs-github]

## How does this project work?

Start by reading up on Exercism Representers:

- [Exercism Representers introduction][representer-docs-intro]
- [The Representer interface][representer-docs-interface]
- [Normalization within Representers][representer-docs-normalization]

[coc]: https://github.com/exercism/java-analyzer/blob/main/CODE_OF_CONDUCT.md
[java-forum]: https://forum.exercism.org/c/programming/java/91
[community-docs]: https://exercism.org/docs/community/being-a-good-community-member
[contributing-docs-github]: https://exercism.org/docs/building/github
[representer-docs-interface]: https://exercism.org/docs/building/tooling/representers/interface
[representer-docs-intro]: https://exercism.org/docs/building/tooling/representers
[representer-docs-normalization]: https://exercism.org/docs/building/tooling/representers/normalization
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ FROM gradle:8.5-jdk21 AS build

WORKDIR /app
COPY . /app

RUN gradle -i --stacktrace clean build shadowJar
RUN gradle -i --stacktrace clean build

FROM eclipse-temurin:21

ENV LOGGING_LEVEL=INFO

WORKDIR /opt/representer
COPY ./bin/run.sh bin/run.sh
COPY --from=build /app/build/libs/java-representer.jar .
Expand Down
76 changes: 67 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,75 @@
# Exercism java-representer
# Java Representer [![Coverage Status][coveralls-badge]][coveralls-report]

Java representer takes a Java exercise solution and provides a normalized representation of it.
The representer implements the [representer interface](https://exercism.org/docs/building/tooling/representers)
This repository contains the source code for the [Representer][representer-docs] used by the Java track on Exercism.
It takes a Java exercise solution submitted by a student and transforms it into a normalized representation.

## Contributing

## Quickstart
If you want to contribute to the Java Representer, please refer to the [Contributing Guide][contributing-guide].

### Docker
## Usage

1. Create image
### Running directly

`sudo docker build --no-cache -t exercism/java-representer .`
Start by building the JAR using Gradle:

2. Run the container
```sh
./gradlew build
```

`sudo docker run  -v <EXERCISES_FOLDER>:/app/data exercism/java-representer <EXERCISE_SLUG> /app/data/`
Then, run the Java Representer using `build/libs/java-representer.jar`.
For example, to create a representation for a solution to the `leap` exercise, run:

```sh
java -jar build/libs/java-representer.jar leap /path/to/leap /path/to/output/folder
```

The Representer output can be found in the following files inside the `/path/to/output/folder` directory:

- `representation.txt` - Contains the normalized representation of the Java code inside the solution.
- `mapping.json` - Contains a mapping between the generated placeholders used in the representation and the original identifiers.
- `representation.json` - Contains metadata.

### Running with Docker

To run the Java Representer using Docker, first build and tag the Docker image:

```sh
docker build -t exercism/java-representer .
```

Then, run the image and mount the directory of the solution to represent.
For example, to create a representation for a solution to the `leap` exercise, run:

```sh
docker run -v /path/to/leap:/input -v /path/to/output/folder:/output exercism/java-representer leap /input /output
```

The Representer output can be found in the following files inside the `/path/to/output/folder` directory:

- `representation.txt` - Contains the normalized representation of the Java code inside the solution.
- `mapping.json` - Contains a mapping between the generated placeholders used in the representation and the original identifiers.
- `representation.json` - Contains metadata.

## Tests

### Unit tests

To run the unit tests:

```sh
./gradlew test
```

### Smoke tests

To run the smoke tests using Docker:

```sh
bin/run-tests-in-docker.sh
```

[contributing-guide]: https://github.com/exercism/java-representer/blob/main/CONTRIBUTING.md
[representer-docs]: https://exercism.org/docs/building/tooling/representers
[coveralls-badge]: https://coveralls.io/repos/github/exercism/java-representer/badge.svg?branch=main
[coveralls-report]: https://coveralls.io/github/exercism/java-representer?branch=main
4 changes: 0 additions & 4 deletions bin/generate-test-data.sh

This file was deleted.

6 changes: 3 additions & 3 deletions bin/run-in-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
# The test results are formatted according to the specifications at https://github.com/exercism/docs/blob/main/building/tooling/representers/interface.md

# Example:
# ./bin/run-in-docker.sh two-fer /absolute/path/to/two-fer/solution/folder/ /absolute/path/to/output/directory/
# ./bin/run-in-docker.sh two-fer /absolute/path/to/two-fer/solution/folder /absolute/path/to/output/directory

# If any required arguments is missing, print the usage and exit
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
echo "usage: ./bin/run-in-docker.sh exercise-slug /absolute/path/to/solution/folder/ /absolute/path/to/output/directory/"
echo "usage: ./bin/run-in-docker.sh exercise-slug /absolute/path/to/solution/folder /absolute/path/to/output/directory"
exit 1
fi

Expand All @@ -40,4 +40,4 @@ docker run \
--mount type=bind,source="${input_dir}",destination=/solution \
--mount type=bind,source="${output_dir}",destination=/output \
--mount type=tmpfs,destination=/tmp \
exercism/java-representer "${slug}" /solution /output
exercism/java-representer "${slug}" /solution /output
2 changes: 1 addition & 1 deletion bin/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ for test_dir in tests/*; do
test_dir_name=$(basename "${test_dir}")
test_dir_path=$(realpath "${test_dir}")

bin/run.sh "${test_dir_name}" "${test_dir_path}/" "${test_dir_path}/"
bin/run.sh "${test_dir_name}" "${test_dir_path}" "${test_dir_path}"

for file in representation.txt representation.json mapping.json; do
expected_file="expected_${file}"
Expand Down
2 changes: 1 addition & 1 deletion bin/run.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/sh

java -jar /opt/representer/java-representer.jar $1 $2
java -jar /opt/representer/java-representer.jar "$@"
32 changes: 23 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
plugins {
id "com.github.johnrengelman.shadow" version "8.1.1"
id "java"
id "application"
id "jacoco"
id "com.github.johnrengelman.shadow" version "8.1.1"
}

group = "org.exercism"
Expand All @@ -13,14 +14,16 @@ repositories {
}

dependencies {
implementation "org.json:json:20231013"
implementation "com.github.javaparser:javaparser-symbol-solver-core:3.25.7"
implementation "org.apache.logging.log4j:log4j-api:2.22.1"
implementation "org.apache.logging.log4j:log4j-core:2.22.1"

testImplementation "junit:junit:4.13.2"
testImplementation "org.assertj:assertj-core:3.25.0"
testImplementation "org.hamcrest:hamcrest-all:1.3"
implementation "fr.inria.gforge.spoon:spoon-core:10.4.2"
implementation "com.google.code.gson:gson:2.10.1"
implementation "org.slf4j:slf4j-api:2.0.10"
implementation "ch.qos.logback:logback-core:1.4.12"
implementation "ch.qos.logback:logback-classic:1.4.12"

testImplementation platform("org.junit:junit-bom:5.10.0")
testImplementation "org.junit.jupiter:junit-jupiter"
testImplementation "org.assertj:assertj-core:3.25.1"
testImplementation "com.approvaltests:approvaltests:22.3.2"
}

shadowJar {
Expand All @@ -33,9 +36,20 @@ artifacts {
}

test {
useJUnitPlatform()

testLogging {
exceptionFormat = "full"
showStandardStreams = true
events = ["passed", "failed", "skipped"]
}

finalizedBy jacocoTestReport
}

jacocoTestReport {
dependsOn test
reports {
xml.required = true
}
}
11 changes: 0 additions & 11 deletions src/main/java/representer/OptionsValidator.java

This file was deleted.

55 changes: 55 additions & 0 deletions src/main/java/representer/OutputWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package representer;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Map;
import java.util.TreeMap;

class OutputWriter {
private static final int REPRESENTER_VERSION = 2;
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Logger LOGGER = LoggerFactory.getLogger(OutputWriter.class);

private final Path outputPath;

OutputWriter(Path outputPath) {
this.outputPath = outputPath;
}

void write(Representation representation) throws IOException {
writeRepresentation(representation.representation(), this.outputPath.resolve("representation.txt"));
writePlaceholders(representation.placeholders(), this.outputPath.resolve("mapping.json"));
writeMetadata(this.outputPath.resolve("representation.json"));
}

private void writeRepresentation(String representation, Path path) throws IOException {
LOGGER.info("Writing representation to {}", path);
try (var writer = new FileWriter(path.toFile())) {
writer.write(representation);
}
}

private void writePlaceholders(Map<String, String> mapping, Path path) throws IOException {
LOGGER.info("Writing placeholder mapping to {}", path);
var sorted = new TreeMap<>(mapping);
try (var writer = new FileWriter(path.toFile())) {
writer.write(GSON.toJson(sorted));
}
}

private void writeMetadata(Path path) throws IOException {
LOGGER.info("Writing representation metadata to {}", path);
var json = new JsonObject();
json.addProperty("version", REPRESENTER_VERSION);
try (var writer = new FileWriter(path.toFile())) {
writer.write(GSON.toJson(json));
}
}
}
14 changes: 0 additions & 14 deletions src/main/java/representer/ParserConfigurationFactory.java

This file was deleted.

6 changes: 6 additions & 0 deletions src/main/java/representer/PlaceholderGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package representer;

public interface PlaceholderGenerator {
boolean isPlaceholder(String identifier);
String getPlaceholder(String identifier);
}
26 changes: 26 additions & 0 deletions src/main/java/representer/Placeholders.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package representer;

import java.util.HashMap;
import java.util.Map;

public class Placeholders implements PlaceholderGenerator {
private static final String PLACEHOLDER_PREFIX = "PLACEHOLDER";

private final Map<String, String> placeholders = new HashMap<>();

@Override
public boolean isPlaceholder(String identifier) {
return identifier.startsWith(PLACEHOLDER_PREFIX);
}

@Override
public String getPlaceholder(String identifier) {
var placeholder = String.format("%s_%02d", PLACEHOLDER_PREFIX, this.placeholders.size() + 1);
this.placeholders.put(placeholder, identifier);
return placeholder;
}

public Map<String, String> getPlaceholders() {
return Map.copyOf(this.placeholders);
}
}
Loading
Loading