diff --git a/.dockerignore b/.dockerignore index 161eab7..f009512 100644 --- a/.dockerignore +++ b/.dockerignore @@ -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 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..714dfb6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.approved.* binary diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e52beac..eca19e4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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: diff --git a/.gitignore b/.gitignore index 7da88cb..efa8904 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .settings/ .project .classpath +src/test/resources/**/*.received.txt tests/**/*/representation.txt tests/**/*/representation.json tests/**/*/mapping.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dd1d6e9 --- /dev/null +++ b/CONTRIBUTING.md @@ -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 diff --git a/Dockerfile b/Dockerfile index dfff825..043c0fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 . diff --git a/README.md b/README.md index d3742d2..28705b0 100644 --- a/README.md +++ b/README.md @@ -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 :/app/data exercism/java-representer /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 diff --git a/bin/generate-test-data.sh b/bin/generate-test-data.sh deleted file mode 100755 index d5399e3..0000000 --- a/bin/generate-test-data.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -mkdir -p /tmp/exercism/two-fer/src/main/java -cp ../src/test/resources/Twofer.java /tmp/exercism/two-fer/src/main/java diff --git a/bin/run-in-docker.sh b/bin/run-in-docker.sh index 1c9567e..47a786f 100755 --- a/bin/run-in-docker.sh +++ b/bin/run-in-docker.sh @@ -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 @@ -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 diff --git a/bin/run-tests.sh b/bin/run-tests.sh index 3a39b26..946499c 100755 --- a/bin/run-tests.sh +++ b/bin/run-tests.sh @@ -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}" diff --git a/bin/run.sh b/bin/run.sh index 371afa7..49444b8 100755 --- a/bin/run.sh +++ b/bin/run.sh @@ -1,3 +1,3 @@ #!/bin/sh -java -jar /opt/representer/java-representer.jar $1 $2 +java -jar /opt/representer/java-representer.jar "$@" diff --git a/build.gradle b/build.gradle index 6d4b443..f6db3e0 100644 --- a/build.gradle +++ b/build.gradle @@ -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" @@ -13,14 +14,30 @@ repositories { } dependencies { - implementation "org.json:json:20240303" - implementation "com.github.javaparser:javaparser-symbol-solver-core:3.25.7" - implementation "org.apache.logging.log4j:log4j-api:2.23.0" - implementation "org.apache.logging.log4j:log4j-core:2.23.0" + implementation "fr.inria.gforge.spoon:spoon-core:10.4.3-beta-18" + implementation "com.google.code.gson:gson:2.10.1" + implementation "com.google.googlejavaformat:google-java-format:1.19.2" + 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 "junit:junit:4.13.2" + testImplementation platform("org.junit:junit-bom:5.10.1") + testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.3" - testImplementation "org.hamcrest:hamcrest-all:1.3" + testImplementation "com.approvaltests:approvaltests:22.3.2" +} + +jar { + manifest { + // Required for google-java-format + // https://github.com/google/google-java-format/blob/c20a02715cf8be569515857d96fd558eb71cfa72/README.md#as-a-library + attributes "Add-Exports": "jdk.compiler/com.sun.tools.javac.api " + + "jdk.compiler/com.sun.tools.javac.code " + + "jdk.compiler/com.sun.tools.javac.file " + + "jdk.compiler/com.sun.tools.javac.parser " + + "jdk.compiler/com.sun.tools.javac.tree " + + "jdk.compiler/com.sun.tools.javac.util" + } } shadowJar { @@ -33,9 +50,29 @@ artifacts { } test { + useJUnitPlatform() + testLogging { exceptionFormat = "full" showStandardStreams = true events = ["passed", "failed", "skipped"] } + + jvmArgs = [ + "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" + ] + + finalizedBy jacocoTestReport +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + } } diff --git a/src/main/java/representer/OptionsValidator.java b/src/main/java/representer/OptionsValidator.java deleted file mode 100644 index 7959793..0000000 --- a/src/main/java/representer/OptionsValidator.java +++ /dev/null @@ -1,11 +0,0 @@ -package representer; - -class OptionsValidator { - public boolean isValidContext(String contextPath) { - return contextPath.endsWith("/"); - } - - public boolean isValid(String[] args) { - return args.length == 2; - } -} diff --git a/src/main/java/representer/OutputWriter.java b/src/main/java/representer/OutputWriter.java new file mode 100644 index 0000000..f5293e3 --- /dev/null +++ b/src/main/java/representer/OutputWriter.java @@ -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 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)); + } + } +} diff --git a/src/main/java/representer/ParserConfigurationFactory.java b/src/main/java/representer/ParserConfigurationFactory.java deleted file mode 100644 index f3d3e6a..0000000 --- a/src/main/java/representer/ParserConfigurationFactory.java +++ /dev/null @@ -1,14 +0,0 @@ -package representer; - -import com.github.javaparser.ParserConfiguration; -import com.github.javaparser.symbolsolver.JavaSymbolSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; - -class ParserConfigurationFactory { - static ParserConfiguration getParserConfiguration() { - CombinedTypeSolver comb = new CombinedTypeSolver(new ReflectionTypeSolver()); - JavaSymbolSolver solver = new JavaSymbolSolver(comb); - return new ParserConfiguration().setSymbolResolver(solver); - } -} diff --git a/src/main/java/representer/PlaceholderGenerator.java b/src/main/java/representer/PlaceholderGenerator.java new file mode 100644 index 0000000..fc64e86 --- /dev/null +++ b/src/main/java/representer/PlaceholderGenerator.java @@ -0,0 +1,6 @@ +package representer; + +public interface PlaceholderGenerator { + boolean isPlaceholder(String identifier); + String getPlaceholder(String identifier); +} diff --git a/src/main/java/representer/Placeholders.java b/src/main/java/representer/Placeholders.java new file mode 100644 index 0000000..097d30a --- /dev/null +++ b/src/main/java/representer/Placeholders.java @@ -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 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 getPlaceholders() { + return Map.copyOf(this.placeholders); + } +} diff --git a/src/main/java/representer/Representation.java b/src/main/java/representer/Representation.java new file mode 100644 index 0000000..6fe4c49 --- /dev/null +++ b/src/main/java/representer/Representation.java @@ -0,0 +1,9 @@ +package representer; + +import java.util.Map; + +public record Representation(String representation, Map placeholders) { + public Representation { + placeholders = Map.copyOf(placeholders); + } +} diff --git a/src/main/java/representer/Representer.java b/src/main/java/representer/Representer.java index c5497f7..2648744 100644 --- a/src/main/java/representer/Representer.java +++ b/src/main/java/representer/Representer.java @@ -1,58 +1,58 @@ package representer; -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.visitor.ModifierVisitor; -import com.github.javaparser.ast.visitor.VoidVisitor; -import com.github.javaparser.printer.DefaultPrettyPrinterVisitor; -import com.github.javaparser.printer.configuration.DefaultPrinterConfiguration; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import representer.normalizer.PlaceholderNormalizer; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class Representer { - - private static final Logger logger = LogManager.getLogger(Representer.class); - - private List> genericNormalizers; - private List> voidNormalizers; - - public Representer(List> genericNormalizers, - List> voidNormalizer) { - this.voidNormalizers = voidNormalizer != null ? voidNormalizer : Collections.emptyList(); - this.genericNormalizers = - genericNormalizers != null ? genericNormalizers : Collections.emptyList(); - if (logger.isInfoEnabled()) { - List loadedNormalizersNames = Stream - .concat(this.voidNormalizers.stream().map(n -> n.getClass().getSimpleName()), - this.genericNormalizers.stream().map(n -> n.getClass().getSimpleName())) - .collect(Collectors.toList()); - logger.info("Normalizers loaded: {}", loadedNormalizersNames); - } +import com.google.googlejavaformat.java.Formatter; +import com.google.googlejavaformat.java.FormatterException; +import com.google.googlejavaformat.java.JavaFormatterOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import representer.processors.*; +import spoon.Launcher; +import spoon.compiler.Environment; +import spoon.reflect.CtModel; +import spoon.reflect.declaration.CtType; + +class Representer { + private static final Logger LOGGER = LoggerFactory.getLogger(Representer.class); + private static final JavaFormatterOptions FORMATTER_OPTIONS = JavaFormatterOptions + .builder() + .style(JavaFormatterOptions.Style.AOSP) + .build(); + + public static Representation generate(String path) { + var placeholders = new Placeholders(); + + var launcher = new Launcher(); + launcher.getEnvironment().setComplianceLevel(19); + launcher.getEnvironment().setPrettyPrintingMode(Environment.PRETTY_PRINTING_MODE.AUTOIMPORT); + launcher.addInputResource(path); + launcher.addProcessor(new RenameTypes(placeholders)); + launcher.addProcessor(new RenameRecordComponents(placeholders)); + launcher.addProcessor(new RenameMethods(placeholders)); + launcher.addProcessor(new RenameFields(placeholders)); + launcher.addProcessor(new RenameVariables(placeholders)); + launcher.addProcessor(new RemoveComments()); + launcher.buildModel(); + launcher.process(); + + return new Representation(getRepresentationString(launcher.getModel()), placeholders.getPlaceholders()); } - public String generate(CompilationUnit unit) { - voidNormalizers.forEach(n -> unit.accept(n, null)); - genericNormalizers.forEach(n -> unit.accept(n, null)); - DefaultPrettyPrinterVisitor visitor = new DefaultPrettyPrinterVisitor(new DefaultPrinterConfiguration()); - unit.accept(visitor, null); - return visitor.toString(); - } - - public Optional placeholderNormalizer() { - return placeholderNormalizer(voidNormalizers); + private static String getRepresentationString(CtModel model) { + var normalized = new StringBuilder(); + for (CtType type : model.getAllTypes()) { + normalized.append(type.toString()); + normalized.append("\n"); + } + return format(normalized.toString()); } - private Optional placeholderNormalizer( - List> normalizers) { - return normalizers.stream().filter(n -> n.getClass() == PlaceholderNormalizer.class) - .map(n -> (PlaceholderNormalizer) n) - .findFirst(); + private static String format(String representation) { + try { + return new Formatter(FORMATTER_OPTIONS).formatSource(representation); + } catch (FormatterException e) { + LOGGER.warn("Caught exception while attempting to format representation, " + + "using unformatted representation instead", e); + return representation; + } } - } diff --git a/src/main/java/representer/RepresenterCli.java b/src/main/java/representer/RepresenterCli.java index 165f957..66d27c1 100644 --- a/src/main/java/representer/RepresenterCli.java +++ b/src/main/java/representer/RepresenterCli.java @@ -1,96 +1,38 @@ package representer; -import com.github.javaparser.ast.visitor.ModifierVisitor; -import com.github.javaparser.ast.visitor.VoidVisitor; -import com.github.javaparser.utils.SourceRoot; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.json.JSONObject; -import representer.normalizer.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.io.FileWriter; +import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; public class RepresenterCli { + private static final Logger logger = LoggerFactory.getLogger(RepresenterCli.class); - private static final Logger logger = LogManager.getLogger(RepresenterCli.class); + private static Path validateDirectory(String directory) { + var file = new File(directory); - private static final List> modifierNormalizers = - Arrays.asList(new PackageNormalizer(), new BlockNormalizer(), - new CommentNormalizer(), new ImportNormalizer()); - - private static final List> voidNormalizers = - Arrays.asList(new PlaceholderNormalizer()); - - private static final String JAVA_PROJECT_STRUCTURE = "src/main/java"; - - public static void main(String[] args) throws IOException { - OptionsValidator validator = new OptionsValidator(); - if (!validator.isValid(args)) { - throw new IllegalArgumentException( - "expected 2 args: exercise-slug and exercise-context-path"); - } - final String slug = args[0]; - final String contextPath = args[1]; - - logger.info("Parameters slug: {}, contextPath: {}", slug, contextPath); - if (!validator.isValidContext(contextPath)) { - throw new IllegalArgumentException( - "exercise-context-path requires the ending trailing slash"); - } - - var representer = new Representer(modifierNormalizers, voidNormalizers); - var sourceRoot = new SourceRoot(Path.of(contextPath, JAVA_PROJECT_STRUCTURE)) - .setParserConfiguration(ParserConfigurationFactory.getParserConfiguration()); - var representations = new ArrayList(); - - try { - for (var parseResult : sourceRoot.tryToParse()) { - var compilationUnit = parseResult.getResult().get(); - var representation = representer.generate(compilationUnit); - representations.add(representation); - } - } catch (IOException e) { - logger.error("Problems reading the source files", e); + if (!file.exists() || !file.isDirectory()) { + throw new IllegalArgumentException("Not a valid directory: " + directory); } - writeRepresentations(representations, contextPath); - writeMetadata(contextPath); - logger.info("Generated representation"); - - if (representer.placeholderNormalizer().isPresent()) { - var mapping = representer.placeholderNormalizer().get().mapping(); - writeMapping(mapping, contextPath); - logger.info("Generated mapping"); - } else { - logger.warn("PlaceholderNormalizer not loaded, mapping file will not be created"); - } + return file.toPath(); } - private static void writeRepresentations(List representations, String outputDirectory) throws IOException { - writeFile(String.join("\n", representations), outputDirectory + "representation.txt"); - } + public static void main(String[] args) throws IOException { + if (args.length != 3) { + throw new IllegalArgumentException("expected 3 args: exercise-slug; exercise-input-path; output-path"); + } - private static void writeMapping(Map mapping, String outputDirectory) throws IOException { - var json = new JSONObject(); - mapping.forEach(json::put); - writeFile(json.toString(2), outputDirectory + "mapping.json"); - } + var slug = args[0]; + var inputDirectory = validateDirectory(args[1]); + var outputDirectory = validateDirectory(args[2]); - private static void writeMetadata(String outputDirectory) throws IOException { - var json = new JSONObject() - .put("version", 1); - writeFile(json.toString(2), outputDirectory + "representation.json"); - } + logger.info("Parameters slug: {}, input directory: {}, output directory: {}", slug, inputDirectory, outputDirectory); - private static void writeFile(String contents, String filePath) throws IOException { - try (var writer = new FileWriter(filePath)) { - writer.write(contents); - } + var outputWriter = new OutputWriter(outputDirectory); + var representation = Representer.generate(inputDirectory.resolve("src/main/java").toString()); + outputWriter.write(representation); } } diff --git a/src/main/java/representer/SerializatorException.java b/src/main/java/representer/SerializatorException.java deleted file mode 100644 index 5232704..0000000 --- a/src/main/java/representer/SerializatorException.java +++ /dev/null @@ -1,28 +0,0 @@ -package representer; - -public class SerializatorException extends RuntimeException { - private static final long serialVersionUID = -6741466477072057537L; - - public SerializatorException() { - super(); - } - - public SerializatorException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - - public SerializatorException(String message, Throwable cause) { - super(message, cause); - } - - public SerializatorException(String message) { - super(message); - } - - public SerializatorException(Throwable cause) { - super(cause); - } - - -} diff --git a/src/main/java/representer/normalizer/BlockNormalizer.java b/src/main/java/representer/normalizer/BlockNormalizer.java deleted file mode 100644 index 5251e72..0000000 --- a/src/main/java/representer/normalizer/BlockNormalizer.java +++ /dev/null @@ -1,34 +0,0 @@ -package representer.normalizer; - -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.stmt.BlockStmt; -import com.github.javaparser.ast.stmt.IfStmt; -import com.github.javaparser.ast.stmt.Statement; -import com.github.javaparser.ast.visitor.ModifierVisitor; - -public class BlockNormalizer extends ModifierVisitor { - - - @Override - public Node visit(IfStmt n, String arg) { - if (!n.hasThenBlock()) { - n.setThenStmt(wrapWithBlock(n.getThenStmt())); - } - - n.getElseStmt().ifPresent(stmt -> { - if(!stmt.isBlockStmt()) { - n.setElseStmt(wrapWithBlock(stmt)); - } - }); - - return n; - } - - private Statement wrapWithBlock(Statement stmt) { - BlockStmt wrapStmt = new BlockStmt(); - wrapStmt.addAndGetStatement(stmt); - return wrapStmt; - - } - -} diff --git a/src/main/java/representer/normalizer/CommentNormalizer.java b/src/main/java/representer/normalizer/CommentNormalizer.java deleted file mode 100644 index 791c97c..0000000 --- a/src/main/java/representer/normalizer/CommentNormalizer.java +++ /dev/null @@ -1,35 +0,0 @@ -package representer.normalizer; - -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.comments.BlockComment; -import com.github.javaparser.ast.comments.LineComment; -import com.github.javaparser.ast.comments.JavadocComment; - -import com.github.javaparser.ast.visitor.ModifierVisitor; - -public class CommentNormalizer extends ModifierVisitor { - - /** - * Remove nodes of type LineComment - */ - @Override - public Node visit(LineComment n, String arg) { - return null; - } - - /** - * Remove nodes of type BlockComment - */ - @Override - public Node visit(BlockComment n, String arg) { - return null; - } - - /** - * Remove nodes of type JavadocComment - */ - @Override - public Node visit(JavadocComment n, String arg) { - return null; - } -} diff --git a/src/main/java/representer/normalizer/ImportNormalizer.java b/src/main/java/representer/normalizer/ImportNormalizer.java deleted file mode 100644 index 3212295..0000000 --- a/src/main/java/representer/normalizer/ImportNormalizer.java +++ /dev/null @@ -1,17 +0,0 @@ -package representer.normalizer; - -import com.github.javaparser.ast.ImportDeclaration; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.visitor.ModifierVisitor; - -public class ImportNormalizer extends ModifierVisitor { - - /** - * Remove nodes of type ImportDeclaration - */ - - @Override - public Node visit(ImportDeclaration n, String arg) { - return null; - } -} diff --git a/src/main/java/representer/normalizer/Mapper.java b/src/main/java/representer/normalizer/Mapper.java deleted file mode 100644 index 0177dfe..0000000 --- a/src/main/java/representer/normalizer/Mapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package representer.normalizer; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class Mapper { - private Map mapping = new HashMap<>(); - private int counter = 0; - - public String getPlaceholder(String fieldName) { - String placeholder = mapping.get(fieldName); - if (placeholder == null) { - placeholder = generate(); - mapping.put(fieldName, placeholder); - } - return placeholder; - } - - private String generate() { - return "PLACEHOLDER_" + (++counter); - } - - public Map getMapping() { - return Collections.unmodifiableMap(mapping); - } -} diff --git a/src/main/java/representer/normalizer/PackageNormalizer.java b/src/main/java/representer/normalizer/PackageNormalizer.java deleted file mode 100644 index 1421bed..0000000 --- a/src/main/java/representer/normalizer/PackageNormalizer.java +++ /dev/null @@ -1,17 +0,0 @@ -package representer.normalizer; - -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.PackageDeclaration; -import com.github.javaparser.ast.visitor.ModifierVisitor; - -public class PackageNormalizer extends ModifierVisitor { - - /** - * Remove nodes of type PackageDeclaration - */ - @Override - public Node visit(PackageDeclaration n, String arg) { - return null; - } - -} diff --git a/src/main/java/representer/normalizer/PlaceholderNormalizer.java b/src/main/java/representer/normalizer/PlaceholderNormalizer.java deleted file mode 100644 index c38c8be..0000000 --- a/src/main/java/representer/normalizer/PlaceholderNormalizer.java +++ /dev/null @@ -1,217 +0,0 @@ -package representer.normalizer; - -import java.util.Map; -import java.util.Optional; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; -import com.github.javaparser.ast.body.ConstructorDeclaration; -import com.github.javaparser.ast.body.EnumConstantDeclaration; -import com.github.javaparser.ast.body.EnumDeclaration; -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.body.Parameter; -import com.github.javaparser.ast.body.VariableDeclarator; -import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.FieldAccessExpr; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.NameExpr; -import com.github.javaparser.ast.stmt.ReturnStmt; -import com.github.javaparser.ast.type.ClassOrInterfaceType; -import com.github.javaparser.ast.type.Type; -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import com.github.javaparser.resolution.UnsolvedSymbolException; - -import javassist.bytecode.TypeAnnotationsAttribute; - -public class PlaceholderNormalizer extends VoidVisitorAdapter { - private static final Logger logger = LogManager.getLogger(PlaceholderNormalizer.class); - - private Mapper mapper = new Mapper(); - - @Override - public void visit(ClassOrInterfaceDeclaration n, String arg) { - logger.debug("ClassOrInterfaceDeclaration: {}", n.getName().asString()); - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - super.visit(n, arg); - } - - @Override - public void visit(MethodDeclaration n, String arg) { - logger.debug("MethodDeclaration: {}", n.getName().asString()); - logger.debug("method return type: {}", n.getTypeAsString()); - mapType(n.getType()); - n.getParameters().forEach(p -> logger.debug("method parameter: {}", p.getType().asString())); - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - super.visit(n, arg); - } - - private void mapType(Type t) { - if(t.isClassOrInterfaceType()) { - logger.debug("class or interface type"); - ClassOrInterfaceType classOrInterfaceType = t.asClassOrInterfaceType(); - Optional> typeArguments = classOrInterfaceType.getTypeArguments(); - typeArguments.ifPresent(types -> { - String genericsClass = types.get(0).asString(); - if(genericsClass.length() > 1) { // workaround to avoid substitution of T - types.set(0, new ClassOrInterfaceType(null,mapper.getPlaceholder(genericsClass))); - } - }); - } - if(t.isArrayType()) { - logger.debug("array type"); - mapType(t.getElementType()); - } - if(t.isPrimitiveType()) { - logger.debug("primitive type"); - } - - if(t.isTypeParameter()) { - logger.debug("type parameter"); - } - if(t.isVoidType()) { - logger.debug("void type"); - } - } - - @Override - public void visit(Parameter n, String arg) { - logger.debug("Parameter: {}", n.getName().asString()); - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - super.visit(n, arg); - } - - @Override - public void visit(NameExpr n, String arg) { - logger.debug("Name Expr: {}", n); - logger.debug("Name Expr: {}", n.getClass()); - if (isUserDefined(qualifiedName(n))) { - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - } - super.visit(n, arg); - } - - @Override - public void visit(FieldAccessExpr n, String arg) { - logger.debug("FieldAccessExpr: {}", n); - Expression scope = n.getScope(); - if (scope.isNameExpr()) { - if (isUserDefined(qualifiedName(scope.asNameExpr()))) { - logger.debug("user defined"); - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - } else { - logger.debug("Java language"); - } - } - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - super.visit(n, arg); - } - - @Override - public void visit(ReturnStmt n, String arg) { - logger.debug("ReturnStmt: {}", n); - Optional scope = n.getExpression(); - scope.ifPresent(s -> { - if (s.isNameExpr()) { - if (isUserDefined(qualifiedName(s.asNameExpr()))) { - logger.debug("user defined"); - final String name = s.asNameExpr().getName().asString(); - s.asNameExpr().setName(mapper.getPlaceholder(name)); - } else { - logger.debug("Java language"); - } - } - }); - super.visit(n, arg); - } - - @Override - public void visit(MethodCallExpr n, String arg) { - logger.debug("MethodCallExpr: {}", n); - Optional scope = n.getScope(); - scope.ifPresent(s -> { - if (s.isNameExpr()) { - if (isUserDefined(qualifiedName(s.asNameExpr()))) { - logger.debug("user defined"); - final String name = s.asNameExpr().getName().asString(); - s.asNameExpr().setName(mapper.getPlaceholder(name)); - } else { - logger.debug("Java language"); - } - } - }); - - if (!scope.isPresent()) { - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - n.getArguments().forEach(a -> { - if(a.isNameExpr()) { - String nn = a.asNameExpr().getName().asString(); - a.asNameExpr().setName(mapper.getPlaceholder(nn)); - } - }); - } - super.visit(n, arg); - } - - public void visit(VariableDeclarator n, String arg) { - logger.debug("VariableDeclarator: {}", n.getName().asString()); - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - super.visit(n, arg); - } - - @Override - public void visit(ConstructorDeclaration n, String arg) { - logger.debug("ConstructorDeclaration: {}", n.getName().asString()); - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - super.visit(n, arg); - } - - @Override - public void visit(EnumDeclaration n, String arg) { - logger.debug("EnumDeclaration: {}", n.getName().asString()); - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - super.visit(n, arg); - } - - @Override - public void visit(EnumConstantDeclaration n, String arg) { - logger.debug("EnumConstantDeclaration: {}", n.getName().asString()); - final String name = n.getNameAsString(); - n.setName(mapper.getPlaceholder(name)); - super.visit(n, arg); - } - - public Map mapping() { - return mapper.getMapping(); - } - - private boolean isUserDefined(String qualifiedName) { - return qualifiedName == null || !qualifiedName.startsWith("java.") && !qualifiedName.startsWith("javax."); - } - - private String qualifiedName(NameExpr nameExpr) { - try { - logger.debug("qualified name: {}", nameExpr.getNameAsString()); - if (Character.isUpperCase(nameExpr.getNameAsString().charAt(0))) { - return nameExpr.calculateResolvedType().describe(); - } else { - return null; - } - } catch (UnsolvedSymbolException e) { - return null; - } - } - -} diff --git a/src/main/java/representer/processors/RemoveComments.java b/src/main/java/representer/processors/RemoveComments.java new file mode 100644 index 0000000..1d9a505 --- /dev/null +++ b/src/main/java/representer/processors/RemoveComments.java @@ -0,0 +1,16 @@ +package representer.processors; + +import spoon.processing.AbstractProcessor; +import spoon.reflect.code.CtComment; + +/** + * This {@link spoon.processing.Processor} removes all comments from the solution, + * so that solutions that only differ in comments become equivalent. + * Removed comments include line comments, block comments, and Javadoc comments. + */ +public final class RemoveComments extends AbstractProcessor { + @Override + public void process(CtComment ctComment) { + ctComment.delete(); + } +} diff --git a/src/main/java/representer/processors/RenameFields.java b/src/main/java/representer/processors/RenameFields.java new file mode 100644 index 0000000..8e1af83 --- /dev/null +++ b/src/main/java/representer/processors/RenameFields.java @@ -0,0 +1,35 @@ +package representer.processors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import representer.PlaceholderGenerator; +import representer.refactoring.RenameFieldRefactoring; +import spoon.processing.AbstractProcessor; +import spoon.reflect.declaration.CtField; + +/** + * This {@link spoon.processing.Processor} renames all fields and their usages to use placeholder names, + * so that they become name-agnostic. + * Note that enum values also count towards fields and are therefore also renamed by this processor. + */ +public final class RenameFields extends AbstractProcessor> { + private static final Logger LOGGER = LoggerFactory.getLogger(RenameFields.class); + private final PlaceholderGenerator placeholderGenerator; + + public RenameFields(PlaceholderGenerator placeholderGenerator) { + this.placeholderGenerator = placeholderGenerator; + } + + @Override + public void process(CtField ctField) { + var identifier = ctField.getSimpleName(); + + if (placeholderGenerator.isPlaceholder(identifier)) { + return; + } + + var placeholder = this.placeholderGenerator.getPlaceholder(identifier); + LOGGER.info("Renaming field '{}' to '{}'", identifier, placeholder); + new RenameFieldRefactoring().setTarget(ctField).setNewName(placeholder).refactor(); + } +} diff --git a/src/main/java/representer/processors/RenameMethods.java b/src/main/java/representer/processors/RenameMethods.java new file mode 100644 index 0000000..1d0204b --- /dev/null +++ b/src/main/java/representer/processors/RenameMethods.java @@ -0,0 +1,34 @@ +package representer.processors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import representer.PlaceholderGenerator; +import spoon.processing.AbstractProcessor; +import spoon.refactoring.Refactoring; +import spoon.reflect.declaration.CtMethod; + +/** + * This {@link spoon.processing.Processor} renames all methods and their usages to use placeholder names, + * so that any helper methods added by students become name-agnostic. + */ +public final class RenameMethods extends AbstractProcessor> { + private static final Logger LOGGER = LoggerFactory.getLogger(RenameMethods.class); + private final PlaceholderGenerator placeholderGenerator; + + public RenameMethods(PlaceholderGenerator placeholderGenerator) { + this.placeholderGenerator = placeholderGenerator; + } + + @Override + public void process(CtMethod ctMethod) { + var identifier = ctMethod.getSimpleName(); + + if (placeholderGenerator.isPlaceholder(identifier)) { + return; + } + + var placeholder = this.placeholderGenerator.getPlaceholder(identifier); + LOGGER.info("Renaming method '{}' to '{}'", identifier, placeholder); + Refactoring.changeMethodName(ctMethod, placeholder); + } +} diff --git a/src/main/java/representer/processors/RenameRecordComponents.java b/src/main/java/representer/processors/RenameRecordComponents.java new file mode 100644 index 0000000..98210d5 --- /dev/null +++ b/src/main/java/representer/processors/RenameRecordComponents.java @@ -0,0 +1,64 @@ +package representer.processors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import representer.PlaceholderGenerator; +import representer.refactoring.RenameFieldRefactoring; +import spoon.processing.AbstractProcessor; +import spoon.refactoring.CtRenameGenericVariableRefactoring; +import spoon.refactoring.Refactoring; +import spoon.reflect.declaration.CtParameter; +import spoon.reflect.declaration.CtRecord; +import spoon.reflect.declaration.CtRecordComponent; + +import java.util.stream.Collectors; + +/** + * This {@link spoon.processing.Processor} renames all record components and their usages to use placeholder names, + * so that they become name-agnostic. + * Note: because record components generate a getter method, class field and constructor parameter in the background, + * these are all renamed as well to remain consistent. + */ +public final class RenameRecordComponents extends AbstractProcessor { + private static final Logger LOGGER = LoggerFactory.getLogger(RenameRecordComponents.class); + + private final PlaceholderGenerator placeholderGenerator; + + public RenameRecordComponents(PlaceholderGenerator placeholderGenerator) { + this.placeholderGenerator = placeholderGenerator; + } + + @Override + public void process(CtRecordComponent ctRecordComponent) { + var identifier = ctRecordComponent.getSimpleName(); + + if (placeholderGenerator.isPlaceholder(identifier)) { + return; + } + + var placeholder = placeholderGenerator.getPlaceholder(identifier); + LOGGER.info("Renaming record component '{}' to '{}'", identifier, placeholder); + ctRecordComponent.setSimpleName(placeholder); + + var parent = ctRecordComponent.getParent(CtRecord.class); + + var getter = parent.getMethod(ctRecordComponent.getType(), identifier); + LOGGER.info("Renaming record component getter '{}' to '{}'", getter.getSimpleName(), placeholder); + Refactoring.changeMethodName(getter, placeholder); + + var field = parent.getField(identifier); + LOGGER.info("Renaming record component field '{}' to '{}'", field.getSimpleName(), placeholder); + new RenameFieldRefactoring().setTarget(field).setNewName(placeholder).refactor(); + + var constructorParameters = parent.getConstructors() + .stream() + .flatMap(ctConstructor -> ctConstructor.getParameters().stream()) + .filter(ctParameter -> ctParameter.getSimpleName().equals(identifier)) + .collect(Collectors.toSet()); + + for (CtParameter ctParameter : constructorParameters) { + LOGGER.info("Renaming record constructor parameter '{}' to '{}'", ctParameter.getSimpleName(), placeholder); + new CtRenameGenericVariableRefactoring().setTarget(ctParameter).setNewName(placeholder).refactor(); + } + } +} diff --git a/src/main/java/representer/processors/RenameTypes.java b/src/main/java/representer/processors/RenameTypes.java new file mode 100644 index 0000000..80b04ca --- /dev/null +++ b/src/main/java/representer/processors/RenameTypes.java @@ -0,0 +1,34 @@ +package representer.processors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import representer.PlaceholderGenerator; +import spoon.processing.AbstractProcessor; +import spoon.refactoring.Refactoring; +import spoon.reflect.declaration.CtType; + +/** + * This {@link spoon.processing.Processor} renames all types and their usages to use placeholder names, + * so that any helper classes or enums in the solution become name-agnostic. + */ +public final class RenameTypes extends AbstractProcessor> { + private static final Logger LOGGER = LoggerFactory.getLogger(RenameTypes.class); + private final PlaceholderGenerator placeholderGenerator; + + public RenameTypes(PlaceholderGenerator placeholderGenerator) { + this.placeholderGenerator = placeholderGenerator; + } + + @Override + public void process(CtType ctType) { + var identifier = ctType.getSimpleName(); + + if (placeholderGenerator.isPlaceholder(identifier)) { + return; + } + + var placeholder = this.placeholderGenerator.getPlaceholder(identifier); + LOGGER.info("Renaming type '{}' to '{}'", identifier, placeholder); + Refactoring.changeTypeName(ctType, placeholder); + } +} diff --git a/src/main/java/representer/processors/RenameVariables.java b/src/main/java/representer/processors/RenameVariables.java new file mode 100644 index 0000000..440c61c --- /dev/null +++ b/src/main/java/representer/processors/RenameVariables.java @@ -0,0 +1,45 @@ +package representer.processors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import representer.PlaceholderGenerator; +import spoon.processing.AbstractProcessor; +import spoon.refactoring.CtRenameGenericVariableRefactoring; +import spoon.reflect.code.CtCatchVariable; +import spoon.reflect.code.CtLocalVariable; +import spoon.reflect.declaration.CtParameter; +import spoon.reflect.declaration.CtVariable; + +/** + * This {@link spoon.processing.Processor} renames all variables to use placeholder names, + * so that they become name-agnostic. + * Renamed variables include local variables, scoped variables like in a catch-clause, and method parameters. + */ +public final class RenameVariables extends AbstractProcessor> { + private static final Logger LOGGER = LoggerFactory.getLogger(RenameVariables.class); + private final PlaceholderGenerator placeholderGenerator; + + public RenameVariables(PlaceholderGenerator placeholderGenerator) { + this.placeholderGenerator = placeholderGenerator; + } + + @Override + public boolean isToBeProcessed(CtVariable candidate) { + return candidate instanceof CtLocalVariable || + candidate instanceof CtCatchVariable || + candidate instanceof CtParameter; + } + + @Override + public void process(CtVariable ctVariable) { + var identifier = ctVariable.getSimpleName(); + + if (placeholderGenerator.isPlaceholder(identifier)) { + return; + } + + var placeholder = this.placeholderGenerator.getPlaceholder(identifier); + LOGGER.info("Renaming variable '{}' to '{}'", identifier, placeholder); + new CtRenameGenericVariableRefactoring().setTarget(ctVariable).setNewName(placeholder).refactor(); + } +} \ No newline at end of file diff --git a/src/main/java/representer/refactoring/RenameFieldRefactoring.java b/src/main/java/representer/refactoring/RenameFieldRefactoring.java new file mode 100644 index 0000000..be1fdad --- /dev/null +++ b/src/main/java/representer/refactoring/RenameFieldRefactoring.java @@ -0,0 +1,26 @@ +package representer.refactoring; + +import spoon.refactoring.AbstractRenameRefactoring; +import spoon.reflect.declaration.CtField; +import spoon.reflect.reference.CtReference; +import spoon.reflect.visitor.chain.CtConsumer; +import spoon.reflect.visitor.filter.FieldReferenceFunction; + +/** + * This {@link spoon.refactoring.CtRenameRefactoring} renames a field and all its usages. + * Works on both class fields and enum values. + */ +public final class RenameFieldRefactoring extends AbstractRenameRefactoring> { + public RenameFieldRefactoring() { + super(javaIdentifierRE); + } + + @Override + protected void refactorNoCheck() { + getTarget() + .map(new FieldReferenceFunction()) + .forEach((CtConsumer) ctReference -> ctReference.setSimpleName(newName)); + + target.setSimpleName(newName); + } +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml deleted file mode 100644 index 1274bff..0000000 --- a/src/main/resources/log4j2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..abd48f5 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,17 @@ + + + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -- %msg%n + + + + + + + diff --git a/src/test/java/representer/ExercisesTest.java b/src/test/java/representer/ExercisesTest.java deleted file mode 100644 index 304eff8..0000000 --- a/src/test/java/representer/ExercisesTest.java +++ /dev/null @@ -1,116 +0,0 @@ -package representer; - -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.visitor.ModifierVisitor; -import com.github.javaparser.ast.visitor.VoidVisitor; -import org.assertj.core.api.Assertions; -import org.junit.Before; -import org.junit.Test; -import representer.normalizer.*; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -public class ExercisesTest { - - private Representer representer; - private final TestUtils testUtils = new TestUtils(); - - @Before - public void init() { - List> genericNormalizers = Arrays.asList(new PackageNormalizer(), new ImportNormalizer(), new CommentNormalizer(), new BlockNormalizer()); - List> voidNormalizers = Arrays.asList(new PlaceholderNormalizer()); - representer = new Representer(genericNormalizers, voidNormalizers); - } - - @Test - public void accumulateRepresentation() throws Exception { - String testFolder = "exercises/accumulate"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } - - @Test - public void acronymRepresentation() throws Exception { - String testFolder = "exercises/acronym"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } - - @Test - public void affineCipherRepresentation() throws Exception { - String testFolder = "exercises/affine-cipher"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } - - @Test - public void allergiesRepresentation() throws Exception { - String testFolder = "exercises/allergies"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } - - @Test - public void allYourBaseRepresentation() throws Exception { - String testFolder = "exercises/all-your-base"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } - - @Test - public void alphameticsRepresentation() throws Exception { - String testFolder = "exercises/alphametics"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } - - @Test - public void helloWorldRepresentation() throws Exception { - String testFolder = "exercises/hello-world"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } - - @Test - public void pigLatinRepresentation() throws Exception { - String testFolder = "exercises/pig-latin"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } - - @Test - public void raindropsRepresentation() throws Exception { - String testFolder = "exercises/raindrops"; - List sources = testUtils.getParsedResourceContentFromFolder(testFolder); - String representation = sources.stream().map(s -> representer.generate(s)).collect(Collectors.joining()); - String expectedRepresentation = testUtils.getResourceContent(testFolder + "/representation"); - System.out.println(representation); - Assertions.assertThat(representation).isEqualTo(expectedRepresentation); - } -} diff --git a/src/test/java/representer/OutputWriterTest.java b/src/test/java/representer/OutputWriterTest.java new file mode 100644 index 0000000..b08bf6a --- /dev/null +++ b/src/test/java/representer/OutputWriterTest.java @@ -0,0 +1,67 @@ +package representer; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class OutputWriterTest { + private static final String REPRESENTATION = """ + class PLACEHOLDER_1 { + String PLACEHOLDER_3(String PLACEHOLDER_2) { + return "One for " + PLACEHOLDER_2 == null ? "you" : PLACEHOLDER_2 + ", one for me."; + } + } + """; + + private static final Map PLACEHOLDERS = Map.of( + "PLACEHOLDER_1", "Twofer", + "PLACEHOLDER_3", "twofer", + "PLACEHOLDER_2", "name" + ); + + @TempDir + static Path outputPath; + + @BeforeAll + static void setup() throws IOException { + var writer = new OutputWriter(outputPath); + writer.write(new Representation(REPRESENTATION, PLACEHOLDERS)); + } + + @Test + @DisplayName("Writes the representation to representation.txt") + void writesRepresentationTxt() throws IOException { + var actual = Files.readString(outputPath.resolve("representation.txt")); + assertThat(actual).isEqualTo(REPRESENTATION); + } + + @Test + @DisplayName("Writes the placeholder mapping to mapping.json") + void writesMappingJson() throws IOException { + var actual = Files.readString(outputPath.resolve("mapping.json")); + assertThat(actual).isEqualTo(""" + { + "PLACEHOLDER_1": "Twofer", + "PLACEHOLDER_2": "name", + "PLACEHOLDER_3": "twofer" + }"""); + } + + @Test + @DisplayName("Writes the representation metadata to representation.json") + void writesRepresentationJson() throws IOException { + var actual = Files.readString(outputPath.resolve("representation.json")); + assertThat(actual).isEqualTo(""" + { + "version": 2 + }"""); + } +} diff --git a/src/test/java/representer/PackageSettings.java b/src/test/java/representer/PackageSettings.java new file mode 100644 index 0000000..842b98d --- /dev/null +++ b/src/test/java/representer/PackageSettings.java @@ -0,0 +1,9 @@ +package representer; + +/** + * Settings for {@link org.approvaltests.Approvals}. + */ +@SuppressWarnings("unused") +public class PackageSettings { + public static String ApprovalBaseDirectory = "../resources"; +} diff --git a/src/test/java/representer/RepresenterTest.java b/src/test/java/representer/RepresenterTest.java index 186a19b..ab5da8b 100644 --- a/src/test/java/representer/RepresenterTest.java +++ b/src/test/java/representer/RepresenterTest.java @@ -1,71 +1,33 @@ package representer; -import com.github.javaparser.ast.CompilationUnit; -import org.junit.Test; -import representer.normalizer.*; - -import java.util.Arrays; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -public class RepresenterTest { - private final TestUtils testUtils = new TestUtils(); - - @Test - public void simple_scenario() throws Exception { - CompilationUnit sourceCode = testUtils.getParsedResourceContent("representer/simple_scenario_input"); - Representer representer = new Representer(null, Arrays.asList(new PlaceholderNormalizer())); - final String codeNormalized = representer.generate(sourceCode); - final String expected = testUtils.getResourceContent("representer/result_expected"); - assertThat(codeNormalized, is(expected)); - } - - @Test - public void package_scenario() throws Exception { - CompilationUnit sourceCode = testUtils.getParsedResourceContent("representer/package_scenario_input"); - Representer representer = new Representer(Arrays.asList(new PackageNormalizer()), - Arrays.asList(new PlaceholderNormalizer())); - final String codeNormalized = representer.generate(sourceCode); - final String expected = testUtils.getResourceContent("representer/result_expected"); - assertThat(codeNormalized, is(expected)); +import org.approvaltests.Approvals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class RepresenterTest { + @ParameterizedTest + @MethodSource("scenarios") + void testRepresentation(String scenario) { + var actual = Representer.generate(path(scenario)); + Approvals.verify(actual.representation(), Approvals.NAMES.withParameters(scenario)); } - @Test - public void comments_scenario() throws Exception { - CompilationUnit sourceCode = testUtils.getParsedResourceContent("representer/comments_scenario_input"); - Representer representer = - new Representer(Arrays.asList(new PackageNormalizer(), new CommentNormalizer()), - Arrays.asList(new PlaceholderNormalizer())); - final String codeNormalized = representer.generate(sourceCode); - final String expected = testUtils.getResourceContent("representer/result_expected"); - assertThat(codeNormalized, is(expected)); + private static Stream scenarios() { + return Stream.of( + "simple", + "class-and-enum", + "class-with-nested-enum", + "lambda-arguments", + "generic-type-arguments", + "if-statements-without-block-bodies", + "record-class", + "switch-expression" + ); } - - @Test - public void import_scenario() throws Exception { - CompilationUnit sourceCode = testUtils.getParsedResourceContent("representer/import_scenario_input"); - Representer representer = - new Representer( - Arrays.asList(new PackageNormalizer(), new CommentNormalizer(), - new ImportNormalizer()), - Arrays.asList(new PlaceholderNormalizer())); - final String codeNormalized = representer.generate(sourceCode); - final String expected = testUtils.getResourceContent("representer/result_expected"); - assertThat(codeNormalized, is(expected)); + private String path(String scenario) { + return getClass().getResource("/scenarios/" + scenario).getPath(); } - - @Test - public void block_scenario() throws Exception { - CompilationUnit sourceCode = testUtils.getParsedResourceContent("representer/block_scenario_input"); - Representer representer = - new Representer(Arrays.asList(new PackageNormalizer(), new BlockNormalizer()), - Arrays.asList(new PlaceholderNormalizer())); - final String codeNormalized = representer.generate(sourceCode); - final String expected = testUtils.getResourceContent("representer/result_expected"); - assertThat(codeNormalized, is(expected)); - } - - } diff --git a/src/test/java/representer/TestUtils.java b/src/test/java/representer/TestUtils.java deleted file mode 100644 index ec81744..0000000 --- a/src/test/java/representer/TestUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -package representer; - -import com.github.javaparser.JavaParser; -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.utils.SourceRoot; - -import java.net.URI; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; - -public class TestUtils { - public String getResourceContent(String name) throws Exception { - URI resource = TestUtils.class.getClassLoader().getResource(name).toURI(); - return new String(Files.readAllBytes(Paths.get(resource))); - } - - public CompilationUnit getParsedResourceContent(String name) throws Exception { - var parser = new JavaParser(ParserConfigurationFactory.getParserConfiguration()); - return parser.parse(getResourceContent(name)).getResult().get(); - } - - public List getParsedResourceContentFromFolder(String folder) throws Exception { - URL resourceURL = TestUtils.class.getClassLoader().getResource(folder); - URI resource = resourceURL.toURI(); - SourceRoot sourceRoot = new SourceRoot(Path.of(resource)) - .setParserConfiguration(ParserConfigurationFactory.getParserConfiguration()); - - List sources = new ArrayList<>(); - for (var result : sourceRoot.tryToParse()) { - sources.add(result.getResult().get()); - } - - return sources; - } -} diff --git a/src/test/resources/Twofer.java b/src/test/resources/Twofer.java deleted file mode 100644 index 6e96b61..0000000 --- a/src/test/resources/Twofer.java +++ /dev/null @@ -1,5 +0,0 @@ -class Twofer { - String twofer(String name) { - return "One for " + (name != null ? name : "you") + ", one for me."; - } -} \ No newline at end of file diff --git a/src/test/resources/exercises/accumulate/representation b/src/test/resources/exercises/accumulate/representation deleted file mode 100644 index 5588865..0000000 --- a/src/test/resources/exercises/accumulate/representation +++ /dev/null @@ -1,10 +0,0 @@ -class PLACEHOLDER_1 { - - static List PLACEHOLDER_2(List PLACEHOLDER_7, Function PLACEHOLDER_4) { - List PLACEHOLDER_3 = new ArrayList<>(); - for (T PLACEHOLDER_5 : PLACEHOLDER_7) { - PLACEHOLDER_3.add(PLACEHOLDER_6.apply(PLACEHOLDER_5)); - } - return PLACEHOLDER_3; - } -} diff --git a/src/test/resources/exercises/acronym/Acronym.java b/src/test/resources/exercises/acronym/Acronym.java deleted file mode 100644 index 5569ecf..0000000 --- a/src/test/resources/exercises/acronym/Acronym.java +++ /dev/null @@ -1,26 +0,0 @@ -import java.util.Arrays; -import java.util.stream.Collectors; - -class Acronym { - - private String acronym; - - Acronym(String phrase) { - this.acronym = generateAcronym(phrase); - } - - - String get() { - return acronym; - } - - private String generateAcronym(String phrase) { - String phraseWithoutUnderscores = phrase.replace("_", ""); - return Arrays.stream(phraseWithoutUnderscores.split("(?!')\\W")) - .filter(word -> !word.isEmpty()) // Remove empty strings from the result of phrase.split - .map(word -> word.substring(0, 1)) // Get the first character of each word - .collect(Collectors.joining()) // Concatenate the characters - .toUpperCase(); - } - -} diff --git a/src/test/resources/exercises/acronym/representation b/src/test/resources/exercises/acronym/representation deleted file mode 100644 index 2274743..0000000 --- a/src/test/resources/exercises/acronym/representation +++ /dev/null @@ -1,17 +0,0 @@ -class PLACEHOLDER_1 { - - private String PLACEHOLDER_2; - - PLACEHOLDER_1(String PLACEHOLDER_4) { - this.PLACEHOLDER_2 = PLACEHOLDER_3(PLACEHOLDER_5); - } - - String PLACEHOLDER_6() { - return PLACEHOLDER_2; - } - - private String PLACEHOLDER_3(String PLACEHOLDER_4) { - String PLACEHOLDER_7 = PLACEHOLDER_5.replace("_", ""); - return Arrays.stream(PLACEHOLDER_7.split("(?!')\\W")).filter(PLACEHOLDER_8 -> !PLACEHOLDER_9.isEmpty()).map(PLACEHOLDER_8 -> PLACEHOLDER_9.substring(0, 1)).collect(Collectors.joining()).toUpperCase(); - } -} diff --git a/src/test/resources/exercises/affine-cipher/representation b/src/test/resources/exercises/affine-cipher/representation deleted file mode 100644 index fbc699f..0000000 --- a/src/test/resources/exercises/affine-cipher/representation +++ /dev/null @@ -1,60 +0,0 @@ -class PLACEHOLDER_1 { - - private static final int PLACEHOLDER_2 = 5; - - private static final int PLACEHOLDER_3 = 26; - - private static final int PLACEHOLDER_4 = 97; - - private enum PLACEHOLDER_5 { - - PLACEHOLDER_6, PLACEHOLDER_7 - } - - String PLACEHOLDER_8(String PLACEHOLDER_11, int PLACEHOLDER_12, int PLACEHOLDER_13) { - return PLACEHOLDER_9(PLACEHOLDER_10(PLACEHOLDER_14, PLACEHOLDER_15, PLACEHOLDER_16, PLACEHOLDER_5.PLACEHOLDER_17)); - } - - String PLACEHOLDER_18(String PLACEHOLDER_19, int PLACEHOLDER_12, int PLACEHOLDER_13) { - return PLACEHOLDER_10(PLACEHOLDER_20, PLACEHOLDER_15, PLACEHOLDER_16, PLACEHOLDER_5.PLACEHOLDER_21); - } - - private static int PLACEHOLDER_22(int PLACEHOLDER_12) { - int PLACEHOLDER_23 = Math.floorMod(PLACEHOLDER_12, PLACEHOLDER_3); - for (int PLACEHOLDER_24 = 1; PLACEHOLDER_24 < PLACEHOLDER_3; PLACEHOLDER_24++) { - if (Math.floorMod(PLACEHOLDER_23 * PLACEHOLDER_24, PLACEHOLDER_3) == 1) { - return PLACEHOLDER_25; - } - } - return 1; - } - - private static String PLACEHOLDER_9(String PLACEHOLDER_27) { - List PLACEHOLDER_26 = new ArrayList<>(); - for (int PLACEHOLDER_28 = 0; PLACEHOLDER_28 < PLACEHOLDER_29.length(); PLACEHOLDER_28 += PLACEHOLDER_2) { - PLACEHOLDER_26.add(PLACEHOLDER_29.substring(PLACEHOLDER_28, Math.min(PLACEHOLDER_29.length(), PLACEHOLDER_28 + PLACEHOLDER_2))); - } - return String.join(" ", PLACEHOLDER_26); - } - - private static String PLACEHOLDER_10(String PLACEHOLDER_27, int PLACEHOLDER_12, int PLACEHOLDER_13, Mode PLACEHOLDER_34) { - int PLACEHOLDER_30 = PLACEHOLDER_22(PLACEHOLDER_15); - if (PLACEHOLDER_30 == 1) { - throw new IllegalArgumentException("Error: keyA and alphabet size must be coprime."); - } - StringBuilder PLACEHOLDER_31 = new StringBuilder(); - PLACEHOLDER_29.chars().filter(Character::isLetterOrDigit).map(Character::toLowerCase).forEach(PLACEHOLDER_33 -> { - int PLACEHOLDER_32 = PLACEHOLDER_33 - PLACEHOLDER_4; - if (PLACEHOLDER_32 < 0) { - PLACEHOLDER_31.appendCodePoint(PLACEHOLDER_33); - } else { - if (PLACEHOLDER_34 == PLACEHOLDER_5.PLACEHOLDER_17) { - PLACEHOLDER_31.appendCodePoint(PLACEHOLDER_4 + Math.floorMod(PLACEHOLDER_12 * PLACEHOLDER_32 + PLACEHOLDER_13, PLACEHOLDER_3)); - } else if (PLACEHOLDER_34 == PLACEHOLDER_5.PLACEHOLDER_21) { - PLACEHOLDER_31.appendCodePoint(PLACEHOLDER_4 + Math.floorMod(PLACEHOLDER_30 * (PLACEHOLDER_32 - PLACEHOLDER_13), PLACEHOLDER_3)); - } - } - }); - return PLACEHOLDER_31.toString(); - } -} diff --git a/src/test/resources/exercises/all-your-base/BaseConverter.java b/src/test/resources/exercises/all-your-base/BaseConverter.java deleted file mode 100644 index 0cda335..0000000 --- a/src/test/resources/exercises/all-your-base/BaseConverter.java +++ /dev/null @@ -1,76 +0,0 @@ -import java.util.Arrays; - -final class BaseConverter { - - private static final int MINIMUM_VALID_BASE = 2; - - private static final String INVALID_BASE_ERROR_MESSAGE = "Bases must be at least 2."; - - private final int numeral; - - BaseConverter(final int originalBase, final int[] originalDigits) { - validateInputs(originalBase, originalDigits); - this.numeral = computeNumeral(originalBase, originalDigits); - } - - int[] convertToBase(final int newBase) { - if (newBase < MINIMUM_VALID_BASE) { - throw new IllegalArgumentException(INVALID_BASE_ERROR_MESSAGE); - } - - final int largestExponent = computeLargestExponentForBase(newBase); - final int[] result = new int[largestExponent + 1]; - int remainder = numeral; - - for (int currentExponent = largestExponent; currentExponent >= 0; currentExponent--) { - final int coefficient = (int) Math.floor(remainder / Math.pow(newBase, currentExponent)); - - result[largestExponent - currentExponent] = coefficient; - - remainder -= coefficient * Math.pow(newBase, currentExponent); - } - - return result; - } - - private void validateInputs(final int originalBase, final int[] originalDigits) { - if (originalBase < MINIMUM_VALID_BASE) { - throw new IllegalArgumentException(INVALID_BASE_ERROR_MESSAGE); - } - - if (originalDigits.length == 0) { - return; - } - - if (Arrays.stream(originalDigits).min().getAsInt() < 0) { - throw new IllegalArgumentException("Digits may not be negative."); - } - - if (Arrays.stream(originalDigits).max().getAsInt() >= originalBase) { - throw new IllegalArgumentException("All digits must be strictly less than the base."); - } - } - - private int computeNumeral(final int originalBase, final int[] originalDigits) { - int result = 0; - - final int largestExponent = originalDigits.length - 1; - - for (int currentExponent = largestExponent; currentExponent >= 0; currentExponent--) { - result += originalDigits[largestExponent - currentExponent] * Math.pow(originalBase, currentExponent); - } - - return result; - } - - private int computeLargestExponentForBase(final int newBase) { - int result = 0; - - while (Math.pow(newBase, result + 1) < numeral) { - result += 1; - } - - return result; - } - -} diff --git a/src/test/resources/exercises/all-your-base/representation b/src/test/resources/exercises/all-your-base/representation deleted file mode 100644 index 69bba26..0000000 --- a/src/test/resources/exercises/all-your-base/representation +++ /dev/null @@ -1,60 +0,0 @@ -final class PLACEHOLDER_1 { - - private static final int PLACEHOLDER_2 = 2; - - private static final String PLACEHOLDER_3 = "Bases must be at least 2."; - - private final int PLACEHOLDER_4; - - PLACEHOLDER_1(final int PLACEHOLDER_6, final int[] PLACEHOLDER_7) { - PLACEHOLDER_5(PLACEHOLDER_8, PLACEHOLDER_9); - this.PLACEHOLDER_4 = PLACEHOLDER_10(PLACEHOLDER_8, PLACEHOLDER_9); - } - - int[] PLACEHOLDER_11(final int PLACEHOLDER_12) { - if (PLACEHOLDER_12 < PLACEHOLDER_2) { - throw new IllegalArgumentException(PLACEHOLDER_3); - } - final int PLACEHOLDER_13 = PLACEHOLDER_14(PLACEHOLDER_15); - final int[] PLACEHOLDER_16 = new int[PLACEHOLDER_13 + 1]; - int PLACEHOLDER_17 = PLACEHOLDER_4; - for (int PLACEHOLDER_19 = PLACEHOLDER_13; PLACEHOLDER_19 >= 0; PLACEHOLDER_19--) { - final int PLACEHOLDER_18 = (int) Math.floor(PLACEHOLDER_17 / Math.pow(PLACEHOLDER_12, PLACEHOLDER_19)); - PLACEHOLDER_16[PLACEHOLDER_13 - PLACEHOLDER_19] = PLACEHOLDER_18; - PLACEHOLDER_17 -= PLACEHOLDER_18 * Math.pow(PLACEHOLDER_12, PLACEHOLDER_19); - } - return PLACEHOLDER_20; - } - - private void PLACEHOLDER_5(final int PLACEHOLDER_6, final int[] PLACEHOLDER_7) { - if (PLACEHOLDER_6 < PLACEHOLDER_2) { - throw new IllegalArgumentException(PLACEHOLDER_3); - } - if (PLACEHOLDER_7.PLACEHOLDER_22 == 0) { - return; - } - if (Arrays.stream(PLACEHOLDER_7).min().getAsInt() < 0) { - throw new IllegalArgumentException("Digits may not be negative."); - } - if (Arrays.stream(PLACEHOLDER_7).max().getAsInt() >= PLACEHOLDER_6) { - throw new IllegalArgumentException("All digits must be strictly less than the base."); - } - } - - private int PLACEHOLDER_10(final int PLACEHOLDER_6, final int[] PLACEHOLDER_7) { - int PLACEHOLDER_16 = 0; - final int PLACEHOLDER_13 = PLACEHOLDER_7.PLACEHOLDER_22 - 1; - for (int PLACEHOLDER_19 = PLACEHOLDER_13; PLACEHOLDER_19 >= 0; PLACEHOLDER_19--) { - PLACEHOLDER_16 += PLACEHOLDER_7[PLACEHOLDER_13 - PLACEHOLDER_19] * Math.pow(PLACEHOLDER_6, PLACEHOLDER_19); - } - return PLACEHOLDER_20; - } - - private int PLACEHOLDER_14(final int PLACEHOLDER_12) { - int PLACEHOLDER_16 = 0; - while (Math.pow(PLACEHOLDER_12, PLACEHOLDER_16 + 1) < PLACEHOLDER_4) { - PLACEHOLDER_16 += 1; - } - return PLACEHOLDER_20; - } -} diff --git a/src/test/resources/exercises/allergies/representation b/src/test/resources/exercises/allergies/representation deleted file mode 100644 index 4944d4a..0000000 --- a/src/test/resources/exercises/allergies/representation +++ /dev/null @@ -1,37 +0,0 @@ -class PLACEHOLDER_1 { - - private int PLACEHOLDER_2; - - PLACEHOLDER_1(int PLACEHOLDER_2) { - this.PLACEHOLDER_2 = PLACEHOLDER_2; - } - - List PLACEHOLDER_4() { - return EnumSet.allOf(Allergen.class).stream().filter(this::isAllergicTo).collect(Collectors.toList()); - } - - boolean PLACEHOLDER_5(Allergen PLACEHOLDER_6) { - return (PLACEHOLDER_2 & PLACEHOLDER_7.getScore()) != 0; - } -} -enum PLACEHOLDER_3 { - - PLACEHOLDER_8(1), - PLACEHOLDER_9(2), - PLACEHOLDER_10(4), - PLACEHOLDER_11(8), - PLACEHOLDER_12(16), - PLACEHOLDER_13(32), - PLACEHOLDER_14(64), - PLACEHOLDER_15(128); - - private final int PLACEHOLDER_2; - - PLACEHOLDER_3(int PLACEHOLDER_2) { - this.PLACEHOLDER_2 = PLACEHOLDER_2; - } - - int PLACEHOLDER_16() { - return PLACEHOLDER_17; - } -} diff --git a/src/test/resources/exercises/alphametics/Alphametics.java b/src/test/resources/exercises/alphametics/Alphametics.java deleted file mode 100644 index 014a1a4..0000000 --- a/src/test/resources/exercises/alphametics/Alphametics.java +++ /dev/null @@ -1,125 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -public class Alphametics { - private final List wordsToSum; - private final String wordResult; - - Alphametics(String userInput) { - String[] questionAndAnswer = userInput.split("=="); - wordsToSum = Arrays.stream(questionAndAnswer[0].split("\\+")) - .map(String::new) - .map(String::trim) - .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); - wordResult = questionAndAnswer[1].trim(); - } - - Map solve() throws UnsolvablePuzzleException { - AlphameticsRecursion solver = new AlphameticsRecursion(getDistinctCharacters()); - solver.generate(); - return solver.get().orElseThrow(UnsolvablePuzzleException::new); - } - - /** - * Returns the list of distinct characters in this alphametic puzzle. - */ - private List getDistinctCharacters() { - Set distinctCharacters = new LinkedHashSet<>(); - wordsToSum.forEach(word -> distinctCharacters.addAll(toChar(word))); - distinctCharacters.addAll(toChar(wordResult)); - - return new ArrayList<>(distinctCharacters); - } - - private List toChar(String toChar) { - return toChar.chars() - .mapToObj(c -> (char) c) - .collect(Collectors.toList()); - } - - private class AlphameticsRecursion { - private final List characters; - private LinkedHashMap validPermutation; - - private AlphameticsRecursion(List characters) { - this.characters = characters; - } - - private void generate() { - generate(new LinkedHashMap<>(), 0, new boolean[10]); - } - - private void generate(LinkedHashMap permutation, int index, boolean[] isDigitsUsed) { - // base case - if (index == characters.size()) { - if (!isLeadingDigitZero(permutation) && isSumTally(permutation)) { - validPermutation = new LinkedHashMap<>(permutation); - } - return; - } - - for (int i = 0; i <= 9; i++) { // loop through digits 0 to 9 - if (isDigitsUsed[i]) { - continue; - } - - permutation.put(characters.get(index), i); - isDigitsUsed[i] = true; - generate(permutation, index + 1, isDigitsUsed); - isDigitsUsed[i] = false; - } - } - - private Optional> get() { - return Optional.ofNullable(validPermutation); - } - - /** - * Returns true if the mapping letters to digits using {@code letterToDigit} will result in having zero as a - * leading digit. - */ - private boolean isLeadingDigitZero(Map letterToDigit) { - return letterToDigit.keySet().stream() - .filter(key -> letterToDigit.get(key) == 0) // Find the character that is mapped to digit 0 - .filter(charMappedToZero -> wordResult.charAt(0) == charMappedToZero // If the first character in - // wordResult is mapped to 0 - || wordsToSum.stream() // If the first character in any of wordsToSum is mapped to 0 - .map(word -> word.charAt(0)) - .anyMatch(character -> character == charMappedToZero)) - .count() == 1; // One letter maps to zero and is a leading character - } - - /** - * Returns true if the {@code letterToDigit} solves the alphametic puzzle. - */ - private boolean isSumTally(Map letterToDigit) { - long actualSum = wordsToSum.stream() - .mapToLong(word -> mapToNumber(letterToDigit, word)) - .sum(); - long expectedSum = mapToNumber(letterToDigit, wordResult); - return actualSum == expectedSum; - } - - /** - * Returns the long value of {@code word}, mapped using {@code letterToDigit}. - */ - private long mapToNumber(Map letterToDigit, String word) { - StringBuilder builder = new StringBuilder(); - - for (int i = 0; i < word.length(); i++) { - int digit = letterToDigit.get(word.charAt(i)); - builder.append(digit); - } - - return Long.parseLong(builder.toString()); - } - } -} diff --git a/src/test/resources/exercises/alphametics/representation b/src/test/resources/exercises/alphametics/representation deleted file mode 100644 index 7bf3232..0000000 --- a/src/test/resources/exercises/alphametics/representation +++ /dev/null @@ -1,85 +0,0 @@ -public class PLACEHOLDER_1 { - - private final List PLACEHOLDER_2; - - private final String PLACEHOLDER_3; - - PLACEHOLDER_1(String PLACEHOLDER_5) { - String[] PLACEHOLDER_4 = PLACEHOLDER_6.split("=="); - PLACEHOLDER_2 = Arrays.stream(PLACEHOLDER_4[0].split("\\+")).map(String::new).map(String::trim).collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); - PLACEHOLDER_3 = PLACEHOLDER_4[1].trim(); - } - - Map PLACEHOLDER_8() throws UnsolvablePuzzleException { - AlphameticsRecursion PLACEHOLDER_9 = new AlphameticsRecursion(PLACEHOLDER_10()); - PLACEHOLDER_11.generate(); - return PLACEHOLDER_11.get().orElseThrow(UnsolvablePuzzleException::new); - } - - private List PLACEHOLDER_10() { - Set PLACEHOLDER_12 = new LinkedHashSet<>(); - PLACEHOLDER_2.forEach(PLACEHOLDER_14 -> PLACEHOLDER_12.addAll(PLACEHOLDER_13(PLACEHOLDER_15))); - PLACEHOLDER_12.addAll(PLACEHOLDER_13(PLACEHOLDER_3)); - return new ArrayList<>(PLACEHOLDER_12); - } - - private List PLACEHOLDER_13(String PLACEHOLDER_13) { - return PLACEHOLDER_17.chars().mapToObj(PLACEHOLDER_16 -> (char) PLACEHOLDER_16).collect(Collectors.toList()); - } - - private class PLACEHOLDER_18 { - - private final List PLACEHOLDER_19; - - private LinkedHashMap PLACEHOLDER_20; - - private PLACEHOLDER_18(List PLACEHOLDER_19) { - this.PLACEHOLDER_19 = PLACEHOLDER_19; - } - - private void PLACEHOLDER_21() { - PLACEHOLDER_21(new LinkedHashMap<>(), 0, new boolean[10]); - } - - private void PLACEHOLDER_21(LinkedHashMap PLACEHOLDER_24, int PLACEHOLDER_22, boolean[] PLACEHOLDER_28) { - if (PLACEHOLDER_22 == PLACEHOLDER_19.size()) { - if (!PLACEHOLDER_23(PLACEHOLDER_25) && PLACEHOLDER_26(PLACEHOLDER_25)) { - PLACEHOLDER_20 = new LinkedHashMap<>(PLACEHOLDER_24); - } - return; - } - for (int PLACEHOLDER_27 = 0; PLACEHOLDER_27 <= 9; PLACEHOLDER_27++) { - if (PLACEHOLDER_28[PLACEHOLDER_27]) { - continue; - } - PLACEHOLDER_25.put(PLACEHOLDER_19.get(PLACEHOLDER_22), PLACEHOLDER_27); - PLACEHOLDER_28[PLACEHOLDER_27] = true; - PLACEHOLDER_21(PLACEHOLDER_25, PLACEHOLDER_22 + 1, PLACEHOLDER_29); - PLACEHOLDER_28[PLACEHOLDER_27] = false; - } - } - - private Optional PLACEHOLDER_31() { - return Optional.ofNullable(PLACEHOLDER_20); - } - - private boolean PLACEHOLDER_23(Map PLACEHOLDER_34) { - return PLACEHOLDER_36.keySet().stream().filter(PLACEHOLDER_35 -> PLACEHOLDER_36.get(PLACEHOLDER_35) == 0).filter(PLACEHOLDER_32 -> PLACEHOLDER_3.charAt(0) == PLACEHOLDER_32 || PLACEHOLDER_2.stream().map(PLACEHOLDER_14 -> PLACEHOLDER_15.charAt(0)).anyMatch(PLACEHOLDER_33 -> PLACEHOLDER_33 == PLACEHOLDER_32)).count() == 1; - } - - private boolean PLACEHOLDER_26(Map PLACEHOLDER_34) { - long PLACEHOLDER_37 = PLACEHOLDER_2.stream().mapToLong(PLACEHOLDER_14 -> PLACEHOLDER_38(PLACEHOLDER_36, PLACEHOLDER_15)).sum(); - long PLACEHOLDER_39 = PLACEHOLDER_38(PLACEHOLDER_36, PLACEHOLDER_3); - return PLACEHOLDER_37 == PLACEHOLDER_39; - } - - private long PLACEHOLDER_38(Map PLACEHOLDER_34, String PLACEHOLDER_14) { - StringBuilder PLACEHOLDER_40 = new StringBuilder(); - for (int PLACEHOLDER_27 = 0; PLACEHOLDER_27 < PLACEHOLDER_15.length(); PLACEHOLDER_27++) { - int PLACEHOLDER_41 = PLACEHOLDER_36.get(PLACEHOLDER_15.charAt(PLACEHOLDER_27)); - PLACEHOLDER_40.append(PLACEHOLDER_41); - } - return Long.parseLong(PLACEHOLDER_40.toString()); - } - } -} diff --git a/src/test/resources/exercises/hello-world/Greeter.java b/src/test/resources/exercises/hello-world/Greeter.java deleted file mode 100644 index 1999860..0000000 --- a/src/test/resources/exercises/hello-world/Greeter.java +++ /dev/null @@ -1,7 +0,0 @@ -class Greeter { - - String getGreeting() { - return "Hello, World!"; - } - -} diff --git a/src/test/resources/exercises/hello-world/representation b/src/test/resources/exercises/hello-world/representation deleted file mode 100644 index 9afda50..0000000 --- a/src/test/resources/exercises/hello-world/representation +++ /dev/null @@ -1,6 +0,0 @@ -class PLACEHOLDER_1 { - - String PLACEHOLDER_2() { - return "Hello, World!"; - } -} diff --git a/src/test/resources/exercises/pig-latin/PigLatinTranslator.java b/src/test/resources/exercises/pig-latin/PigLatinTranslator.java deleted file mode 100644 index 5f67aa2..0000000 --- a/src/test/resources/exercises/pig-latin/PigLatinTranslator.java +++ /dev/null @@ -1,16 +0,0 @@ -import java.util.Arrays; -import java.util.List; - -class PigLatinTranslator { - private List vowels = Arrays.asList('a','e','i','o','u'); - - - - String translate(String phrase) { - if(phrase.chars().anyMatch(c -> vowels.contains((char) c))) { - return phrase + "ay"; - } - - return ""; - } -} diff --git a/src/test/resources/exercises/pig-latin/representation b/src/test/resources/exercises/pig-latin/representation deleted file mode 100644 index 893dd83..0000000 --- a/src/test/resources/exercises/pig-latin/representation +++ /dev/null @@ -1,11 +0,0 @@ -class PLACEHOLDER_1 { - - private List PLACEHOLDER_2 = Arrays.asList('a', 'e', 'i', 'o', 'u'); - - String PLACEHOLDER_3(String PLACEHOLDER_5) { - if (PLACEHOLDER_6.chars().anyMatch(PLACEHOLDER_4 -> PLACEHOLDER_2.contains((char) PLACEHOLDER_4))) { - return PLACEHOLDER_5 + "ay"; - } - return ""; - } -} diff --git a/src/test/resources/exercises/raindrops/representation b/src/test/resources/exercises/raindrops/representation deleted file mode 100644 index dd03273..0000000 --- a/src/test/resources/exercises/raindrops/representation +++ /dev/null @@ -1,19 +0,0 @@ -class PLACEHOLDER_1 { - - String PLACEHOLDER_2(int PLACEHOLDER_4) { - StringBuffer PLACEHOLDER_3 = new StringBuffer(); - if (PLACEHOLDER_4 % 3 == 0) { - PLACEHOLDER_3.append("Pling"); - } - if (PLACEHOLDER_4 % 5 == 0) { - PLACEHOLDER_3.append("Plang"); - } - if (PLACEHOLDER_4 % 7 == 0) { - PLACEHOLDER_3.append("Plong"); - } - if (PLACEHOLDER_3.length() == 0) { - PLACEHOLDER_3.append(PLACEHOLDER_4); - } - return PLACEHOLDER_3.toString(); - } -} diff --git a/src/test/resources/representer/RepresenterTest.testRepresentation.class-and-enum.approved.txt b/src/test/resources/representer/RepresenterTest.testRepresentation.class-and-enum.approved.txt new file mode 100644 index 0000000..117d8c6 --- /dev/null +++ b/src/test/resources/representer/RepresenterTest.testRepresentation.class-and-enum.approved.txt @@ -0,0 +1,38 @@ +enum PLACEHOLDER_01 { + PLACEHOLDER_07(1), + PLACEHOLDER_08(2), + PLACEHOLDER_09(4), + PLACEHOLDER_10(8), + PLACEHOLDER_11(16), + PLACEHOLDER_12(32), + PLACEHOLDER_13(64), + PLACEHOLDER_14(128); + + private final int PLACEHOLDER_06; + + PLACEHOLDER_01(int PLACEHOLDER_16) { + this.PLACEHOLDER_06 = PLACEHOLDER_16; + } + + int PLACEHOLDER_03() { + return PLACEHOLDER_06; + } +} + +class PLACEHOLDER_02 { + private int PLACEHOLDER_15; + + PLACEHOLDER_02(int PLACEHOLDER_17) { + this.PLACEHOLDER_15 = PLACEHOLDER_17; + } + + List PLACEHOLDER_04() { + return EnumSet.allOf(PLACEHOLDER_01.class).stream() + .filter(this::PLACEHOLDER_05) + .collect(Collectors.toList()); + } + + boolean PLACEHOLDER_05(PLACEHOLDER_01 PLACEHOLDER_18) { + return (PLACEHOLDER_15 & PLACEHOLDER_18.PLACEHOLDER_03()) != 0; + } +} diff --git a/src/test/resources/representer/RepresenterTest.testRepresentation.class-with-nested-enum.approved.txt b/src/test/resources/representer/RepresenterTest.testRepresentation.class-with-nested-enum.approved.txt new file mode 100644 index 0000000..b1a8223 --- /dev/null +++ b/src/test/resources/representer/RepresenterTest.testRepresentation.class-with-nested-enum.approved.txt @@ -0,0 +1,87 @@ +class PLACEHOLDER_02 { + private static final int PLACEHOLDER_08 = 5; + + private static final int PLACEHOLDER_09 = 26; + + private static final int PLACEHOLDER_10 = 97; + + private enum PLACEHOLDER_01 { + PLACEHOLDER_11, + PLACEHOLDER_12; + } + + String PLACEHOLDER_03(String PLACEHOLDER_13, int PLACEHOLDER_14, int PLACEHOLDER_15) { + return PLACEHOLDER_06( + PLACEHOLDER_07( + PLACEHOLDER_13, + PLACEHOLDER_14, + PLACEHOLDER_15, + PLACEHOLDER_01.PLACEHOLDER_11)); + } + + String PLACEHOLDER_04(String PLACEHOLDER_16, int PLACEHOLDER_17, int PLACEHOLDER_18) { + return PLACEHOLDER_07( + PLACEHOLDER_16, PLACEHOLDER_17, PLACEHOLDER_18, PLACEHOLDER_01.PLACEHOLDER_12); + } + + private static int PLACEHOLDER_05(int PLACEHOLDER_19) { + int PLACEHOLDER_20 = Math.floorMod(PLACEHOLDER_19, PLACEHOLDER_09); + for (int PLACEHOLDER_21 = 1; PLACEHOLDER_21 < PLACEHOLDER_09; PLACEHOLDER_21++) { + if (Math.floorMod(PLACEHOLDER_20 * PLACEHOLDER_21, PLACEHOLDER_09) == 1) { + return PLACEHOLDER_21; + } + } + return 1; + } + + private static String PLACEHOLDER_06(String PLACEHOLDER_22) { + List PLACEHOLDER_23 = new ArrayList<>(); + for (int PLACEHOLDER_24 = 0; + PLACEHOLDER_24 < PLACEHOLDER_22.length(); + PLACEHOLDER_24 += PLACEHOLDER_08) { + PLACEHOLDER_23.add( + PLACEHOLDER_22.substring( + PLACEHOLDER_24, + Math.min(PLACEHOLDER_22.length(), PLACEHOLDER_24 + PLACEHOLDER_08))); + } + return String.join(" ", PLACEHOLDER_23); + } + + private static String PLACEHOLDER_07( + String PLACEHOLDER_25, + int PLACEHOLDER_26, + int PLACEHOLDER_27, + PLACEHOLDER_01 PLACEHOLDER_28) { + int PLACEHOLDER_29 = PLACEHOLDER_05(PLACEHOLDER_26); + if (PLACEHOLDER_29 == 1) { + throw new IllegalArgumentException("Error: keyA and alphabet size must be coprime."); + } + StringBuilder PLACEHOLDER_30 = new StringBuilder(); + PLACEHOLDER_25 + .chars() + .filter(Character::isLetterOrDigit) + .map(Character::toLowerCase) + .forEach( + PLACEHOLDER_31 -> { + int PLACEHOLDER_32 = PLACEHOLDER_31 - PLACEHOLDER_10; + if (PLACEHOLDER_32 < 0) { + PLACEHOLDER_30.appendCodePoint(PLACEHOLDER_31); + } else if (PLACEHOLDER_28 == PLACEHOLDER_01.PLACEHOLDER_11) { + PLACEHOLDER_30.appendCodePoint( + PLACEHOLDER_10 + + Math.floorMod( + (PLACEHOLDER_26 * PLACEHOLDER_32) + + PLACEHOLDER_27, + PLACEHOLDER_09)); + } else if (PLACEHOLDER_28 == PLACEHOLDER_01.PLACEHOLDER_12) { + PLACEHOLDER_30.appendCodePoint( + PLACEHOLDER_10 + + Math.floorMod( + PLACEHOLDER_29 + * (PLACEHOLDER_32 - PLACEHOLDER_27), + PLACEHOLDER_09)); + } + }); + return PLACEHOLDER_30.toString(); + } +} diff --git a/src/test/resources/representer/RepresenterTest.testRepresentation.generic-type-arguments.approved.txt b/src/test/resources/representer/RepresenterTest.testRepresentation.generic-type-arguments.approved.txt new file mode 100644 index 0000000..5f7f1b6 --- /dev/null +++ b/src/test/resources/representer/RepresenterTest.testRepresentation.generic-type-arguments.approved.txt @@ -0,0 +1,11 @@ +class PLACEHOLDER_02 { + static List PLACEHOLDER_03( + List PLACEHOLDER_04, + Function PLACEHOLDER_05) { + List PLACEHOLDER_06 = new ArrayList<>(); + for (PLACEHOLDER_01 PLACEHOLDER_07 : PLACEHOLDER_04) { + PLACEHOLDER_06.add(PLACEHOLDER_05.apply(PLACEHOLDER_07)); + } + return PLACEHOLDER_06; + } +} diff --git a/src/test/resources/representer/RepresenterTest.testRepresentation.if-statements-without-block-bodies.approved.txt b/src/test/resources/representer/RepresenterTest.testRepresentation.if-statements-without-block-bodies.approved.txt new file mode 100644 index 0000000..ff6bb07 --- /dev/null +++ b/src/test/resources/representer/RepresenterTest.testRepresentation.if-statements-without-block-bodies.approved.txt @@ -0,0 +1,18 @@ +class PLACEHOLDER_01 { + String PLACEHOLDER_02(int PLACEHOLDER_03) { + StringBuffer PLACEHOLDER_04 = new StringBuffer(); + if ((PLACEHOLDER_03 % 3) == 0) { + PLACEHOLDER_04.append("Pling"); + } + if ((PLACEHOLDER_03 % 5) == 0) { + PLACEHOLDER_04.append("Plang"); + } + if ((PLACEHOLDER_03 % 7) == 0) { + PLACEHOLDER_04.append("Plong"); + } + if (PLACEHOLDER_04.length() == 0) { + PLACEHOLDER_04.append(PLACEHOLDER_03); + } + return PLACEHOLDER_04.toString(); + } +} diff --git a/src/test/resources/representer/RepresenterTest.testRepresentation.lambda-arguments.approved.txt b/src/test/resources/representer/RepresenterTest.testRepresentation.lambda-arguments.approved.txt new file mode 100644 index 0000000..a9a16f6 --- /dev/null +++ b/src/test/resources/representer/RepresenterTest.testRepresentation.lambda-arguments.approved.txt @@ -0,0 +1,7 @@ +class PLACEHOLDER_01 { + List PLACEHOLDER_02(List PLACEHOLDER_03) { + return PLACEHOLDER_03.stream() + .filter(PLACEHOLDER_04 -> !PLACEHOLDER_04.isEmpty()) + .collect(Collectors.toList()); + } +} diff --git a/src/test/resources/representer/RepresenterTest.testRepresentation.record-class.approved.txt b/src/test/resources/representer/RepresenterTest.testRepresentation.record-class.approved.txt new file mode 100644 index 0000000..f056f0d --- /dev/null +++ b/src/test/resources/representer/RepresenterTest.testRepresentation.record-class.approved.txt @@ -0,0 +1,3 @@ +record PLACEHOLDER_01(String PLACEHOLDER_02) { + void PLACEHOLDER_03(int PLACEHOLDER_04) {} +} diff --git a/src/test/resources/representer/RepresenterTest.testRepresentation.simple.approved.txt b/src/test/resources/representer/RepresenterTest.testRepresentation.simple.approved.txt new file mode 100644 index 0000000..830a110 --- /dev/null +++ b/src/test/resources/representer/RepresenterTest.testRepresentation.simple.approved.txt @@ -0,0 +1,5 @@ +class PLACEHOLDER_01 { + String PLACEHOLDER_02() { + return "Hello, World!"; + } +} diff --git a/src/test/resources/representer/RepresenterTest.testRepresentation.switch-expression.approved.txt b/src/test/resources/representer/RepresenterTest.testRepresentation.switch-expression.approved.txt new file mode 100644 index 0000000..4e8d004 --- /dev/null +++ b/src/test/resources/representer/RepresenterTest.testRepresentation.switch-expression.approved.txt @@ -0,0 +1,9 @@ +class PLACEHOLDER_01 { + int PLACEHOLDER_02(String PLACEHOLDER_03) { + return switch (PLACEHOLDER_03) { + case "Lord of the Rings" -> 5; + case "Harry Potter" -> 4; + default -> -1; + }; + } +} diff --git a/src/test/resources/representer/block_scenario_input b/src/test/resources/representer/block_scenario_input deleted file mode 100644 index 6e96b61..0000000 --- a/src/test/resources/representer/block_scenario_input +++ /dev/null @@ -1,5 +0,0 @@ -class Twofer { - String twofer(String name) { - return "One for " + (name != null ? name : "you") + ", one for me."; - } -} \ No newline at end of file diff --git a/src/test/resources/representer/comments_scenario_input b/src/test/resources/representer/comments_scenario_input deleted file mode 100644 index 2c3d8ac..0000000 --- a/src/test/resources/representer/comments_scenario_input +++ /dev/null @@ -1,14 +0,0 @@ -/* multiline - - -comment */ - -package myTest; - -class Twofer { - // method - String twofer(String name) { - - return "One for " + (name != null ? name : "you") + ", one for me."; - } -} \ No newline at end of file diff --git a/src/test/resources/representer/import_scenario_input b/src/test/resources/representer/import_scenario_input deleted file mode 100644 index f71f011..0000000 --- a/src/test/resources/representer/import_scenario_input +++ /dev/null @@ -1,10 +0,0 @@ -package myTest; - -import java.util.Random; -import java.lang.Integer; - -class Twofer { - String twofer(String name) { - return "One for " + (name != null ? name : "you") + ", one for me."; - } -} \ No newline at end of file diff --git a/src/test/resources/representer/package_scenario_input b/src/test/resources/representer/package_scenario_input deleted file mode 100644 index 9c67a8b..0000000 --- a/src/test/resources/representer/package_scenario_input +++ /dev/null @@ -1,10 +0,0 @@ -package myTest; - - - -class Twofer { - String twofer(String name) { - return "One for " + (name != null ? name : "you") + ", one for me."; - } -} - diff --git a/src/test/resources/representer/result_expected b/src/test/resources/representer/result_expected deleted file mode 100644 index 61206fc..0000000 --- a/src/test/resources/representer/result_expected +++ /dev/null @@ -1,6 +0,0 @@ -class PLACEHOLDER_1 { - - String PLACEHOLDER_2(String PLACEHOLDER_3) { - return "One for " + (PLACEHOLDER_3 != null ? PLACEHOLDER_3 : "you") + ", one for me."; - } -} diff --git a/src/test/resources/representer/simple_scenario_input b/src/test/resources/representer/simple_scenario_input deleted file mode 100644 index 364a659..0000000 --- a/src/test/resources/representer/simple_scenario_input +++ /dev/null @@ -1,6 +0,0 @@ -class Twofer { - - String twofer(String name) { - return "One for " + (name != null ? name : "you") + ", one for me."; - } -} \ No newline at end of file diff --git a/src/test/resources/exercises/allergies/Allergen.java b/src/test/resources/scenarios/class-and-enum/Allergen.java similarity index 100% rename from src/test/resources/exercises/allergies/Allergen.java rename to src/test/resources/scenarios/class-and-enum/Allergen.java diff --git a/src/test/resources/exercises/allergies/Allergies.java b/src/test/resources/scenarios/class-and-enum/Allergies.java similarity index 100% rename from src/test/resources/exercises/allergies/Allergies.java rename to src/test/resources/scenarios/class-and-enum/Allergies.java diff --git a/src/test/resources/exercises/affine-cipher/AffineCipher.java b/src/test/resources/scenarios/class-with-nested-enum/AffineCipher.java similarity index 100% rename from src/test/resources/exercises/affine-cipher/AffineCipher.java rename to src/test/resources/scenarios/class-with-nested-enum/AffineCipher.java diff --git a/src/test/resources/exercises/accumulate/Accumulate.java b/src/test/resources/scenarios/generic-type-arguments/Accumulate.java similarity index 100% rename from src/test/resources/exercises/accumulate/Accumulate.java rename to src/test/resources/scenarios/generic-type-arguments/Accumulate.java diff --git a/src/test/resources/exercises/raindrops/RaindropConverter.java b/src/test/resources/scenarios/if-statements-without-block-bodies/RaindropConverter.java similarity index 57% rename from src/test/resources/exercises/raindrops/RaindropConverter.java rename to src/test/resources/scenarios/if-statements-without-block-bodies/RaindropConverter.java index ab43dc5..f1118ee 100644 --- a/src/test/resources/exercises/raindrops/RaindropConverter.java +++ b/src/test/resources/scenarios/if-statements-without-block-bodies/RaindropConverter.java @@ -1,19 +1,15 @@ class RaindropConverter { - - String convert(int number) { + String convert(int number) { StringBuffer buffer = new StringBuffer(); - if(number % 3 == 0) { + + if(number % 3 == 0) buffer.append("Pling"); - } - if(number % 5 == 0) { + if(number % 5 == 0) buffer.append("Plang"); - } - if(number % 7 == 0) { + if(number % 7 == 0) buffer.append("Plong"); - } - if(buffer.length() == 0) { + if(buffer.length() == 0) buffer.append(number); - } return buffer.toString(); } diff --git a/src/test/resources/scenarios/lambda-arguments/WordFilter.java b/src/test/resources/scenarios/lambda-arguments/WordFilter.java new file mode 100644 index 0000000..6762402 --- /dev/null +++ b/src/test/resources/scenarios/lambda-arguments/WordFilter.java @@ -0,0 +1,11 @@ +import java.util.List; +import java.util.stream.Collectors; + +class WordFilter { + List filter(List words) { + return words + .stream() + .filter(word -> !word.isEmpty()) + .collect(Collectors.toList()); + } +} diff --git a/src/test/resources/scenarios/record-class/Record.java b/src/test/resources/scenarios/record-class/Record.java new file mode 100644 index 0000000..c5acd88 --- /dev/null +++ b/src/test/resources/scenarios/record-class/Record.java @@ -0,0 +1,4 @@ +record Record(String field) { + void method(int argument) { + } +} diff --git a/src/test/resources/scenarios/simple/Greeter.java b/src/test/resources/scenarios/simple/Greeter.java new file mode 100644 index 0000000..22eaacb --- /dev/null +++ b/src/test/resources/scenarios/simple/Greeter.java @@ -0,0 +1,9 @@ +class Greeter { + + + String getGreeting() { + // return "Goodbye, Mars!"; + return "Hello, World!" + } + +} diff --git a/src/test/resources/scenarios/switch-expression/SwitchExpression.java b/src/test/resources/scenarios/switch-expression/SwitchExpression.java new file mode 100644 index 0000000..0286c7b --- /dev/null +++ b/src/test/resources/scenarios/switch-expression/SwitchExpression.java @@ -0,0 +1,9 @@ +class SwitchExpression { + int rate(String book) { + return switch (book) { + case "Lord of the Rings" -> 5; + case "Harry Potter" -> 4; + default -> -1; + }; + } +} diff --git a/tests/multiple-solution-files/expected_mapping.json b/tests/multiple-solution-files/expected_mapping.json index 36b2d06..24510e8 100644 --- a/tests/multiple-solution-files/expected_mapping.json +++ b/tests/multiple-solution-files/expected_mapping.json @@ -1,20 +1,16 @@ { - "REVERSE_SIGNALS_BIT_POSITION": "PLACEHOLDER_7", - "PLACEHOLDER_11": "PLACEHOLDER_14", - "PLACEHOLDER_12": "PLACEHOLDER_13", - "WINK": "PLACEHOLDER_2", - "JUMP": "PLACEHOLDER_5", - "result": "PLACEHOLDER_9", - "PLACEHOLDER_7": "PLACEHOLDER_17", - "number": "PLACEHOLDER_11", - "Signal": "PLACEHOLDER_1", - "HandshakeCalculator": "PLACEHOLDER_6", - "calculateHandshake": "PLACEHOLDER_8", - "CLOSE_YOUR_EYES": "PLACEHOLDER_4", - "PLACEHOLDER_9": "PLACEHOLDER_15", - "isBitSet": "PLACEHOLDER_10", - "position": "PLACEHOLDER_18", - "DOUBLE_BLINK": "PLACEHOLDER_3", - "signal": "PLACEHOLDER_12", - "PLACEHOLDER_1": "PLACEHOLDER_16" + "PLACEHOLDER_01": "HandshakeCalculator", + "PLACEHOLDER_02": "Signal", + "PLACEHOLDER_03": "calculateHandshake", + "PLACEHOLDER_04": "isBitSet", + "PLACEHOLDER_05": "REVERSE_SIGNALS_BIT_POSITION", + "PLACEHOLDER_06": "WINK", + "PLACEHOLDER_07": "DOUBLE_BLINK", + "PLACEHOLDER_08": "CLOSE_YOUR_EYES", + "PLACEHOLDER_09": "JUMP", + "PLACEHOLDER_10": "number", + "PLACEHOLDER_11": "result", + "PLACEHOLDER_12": "signal", + "PLACEHOLDER_13": "position", + "PLACEHOLDER_14": "number" } \ No newline at end of file diff --git a/tests/multiple-solution-files/expected_representation.json b/tests/multiple-solution-files/expected_representation.json index 915d838..12dc0a6 100644 --- a/tests/multiple-solution-files/expected_representation.json +++ b/tests/multiple-solution-files/expected_representation.json @@ -1 +1,3 @@ -{"version": 1} \ No newline at end of file +{ + "version": 2 +} \ No newline at end of file diff --git a/tests/multiple-solution-files/expected_representation.txt b/tests/multiple-solution-files/expected_representation.txt index fa19ff9..7688432 100644 --- a/tests/multiple-solution-files/expected_representation.txt +++ b/tests/multiple-solution-files/expected_representation.txt @@ -1,26 +1,27 @@ -enum PLACEHOLDER_1 { +final class PLACEHOLDER_01 { + private static final int PLACEHOLDER_05 = 4; - PLACEHOLDER_2, PLACEHOLDER_3, PLACEHOLDER_4, PLACEHOLDER_5 -} - -final class PLACEHOLDER_6 { - - private static final int PLACEHOLDER_7 = 4; - - List PLACEHOLDER_8(final int PLACEHOLDER_11) { - final List PLACEHOLDER_9 = new ArrayList<>(); - for (final Signal PLACEHOLDER_12 : PLACEHOLDER_16.values()) { - if (PLACEHOLDER_10(PLACEHOLDER_13.ordinal(), PLACEHOLDER_14)) { - PLACEHOLDER_15.add(PLACEHOLDER_12); + List PLACEHOLDER_03(final int PLACEHOLDER_10) { + final List PLACEHOLDER_11 = new ArrayList<>(); + for (final PLACEHOLDER_02 PLACEHOLDER_12 : PLACEHOLDER_02.values()) { + if (PLACEHOLDER_04(PLACEHOLDER_12.ordinal(), PLACEHOLDER_10)) { + PLACEHOLDER_11.add(PLACEHOLDER_12); } } - if (PLACEHOLDER_10(PLACEHOLDER_17, PLACEHOLDER_14)) { - Collections.reverse(PLACEHOLDER_9); + if (PLACEHOLDER_04(PLACEHOLDER_05, PLACEHOLDER_10)) { + Collections.reverse(PLACEHOLDER_11); } - return PLACEHOLDER_15; + return PLACEHOLDER_11; } - private boolean PLACEHOLDER_10(final int PLACEHOLDER_18, final int PLACEHOLDER_11) { - return ((PLACEHOLDER_11 >> PLACEHOLDER_18) & 1) == 1; + private boolean PLACEHOLDER_04(final int PLACEHOLDER_13, final int PLACEHOLDER_14) { + return ((PLACEHOLDER_14 >> PLACEHOLDER_13) & 1) == 1; } } + +enum PLACEHOLDER_02 { + PLACEHOLDER_06, + PLACEHOLDER_07, + PLACEHOLDER_08, + PLACEHOLDER_09; +} diff --git a/tests/normalize-whitespace/.meta/src/reference/java/Anagram.java b/tests/normalize-whitespace/.meta/src/reference/java/Anagram.java deleted file mode 100644 index f0a9718..0000000 --- a/tests/normalize-whitespace/.meta/src/reference/java/Anagram.java +++ /dev/null @@ -1,43 +0,0 @@ -import java.util.*; -import java.util.stream.Collectors; - -class Anagram { - - private final AnagramSubject anagramSubject; - - Anagram(String word) { - anagramSubject = new AnagramSubject(word); - } - - List match(List candidates) { - return candidates.stream() - .filter(anagramSubject::anagramOf) - .collect(Collectors.toList()); - } - - static final class AnagramSubject { - - private final String word; - private final char[] fingerprint; - - AnagramSubject(String other) { - this.word = other; - this.fingerprint = canonicalize(other); - } - - boolean anagramOf(String other) { - return !duplicate(other) && Arrays.equals(fingerprint, canonicalize(other)); - } - - private boolean duplicate(String other) { - return word.equalsIgnoreCase(other); - } - - private char[] canonicalize(String other) { - char[] chars = other.toLowerCase().toCharArray(); - Arrays.sort(chars); - return chars; - } - } -} - diff --git a/tests/normalize-whitespace/.meta/tests.toml b/tests/normalize-whitespace/.meta/tests.toml deleted file mode 100644 index 4f43811..0000000 --- a/tests/normalize-whitespace/.meta/tests.toml +++ /dev/null @@ -1,80 +0,0 @@ -# This is an auto-generated file. -# -# Regenerating this file via `configlet sync` will: -# - Recreate every `description` key/value pair -# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications -# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) -# - Preserve any other key/value pair -# -# As user-added comments (using the # character) will be removed when this file -# is regenerated, comments can be added via a `comment` key. - -[dd40c4d2-3c8b-44e5-992a-f42b393ec373] -description = "no matches" - -[b3cca662-f50a-489e-ae10-ab8290a09bdc] -description = "detects two anagrams" -include = false - -[03eb9bbe-8906-4ea0-84fa-ffe711b52c8b] -description = "detects two anagrams" -reimplements = "b3cca662-f50a-489e-ae10-ab8290a09bdc" - -[a27558ee-9ba0-4552-96b1-ecf665b06556] -description = "does not detect anagram subsets" - -[64cd4584-fc15-4781-b633-3d814c4941a4] -description = "detects anagram" - -[99c91beb-838f-4ccd-b123-935139917283] -description = "detects three anagrams" - -[78487770-e258-4e1f-a646-8ece10950d90] -description = "detects multiple anagrams with different case" - -[1d0ab8aa-362f-49b7-9902-3d0c668d557b] -description = "does not detect non-anagrams with identical checksum" - -[9e632c0b-c0b1-4804-8cc1-e295dea6d8a8] -description = "detects anagrams case-insensitively" - -[b248e49f-0905-48d2-9c8d-bd02d8c3e392] -description = "detects anagrams using case-insensitive subject" - -[f367325c-78ec-411c-be76-e79047f4bd54] -description = "detects anagrams using case-insensitive possible matches" - -[7cc195ad-e3c7-44ee-9fd2-d3c344806a2c] -description = "does not detect an anagram if the original word is repeated" -include = false - -[630abb71-a94e-4715-8395-179ec1df9f91] -description = "does not detect an anagram if the original word is repeated" -reimplements = "7cc195ad-e3c7-44ee-9fd2-d3c344806a2c" - -[9878a1c9-d6ea-4235-ae51-3ea2befd6842] -description = "anagrams must use all letters exactly once" - -[85757361-4535-45fd-ac0e-3810d40debc1] -description = "words are not anagrams of themselves (case-insensitive)" -include = false - -[68934ed0-010b-4ef9-857a-20c9012d1ebf] -description = "words are not anagrams of themselves" -reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" - -[589384f3-4c8a-4e7d-9edc-51c3e5f0c90e] -description = "words are not anagrams of themselves even if letter case is partially different" -reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" - -[ba53e423-7e02-41ee-9ae2-71f91e6d18e6] -description = "words are not anagrams of themselves even if letter case is completely different" -reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" - -[a0705568-628c-4b55-9798-82e4acde51ca] -description = "words other than themselves can be anagrams" -include = false - -[33d3f67e-fbb9-49d3-a90e-0beb00861da7] -description = "words other than themselves can be anagrams" -reimplements = "a0705568-628c-4b55-9798-82e4acde51ca" diff --git a/tests/normalize-whitespace/.meta/version b/tests/normalize-whitespace/.meta/version deleted file mode 100644 index bc80560..0000000 --- a/tests/normalize-whitespace/.meta/version +++ /dev/null @@ -1 +0,0 @@ -1.5.0 diff --git a/tests/normalize-whitespace/expected_mapping.json b/tests/normalize-whitespace/expected_mapping.json deleted file mode 100644 index 846d45f..0000000 --- a/tests/normalize-whitespace/expected_mapping.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "candidates": "PLACEHOLDER_5", - "match": "PLACEHOLDER_4", - "Anagram": "PLACEHOLDER_1", - "String": "PLACEHOLDER_3", - "word": "PLACEHOLDER_2" -} \ No newline at end of file diff --git a/tests/normalize-whitespace/expected_representation.json b/tests/normalize-whitespace/expected_representation.json deleted file mode 100644 index 915d838..0000000 --- a/tests/normalize-whitespace/expected_representation.json +++ /dev/null @@ -1 +0,0 @@ -{"version": 1} \ No newline at end of file diff --git a/tests/normalize-whitespace/expected_representation.txt b/tests/normalize-whitespace/expected_representation.txt deleted file mode 100644 index 8801585..0000000 --- a/tests/normalize-whitespace/expected_representation.txt +++ /dev/null @@ -1,10 +0,0 @@ -class PLACEHOLDER_1 { - - public PLACEHOLDER_1(String PLACEHOLDER_2) { - throw new UnsupportedOperationException("Please implement the Anagram(String word) constructor"); - } - - public List PLACEHOLDER_4(List PLACEHOLDER_5) { - throw new UnsupportedOperationException("Please implement the match() method"); - } -} diff --git a/tests/normalize-whitespace/src/main/java/Anagram.java b/tests/normalize-whitespace/src/main/java/Anagram.java deleted file mode 100644 index 6394f9d..0000000 --- a/tests/normalize-whitespace/src/main/java/Anagram.java +++ /dev/null @@ -1,15 +0,0 @@ -import java.util.List; - -class Anagram { - public Anagram(String word) { - throw new UnsupportedOperationException("Please implement the Anagram(String word) constructor"); - } - - - - public List - match(List candidates) { - throw new UnsupportedOperationException( "Please implement the match() method" ); - } - -} diff --git a/tests/normalize-whitespace/src/test/java/AnagramTest.java b/tests/normalize-whitespace/src/test/java/AnagramTest.java deleted file mode 100644 index 9464252..0000000 --- a/tests/normalize-whitespace/src/test/java/AnagramTest.java +++ /dev/null @@ -1,170 +0,0 @@ -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Ignore; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; - -public class AnagramTest { - - @Test - public void testNoMatches() { - Anagram detector = new Anagram("diaper"); - - assertThat( - detector.match( - Arrays.asList("hello", "world", "zombies", "pants"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testDetectsTwoAnagrams() { - Anagram detector = new Anagram("solemn"); - - assertThat(detector.match(Arrays.asList("lemons", "cherry", "melons"))) - .containsExactlyInAnyOrder("lemons", "melons"); - } - - @Ignore("Remove to run test") - @Test - public void testEliminateAnagramSubsets() { - Anagram detector = new Anagram("good"); - - assertThat(detector.match(Arrays.asList("dog", "goody"))).isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testDetectLongerAnagram() { - Anagram detector = new Anagram("listen"); - - assertThat( - detector.match( - Arrays.asList("enlists", "google", "inlets", "banana"))) - .containsExactlyInAnyOrder("inlets"); - } - - @Ignore("Remove to run test") - @Test - public void testDetectMultipleAnagramsForLongerWord() { - Anagram detector = new Anagram("allergy"); - assertThat( - detector.match( - Arrays.asList( - "gallery", - "ballerina", - "regally", - "clergy", - "largely", - "leading"))) - .containsExactlyInAnyOrder("gallery", "regally", "largely"); - } - - @Ignore("Remove to run test") - @Test - public void testDetectsMultipleAnagramsWithDifferentCase() { - Anagram detector = new Anagram("nose"); - - assertThat(detector.match(Arrays.asList("Eons", "ONES"))) - .containsExactlyInAnyOrder("Eons", "ONES"); - } - - @Ignore("Remove to run test") - @Test - public void testEliminateAnagramsWithSameChecksum() { - Anagram detector = new Anagram("mass"); - - assertThat(detector.match(Collections.singletonList("last"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testCaseInsensitiveWhenBothAnagramAndSubjectStartWithUpperCaseLetter() { - Anagram detector = new Anagram("Orchestra"); - - assertThat( - detector.match( - Arrays.asList("cashregister", "Carthorse", "radishes"))) - .containsExactlyInAnyOrder("Carthorse"); - } - - @Ignore("Remove to run test") - @Test - public void testCaseInsensitiveWhenSubjectStartsWithUpperCaseLetter() { - Anagram detector = new Anagram("Orchestra"); - - assertThat( - detector.match( - Arrays.asList("cashregister", "carthorse", "radishes"))) - .containsExactlyInAnyOrder("carthorse"); - } - - @Ignore("Remove to run test") - @Test - public void testCaseInsensitiveWhenAnagramStartsWithUpperCaseLetter() { - Anagram detector = new Anagram("orchestra"); - - assertThat( - detector.match( - Arrays.asList("cashregister", "Carthorse", "radishes"))) - .containsExactlyInAnyOrder("Carthorse"); - } - - @Ignore("Remove to run test") - @Test - public void testIdenticalWordRepeatedIsNotAnagram() { - Anagram detector = new Anagram("go"); - - assertThat(detector.match(Collections.singletonList("goGoGO"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testAnagramMustUseAllLettersExactlyOnce() { - Anagram detector = new Anagram("tapper"); - - assertThat(detector.match(Collections.singletonList("patter"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testWordsAreNotAnagramsOfThemselvesCaseInsensitive() { - Anagram detector = new Anagram("BANANA"); - - assertThat(detector.match(Collections.singletonList("BANANA"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testWordsAreNotAnagramsOfThemselvesEvenIfLetterCaseIsPartiallyDifferent() { - Anagram detector = new Anagram("BANANA"); - - assertThat(detector.match(Collections.singletonList("Banana"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testWordsAreNotAnagramsOfThemselvesEvenIfLetterCaseIsCompletelyDifferent() { - Anagram detector = new Anagram("BANANA"); - - assertThat(detector.match(Collections.singletonList("banana"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testWordsOtherThanThemselvesCanBeAnagrams() { - Anagram detector = new Anagram("LISTEN"); - - assertThat(detector.match(Arrays.asList("LISTEN", "Silent"))) - .containsExactlyInAnyOrder("Silent"); - } - -} diff --git a/tests/remove-comments/.meta/config.json b/tests/remove-comments/.meta/config.json deleted file mode 100644 index 97be250..0000000 --- a/tests/remove-comments/.meta/config.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "authors": [ - "wdjunaidi" - ], - "contributors": [ - "ericbalawejder", - "FridaTveit", - "javaeeeee", - "jmrunkle", - "jonnynabors", - "jtigger", - "kytrinyx", - "lemoncurry", - "matthewmorgan", - "mirkoperillo", - "msomji", - "muzimuzhi", - "redshirt4", - "rohit1104", - "sjwarner-bp", - "SleeplessByte", - "Smarticles101", - "sshine", - "stkent", - "vdemeester", - "Zaldrick", - "Zhiyuan-Amos" - ], - "files": { - "solution": [ - "src/main/java/Anagram.java" - ], - "test": [ - "src/test/java/AnagramTest.java" - ], - "example": [ - ".meta/src/reference/java/Anagram.java" - ], - "invalidator": [ - "build.gradle" - ] - }, - "blurb": "Given a word and a list of possible anagrams, select the correct sublist.", - "source": "Inspired by the Extreme Startup game", - "source_url": "https://github.com/rchatley/extreme_startup" -} diff --git a/tests/remove-comments/.meta/src/reference/java/Anagram.java b/tests/remove-comments/.meta/src/reference/java/Anagram.java deleted file mode 100644 index f0a9718..0000000 --- a/tests/remove-comments/.meta/src/reference/java/Anagram.java +++ /dev/null @@ -1,43 +0,0 @@ -import java.util.*; -import java.util.stream.Collectors; - -class Anagram { - - private final AnagramSubject anagramSubject; - - Anagram(String word) { - anagramSubject = new AnagramSubject(word); - } - - List match(List candidates) { - return candidates.stream() - .filter(anagramSubject::anagramOf) - .collect(Collectors.toList()); - } - - static final class AnagramSubject { - - private final String word; - private final char[] fingerprint; - - AnagramSubject(String other) { - this.word = other; - this.fingerprint = canonicalize(other); - } - - boolean anagramOf(String other) { - return !duplicate(other) && Arrays.equals(fingerprint, canonicalize(other)); - } - - private boolean duplicate(String other) { - return word.equalsIgnoreCase(other); - } - - private char[] canonicalize(String other) { - char[] chars = other.toLowerCase().toCharArray(); - Arrays.sort(chars); - return chars; - } - } -} - diff --git a/tests/remove-comments/.meta/tests.toml b/tests/remove-comments/.meta/tests.toml deleted file mode 100644 index 4f43811..0000000 --- a/tests/remove-comments/.meta/tests.toml +++ /dev/null @@ -1,80 +0,0 @@ -# This is an auto-generated file. -# -# Regenerating this file via `configlet sync` will: -# - Recreate every `description` key/value pair -# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications -# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) -# - Preserve any other key/value pair -# -# As user-added comments (using the # character) will be removed when this file -# is regenerated, comments can be added via a `comment` key. - -[dd40c4d2-3c8b-44e5-992a-f42b393ec373] -description = "no matches" - -[b3cca662-f50a-489e-ae10-ab8290a09bdc] -description = "detects two anagrams" -include = false - -[03eb9bbe-8906-4ea0-84fa-ffe711b52c8b] -description = "detects two anagrams" -reimplements = "b3cca662-f50a-489e-ae10-ab8290a09bdc" - -[a27558ee-9ba0-4552-96b1-ecf665b06556] -description = "does not detect anagram subsets" - -[64cd4584-fc15-4781-b633-3d814c4941a4] -description = "detects anagram" - -[99c91beb-838f-4ccd-b123-935139917283] -description = "detects three anagrams" - -[78487770-e258-4e1f-a646-8ece10950d90] -description = "detects multiple anagrams with different case" - -[1d0ab8aa-362f-49b7-9902-3d0c668d557b] -description = "does not detect non-anagrams with identical checksum" - -[9e632c0b-c0b1-4804-8cc1-e295dea6d8a8] -description = "detects anagrams case-insensitively" - -[b248e49f-0905-48d2-9c8d-bd02d8c3e392] -description = "detects anagrams using case-insensitive subject" - -[f367325c-78ec-411c-be76-e79047f4bd54] -description = "detects anagrams using case-insensitive possible matches" - -[7cc195ad-e3c7-44ee-9fd2-d3c344806a2c] -description = "does not detect an anagram if the original word is repeated" -include = false - -[630abb71-a94e-4715-8395-179ec1df9f91] -description = "does not detect an anagram if the original word is repeated" -reimplements = "7cc195ad-e3c7-44ee-9fd2-d3c344806a2c" - -[9878a1c9-d6ea-4235-ae51-3ea2befd6842] -description = "anagrams must use all letters exactly once" - -[85757361-4535-45fd-ac0e-3810d40debc1] -description = "words are not anagrams of themselves (case-insensitive)" -include = false - -[68934ed0-010b-4ef9-857a-20c9012d1ebf] -description = "words are not anagrams of themselves" -reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" - -[589384f3-4c8a-4e7d-9edc-51c3e5f0c90e] -description = "words are not anagrams of themselves even if letter case is partially different" -reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" - -[ba53e423-7e02-41ee-9ae2-71f91e6d18e6] -description = "words are not anagrams of themselves even if letter case is completely different" -reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" - -[a0705568-628c-4b55-9798-82e4acde51ca] -description = "words other than themselves can be anagrams" -include = false - -[33d3f67e-fbb9-49d3-a90e-0beb00861da7] -description = "words other than themselves can be anagrams" -reimplements = "a0705568-628c-4b55-9798-82e4acde51ca" diff --git a/tests/remove-comments/.meta/version b/tests/remove-comments/.meta/version deleted file mode 100644 index bc80560..0000000 --- a/tests/remove-comments/.meta/version +++ /dev/null @@ -1 +0,0 @@ -1.5.0 diff --git a/tests/remove-comments/build.gradle b/tests/remove-comments/build.gradle deleted file mode 100644 index 8bd005d..0000000 --- a/tests/remove-comments/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -apply plugin: "java" -apply plugin: "eclipse" -apply plugin: "idea" - -// set default encoding to UTF-8 -compileJava.options.encoding = "UTF-8" -compileTestJava.options.encoding = "UTF-8" - -repositories { - mavenCentral() -} - -dependencies { - testImplementation "junit:junit:4.13" - testImplementation "org.assertj:assertj-core:3.15.0" -} - -test { - testLogging { - exceptionFormat = 'full' - showStandardStreams = true - events = ["passed", "failed", "skipped"] - } -} diff --git a/tests/remove-comments/expected_mapping.json b/tests/remove-comments/expected_mapping.json deleted file mode 100644 index 846d45f..0000000 --- a/tests/remove-comments/expected_mapping.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "candidates": "PLACEHOLDER_5", - "match": "PLACEHOLDER_4", - "Anagram": "PLACEHOLDER_1", - "String": "PLACEHOLDER_3", - "word": "PLACEHOLDER_2" -} \ No newline at end of file diff --git a/tests/remove-comments/expected_representation.json b/tests/remove-comments/expected_representation.json deleted file mode 100644 index 915d838..0000000 --- a/tests/remove-comments/expected_representation.json +++ /dev/null @@ -1 +0,0 @@ -{"version": 1} \ No newline at end of file diff --git a/tests/remove-comments/expected_representation.txt b/tests/remove-comments/expected_representation.txt deleted file mode 100644 index 8801585..0000000 --- a/tests/remove-comments/expected_representation.txt +++ /dev/null @@ -1,10 +0,0 @@ -class PLACEHOLDER_1 { - - public PLACEHOLDER_1(String PLACEHOLDER_2) { - throw new UnsupportedOperationException("Please implement the Anagram(String word) constructor"); - } - - public List PLACEHOLDER_4(List PLACEHOLDER_5) { - throw new UnsupportedOperationException("Please implement the match() method"); - } -} diff --git a/tests/remove-comments/src/test/java/AnagramTest.java b/tests/remove-comments/src/test/java/AnagramTest.java deleted file mode 100644 index 9464252..0000000 --- a/tests/remove-comments/src/test/java/AnagramTest.java +++ /dev/null @@ -1,170 +0,0 @@ -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Ignore; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; - -public class AnagramTest { - - @Test - public void testNoMatches() { - Anagram detector = new Anagram("diaper"); - - assertThat( - detector.match( - Arrays.asList("hello", "world", "zombies", "pants"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testDetectsTwoAnagrams() { - Anagram detector = new Anagram("solemn"); - - assertThat(detector.match(Arrays.asList("lemons", "cherry", "melons"))) - .containsExactlyInAnyOrder("lemons", "melons"); - } - - @Ignore("Remove to run test") - @Test - public void testEliminateAnagramSubsets() { - Anagram detector = new Anagram("good"); - - assertThat(detector.match(Arrays.asList("dog", "goody"))).isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testDetectLongerAnagram() { - Anagram detector = new Anagram("listen"); - - assertThat( - detector.match( - Arrays.asList("enlists", "google", "inlets", "banana"))) - .containsExactlyInAnyOrder("inlets"); - } - - @Ignore("Remove to run test") - @Test - public void testDetectMultipleAnagramsForLongerWord() { - Anagram detector = new Anagram("allergy"); - assertThat( - detector.match( - Arrays.asList( - "gallery", - "ballerina", - "regally", - "clergy", - "largely", - "leading"))) - .containsExactlyInAnyOrder("gallery", "regally", "largely"); - } - - @Ignore("Remove to run test") - @Test - public void testDetectsMultipleAnagramsWithDifferentCase() { - Anagram detector = new Anagram("nose"); - - assertThat(detector.match(Arrays.asList("Eons", "ONES"))) - .containsExactlyInAnyOrder("Eons", "ONES"); - } - - @Ignore("Remove to run test") - @Test - public void testEliminateAnagramsWithSameChecksum() { - Anagram detector = new Anagram("mass"); - - assertThat(detector.match(Collections.singletonList("last"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testCaseInsensitiveWhenBothAnagramAndSubjectStartWithUpperCaseLetter() { - Anagram detector = new Anagram("Orchestra"); - - assertThat( - detector.match( - Arrays.asList("cashregister", "Carthorse", "radishes"))) - .containsExactlyInAnyOrder("Carthorse"); - } - - @Ignore("Remove to run test") - @Test - public void testCaseInsensitiveWhenSubjectStartsWithUpperCaseLetter() { - Anagram detector = new Anagram("Orchestra"); - - assertThat( - detector.match( - Arrays.asList("cashregister", "carthorse", "radishes"))) - .containsExactlyInAnyOrder("carthorse"); - } - - @Ignore("Remove to run test") - @Test - public void testCaseInsensitiveWhenAnagramStartsWithUpperCaseLetter() { - Anagram detector = new Anagram("orchestra"); - - assertThat( - detector.match( - Arrays.asList("cashregister", "Carthorse", "radishes"))) - .containsExactlyInAnyOrder("Carthorse"); - } - - @Ignore("Remove to run test") - @Test - public void testIdenticalWordRepeatedIsNotAnagram() { - Anagram detector = new Anagram("go"); - - assertThat(detector.match(Collections.singletonList("goGoGO"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testAnagramMustUseAllLettersExactlyOnce() { - Anagram detector = new Anagram("tapper"); - - assertThat(detector.match(Collections.singletonList("patter"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testWordsAreNotAnagramsOfThemselvesCaseInsensitive() { - Anagram detector = new Anagram("BANANA"); - - assertThat(detector.match(Collections.singletonList("BANANA"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testWordsAreNotAnagramsOfThemselvesEvenIfLetterCaseIsPartiallyDifferent() { - Anagram detector = new Anagram("BANANA"); - - assertThat(detector.match(Collections.singletonList("Banana"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testWordsAreNotAnagramsOfThemselvesEvenIfLetterCaseIsCompletelyDifferent() { - Anagram detector = new Anagram("BANANA"); - - assertThat(detector.match(Collections.singletonList("banana"))) - .isEmpty(); - } - - @Ignore("Remove to run test") - @Test - public void testWordsOtherThanThemselvesCanBeAnagrams() { - Anagram detector = new Anagram("LISTEN"); - - assertThat(detector.match(Arrays.asList("LISTEN", "Silent"))) - .containsExactlyInAnyOrder("Silent"); - } - -} diff --git a/tests/normalize-whitespace/.meta/config.json b/tests/single-solution-file/.meta/config.json similarity index 100% rename from tests/normalize-whitespace/.meta/config.json rename to tests/single-solution-file/.meta/config.json diff --git a/tests/normalize-whitespace/build.gradle b/tests/single-solution-file/build.gradle similarity index 100% rename from tests/normalize-whitespace/build.gradle rename to tests/single-solution-file/build.gradle diff --git a/tests/single-solution-file/expected_mapping.json b/tests/single-solution-file/expected_mapping.json new file mode 100644 index 0000000..b8dd450 --- /dev/null +++ b/tests/single-solution-file/expected_mapping.json @@ -0,0 +1,6 @@ +{ + "PLACEHOLDER_01": "Anagram", + "PLACEHOLDER_02": "match", + "PLACEHOLDER_03": "word", + "PLACEHOLDER_04": "candidates" +} \ No newline at end of file diff --git a/tests/single-solution-file/expected_representation.json b/tests/single-solution-file/expected_representation.json new file mode 100644 index 0000000..12dc0a6 --- /dev/null +++ b/tests/single-solution-file/expected_representation.json @@ -0,0 +1,3 @@ +{ + "version": 2 +} \ No newline at end of file diff --git a/tests/single-solution-file/expected_representation.txt b/tests/single-solution-file/expected_representation.txt new file mode 100644 index 0000000..e3813d1 --- /dev/null +++ b/tests/single-solution-file/expected_representation.txt @@ -0,0 +1,10 @@ +class PLACEHOLDER_01 { + public PLACEHOLDER_01(String PLACEHOLDER_03) { + throw new UnsupportedOperationException( + "Please implement the Anagram(String word) constructor"); + } + + public List PLACEHOLDER_02(List PLACEHOLDER_04) { + throw new UnsupportedOperationException("Please implement the match() method"); + } +} diff --git a/tests/remove-comments/src/main/java/Anagram.java b/tests/single-solution-file/src/main/java/Anagram.java similarity index 83% rename from tests/remove-comments/src/main/java/Anagram.java rename to tests/single-solution-file/src/main/java/Anagram.java index d8a593d..fd3229d 100644 --- a/tests/remove-comments/src/main/java/Anagram.java +++ b/tests/single-solution-file/src/main/java/Anagram.java @@ -1,8 +1,9 @@ import java.util.List; -class Anagram { +class Anagram { // This is cool - public Anagram(String word) { + public Anagram(String word) + { throw new UnsupportedOperationException("Please implement the Anagram(String word) constructor"); } @@ -12,6 +13,8 @@ public Anagram(String word) { too */ public List match(List candidates) { throw new UnsupportedOperationException("Please implement the match() method"); // Line comment + + ; } } \ No newline at end of file