From 3cecd0f5dc9c7c5ae6e0009eb1be4a8c577a2bbe Mon Sep 17 00:00:00 2001 From: Ovsenka Date: Sat, 28 Sep 2024 14:42:58 +0300 Subject: [PATCH] refactoring --- .github/workflows/main.yml | 4 +- app/build.gradle.kts | 10 ++- app/src/main/java/hexlet/code/App.java | 15 +--- .../main/java/hexlet/code/DiffBuilder.java | 43 +++++++++ app/src/main/java/hexlet/code/Differ.java | 44 +++------- .../java/hexlet/code/FormatterFactory.java | 21 +++++ .../java/hexlet/code/ObjectMapperFactory.java | 19 ---- app/src/main/java/hexlet/code/Parser.java | 10 ++- .../code/formatters/FormatterFactory.java | 14 --- .../hexlet/code/formatters/IFormatter.java | 2 +- .../hexlet/code/formatters/JsonFormatter.java | 25 ++---- .../code/formatters/PlainFormatter.java | 29 +++--- .../code/formatters/StylishFormatter.java | 27 +++--- app/src/test/java/DifferTests.java | 88 +++++++++---------- app/src/test/resources/fixtures/jsonFixture | 1 + app/src/test/resources/fixtures/plainFixture | 14 +++ .../test/resources/fixtures/stylishFixture | 25 ++++++ 17 files changed, 219 insertions(+), 172 deletions(-) create mode 100644 app/src/main/java/hexlet/code/DiffBuilder.java create mode 100644 app/src/main/java/hexlet/code/FormatterFactory.java delete mode 100644 app/src/main/java/hexlet/code/ObjectMapperFactory.java delete mode 100644 app/src/main/java/hexlet/code/formatters/FormatterFactory.java create mode 100644 app/src/test/resources/fixtures/jsonFixture create mode 100644 app/src/test/resources/fixtures/plainFixture create mode 100644 app/src/test/resources/fixtures/stylishFixture diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8e4c591..89aaffe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,10 +24,10 @@ jobs: - run: make test working-directory: ./app - name: Publish code coverage - uses: paambaati/codeclimate-action@v9.0.0 + uses: paambaati/codeclimate-action@v3.1.1 env: CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}} - JACOCO_SOURCE_PATH: app/src/main/java ./app/cc-test-reporter format-coverage ./app/build/reports/jacoco/test/jacocoTestReport.xml --input-type jacoco + JACOCO_SOURCE_PATH: ${{github.workspace}}app/src/main/java with: coverageCommand: make -C app report coverageLocations: ${{github.workspace}}app/build/reports/jacoco/test/jacocoTestReport.xml:jacoco diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e94007a..1c509d8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,13 +15,15 @@ repositories { dependencies { testImplementation(platform("org.junit:junit-bom:5.10.2")) - testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") + testImplementation("org.junit.jupiter:junit-jupiter:5.10.3") implementation("info.picocli:picocli:4.7.6") // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind implementation("com.fasterxml.jackson.core:jackson-databind:2.17.1") implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.0-rc1") } + + tasks.withType { useJUnitPlatform() } @@ -30,6 +32,12 @@ tasks.test { useJUnitPlatform() } +tasks.jacocoTestReport { + reports { + xml.required = true + } +} + application { mainClass = "hexlet.code.App" } diff --git a/app/src/main/java/hexlet/code/App.java b/app/src/main/java/hexlet/code/App.java index 62ac636..4464c8e 100644 --- a/app/src/main/java/hexlet/code/App.java +++ b/app/src/main/java/hexlet/code/App.java @@ -1,20 +1,16 @@ package hexlet.code; -import hexlet.code.formatters.FormatterFactory; -import hexlet.code.formatters.IFormatter; import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -import java.nio.file.Paths; -import java.util.Map; import java.util.concurrent.Callable; @Command(name = "gendiff", mixinStandardHelpOptions = true, description = "Compares two configuration files and shows a difference.", - version = "gendiff 0.2" + version = "gendiff 1.0" ) public class App implements Callable { @@ -22,7 +18,7 @@ public class App implements Callable { defaultValue = "stylish", paramLabel = "format", description = "output format [default: stylish]") - private String format; + String format; @Parameters(paramLabel = "filepath1", description = "path to first file") String filepath; @@ -32,12 +28,7 @@ public class App implements Callable { @Override public Integer call() throws Exception { - Map diffResult = Differ.generate( - Paths.get(filepath).toAbsolutePath().normalize(), - Paths.get(filepath2).toAbsolutePath().normalize() - ); - IFormatter formatter = FormatterFactory.getFormatter(format); - System.out.println(formatter.format(diffResult)); + System.out.print(Differ.generate(filepath, filepath2, format)); return 0; } public static void main(String[] args) { diff --git a/app/src/main/java/hexlet/code/DiffBuilder.java b/app/src/main/java/hexlet/code/DiffBuilder.java new file mode 100644 index 0000000..8926c6c --- /dev/null +++ b/app/src/main/java/hexlet/code/DiffBuilder.java @@ -0,0 +1,43 @@ +package hexlet.code; + +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.LinkedHashMap; +import java.util.ArrayList; + +public class DiffBuilder { + public static Map> build(Map mapFile, Map mapFile2) { + SortedSet keys = new TreeSet<>(mapFile.keySet()); + keys.addAll(mapFile2.keySet()); + Map> diffResult = new LinkedHashMap<>(); + LinkedHashMap deleteMap = new LinkedHashMap<>(); + LinkedHashMap notChangedMap = new LinkedHashMap<>(); + LinkedHashMap changedMap = new LinkedHashMap<>(); + LinkedHashMap addedMap = new LinkedHashMap<>(); + for (String key : keys) { + Object value = mapFile.get(key) == null ? "null" : mapFile.get(key); + if (mapFile2.containsKey(key)) { + Object differFileKeyValue = mapFile2.get(key) == null ? "null" : mapFile2.get(key); + if (differFileKeyValue.equals(value)) { + notChangedMap.put(key, value); + } else if (!mapFile.containsKey(key)) { + addedMap.put(key, differFileKeyValue); + } else { + ArrayList values = new ArrayList<>(); + values.add(value); + values.add(differFileKeyValue); + changedMap.put(key, values); + } + mapFile2.remove(key); + } else { + deleteMap.put(key, value); + } + } + diffResult.put("notchanged", notChangedMap); + diffResult.put("add", addedMap); + diffResult.put("changed", changedMap); + diffResult.put("delete", deleteMap); + return diffResult; + } +} diff --git a/app/src/main/java/hexlet/code/Differ.java b/app/src/main/java/hexlet/code/Differ.java index 79cb269..fbb233f 100644 --- a/app/src/main/java/hexlet/code/Differ.java +++ b/app/src/main/java/hexlet/code/Differ.java @@ -1,45 +1,21 @@ package hexlet.code; +import hexlet.code.formatters.IFormatter; + import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Map; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.LinkedHashMap; - public class Differ { - public static Map generate(Path filepath, Path filepath2) { + public static String generate(String pathToFile, String pathToFile2, String format) throws Exception { + Path filepath = Paths.get(pathToFile).toAbsolutePath().normalize(); + Path filepath2 = Paths.get(pathToFile2).toAbsolutePath().normalize(); + Map mapFile = Parser.parse(filepath); Map mapFile2 = Parser.parse(filepath2); - return compareFiles(mapFile, mapFile2); - } - private static Map compareFiles(Map mapFile, Map mapFile2) { - SortedSet keys = new TreeSet<>(mapFile.keySet()); - keys.addAll(mapFile2.keySet()); - Map diffResult = new LinkedHashMap<>(); - for (String key : keys) { - Object value = mapFile.get(key) == null ? "null" : mapFile.get(key); - if (mapFile2.containsKey(key)) { - Object differFileKeyValue = mapFile2.get(key) == null ? "null" : mapFile2.get(key); - if (differFileKeyValue.equals(value)) { - diffResult.put(key, new Object[] {"none", value}); - } else if (!mapFile.containsKey(key)) { - diffResult.put(key, new Object[] {"+", differFileKeyValue }); - } else { - diffResult.put(key, new Object[] {"-+", value, differFileKeyValue }); - } - mapFile2.remove(key); - } else { - diffResult.put(key, new Object[] {"-", value}); - } - } - mapFile2.forEach((k, v) -> { - if (!mapFile.containsKey(k)) { - diffResult.put(k, new Object[] {"+", v}); - } - }); - return diffResult; + IFormatter formatter = null; + formatter = FormatterFactory.getFormatter(format); + return formatter.format(DiffBuilder.build(mapFile, mapFile2)); } - } diff --git a/app/src/main/java/hexlet/code/FormatterFactory.java b/app/src/main/java/hexlet/code/FormatterFactory.java new file mode 100644 index 0000000..0368050 --- /dev/null +++ b/app/src/main/java/hexlet/code/FormatterFactory.java @@ -0,0 +1,21 @@ +package hexlet.code; + +import hexlet.code.formatters.IFormatter; +import hexlet.code.formatters.JsonFormatter; +import hexlet.code.formatters.PlainFormatter; +import hexlet.code.formatters.StylishFormatter; + +public class FormatterFactory { + + public static IFormatter getFormatter(String format) throws Exception { + return switch (format) { + case "stylish" -> new StylishFormatter(); + case "plain" -> new PlainFormatter(); + case "json" -> new JsonFormatter(); + default -> throw new Exception("Unexpected format '%s'. Possible formats: [stylish, plain, json]" + .formatted(format)); + }; + } + + +} diff --git a/app/src/main/java/hexlet/code/ObjectMapperFactory.java b/app/src/main/java/hexlet/code/ObjectMapperFactory.java deleted file mode 100644 index 731c781..0000000 --- a/app/src/main/java/hexlet/code/ObjectMapperFactory.java +++ /dev/null @@ -1,19 +0,0 @@ -package hexlet.code; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; - -import java.nio.file.Path; - -public class ObjectMapperFactory { - public static ObjectMapper getObjectMapper(Path filepath) { - ObjectMapper mapper = null; - if (filepath.toString().endsWith(".yml")) { - mapper = new YAMLMapper(); - } else if (filepath.toString().endsWith(".json")) { - mapper = new JsonMapper(); - } - return mapper; - } -} diff --git a/app/src/main/java/hexlet/code/Parser.java b/app/src/main/java/hexlet/code/Parser.java index c1452f0..246e3cf 100644 --- a/app/src/main/java/hexlet/code/Parser.java +++ b/app/src/main/java/hexlet/code/Parser.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import java.io.File; import java.io.IOException; @@ -11,8 +13,12 @@ public class Parser { public static Map parse(Path file) { Map mapFile = null; - ObjectMapperFactory factory = new ObjectMapperFactory(); - ObjectMapper mapper = factory.getObjectMapper(file); + ObjectMapper mapper = null; + if (file.toString().endsWith(".yml")) { + mapper = new YAMLMapper(); + } else if (file.toString().endsWith(".json")) { + mapper = new JsonMapper(); + } try { mapFile = mapper.readValue(new File(file.toString()), new TypeReference<>() { }); diff --git a/app/src/main/java/hexlet/code/formatters/FormatterFactory.java b/app/src/main/java/hexlet/code/formatters/FormatterFactory.java deleted file mode 100644 index 5bbd6e8..0000000 --- a/app/src/main/java/hexlet/code/formatters/FormatterFactory.java +++ /dev/null @@ -1,14 +0,0 @@ -package hexlet.code.formatters; - -public class FormatterFactory { - - public static IFormatter getFormatter(String formatter) { - return switch (formatter) { - case "plain" -> new PlainFormatter(); - case "json" -> new JsonFormatter(); - default -> new StylishFormatter(); - }; - } - - -} diff --git a/app/src/main/java/hexlet/code/formatters/IFormatter.java b/app/src/main/java/hexlet/code/formatters/IFormatter.java index b07cc99..1a692ec 100644 --- a/app/src/main/java/hexlet/code/formatters/IFormatter.java +++ b/app/src/main/java/hexlet/code/formatters/IFormatter.java @@ -3,5 +3,5 @@ import java.util.Map; public interface IFormatter { - String format(Map differMap); + String format(Map> differMap); } diff --git a/app/src/main/java/hexlet/code/formatters/JsonFormatter.java b/app/src/main/java/hexlet/code/formatters/JsonFormatter.java index 0244f8c..826883e 100644 --- a/app/src/main/java/hexlet/code/formatters/JsonFormatter.java +++ b/app/src/main/java/hexlet/code/formatters/JsonFormatter.java @@ -2,28 +2,19 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.HashMap; +import com.fasterxml.jackson.databind.json.JsonMapper; + import java.util.Map; +import java.util.TreeMap; public class JsonFormatter implements IFormatter { @Override - public String format(Map differMap) { - ObjectMapper mapper = new ObjectMapper(); - HashMap map = new HashMap<>(); - differMap.forEach((k, v) -> { - switch (v[0].toString()) { - case "+", "-": - map.put(k, v[1]); - break; - case "-+": - map.put(k, v[2]); - break; - default: - break; - } - }); + public String format(Map> differMap) { + TreeMap properties = new TreeMap<>(); + differMap.forEach((k, v) -> properties.putAll(v)); + ObjectMapper mapper = new JsonMapper(); try { - return mapper.writeValueAsString(map); + return mapper.writeValueAsString(properties); } catch (JsonProcessingException e) { System.out.println(e.getMessage()); } diff --git a/app/src/main/java/hexlet/code/formatters/PlainFormatter.java b/app/src/main/java/hexlet/code/formatters/PlainFormatter.java index 9329c76..b5aea88 100644 --- a/app/src/main/java/hexlet/code/formatters/PlainFormatter.java +++ b/app/src/main/java/hexlet/code/formatters/PlainFormatter.java @@ -1,24 +1,31 @@ package hexlet.code.formatters; +import java.util.ArrayList; import java.util.Map; public class PlainFormatter implements IFormatter { @Override - public String format(Map differMap) { + public String format(Map> differMap) { StringBuilder sb = new StringBuilder(); differMap.forEach((k, v) -> { - Object keyvalue = v[1].getClass().isArray() ? "[complex value]" : v[1]; - Object keyvalue2; - switch (v[0].toString()) { - case "+": - sb.append("\nProperty '%s' was added with value: %s".formatted(k, keyvalue)); + switch (k) { + case "add": + v.forEach((key, value) -> + sb.append("\nProperty '%s' was added with value: %s" + .formatted(key, value))); break; - case "-": - sb.append("\nProperty '%s' was removed".formatted(k)); + case "delete": + v.forEach((key, value) -> + sb.append("\nProperty '%s' was removed" + .formatted(key))); break; - case "-+": - keyvalue2 = v[2].getClass().isArray() ? "[complex value]" : v[2]; - sb.append("\nProperty '%s' was updated. From %s to %s".formatted(k, keyvalue, keyvalue2)); + case "changed": + v.forEach((key, value) -> { + Object resultValue = ((ArrayList) value).get(0); + Object resultValue2 = ((ArrayList) value).get(1); + sb.append("\nProperty '%s' was updated. From %s to %s" + .formatted(key, resultValue, resultValue2)); + }); break; default: break; diff --git a/app/src/main/java/hexlet/code/formatters/StylishFormatter.java b/app/src/main/java/hexlet/code/formatters/StylishFormatter.java index 3bf8e8a..ea70ca7 100644 --- a/app/src/main/java/hexlet/code/formatters/StylishFormatter.java +++ b/app/src/main/java/hexlet/code/formatters/StylishFormatter.java @@ -1,25 +1,30 @@ package hexlet.code.formatters; +import java.util.ArrayList; import java.util.Map; public class StylishFormatter implements IFormatter { @Override - public String format(Map differMap) { + public String format(Map> differMap) { StringBuilder result = new StringBuilder("{"); differMap.forEach((k, v) -> { - switch (v[0].toString()) { - case "none": - result.append("\n\t").append(k).append(": ").append(v[1]); + switch (k) { + case "notchanged": + v.forEach((key, value) -> result.append("\n ").append(key).append(": ").append(value)); break; - case "+": - result.append("\n\t+ ").append(k).append(": ").append(v[1]); + case "add": + v.forEach((key, value) -> result.append("\n + ").append(key).append(": ").append(v.get(key))); break; - case "-": - result.append("\n\t- ").append(k).append(": ").append(v[1]); + case "delete": + v.forEach((key, value) -> result.append("\n - ").append(key).append(": ").append(v.get(key))); break; - case "-+": - result.append("\n\t- ").append(k).append(": ").append(v[1]); - result.append("\n\t+ ").append(k).append(": ").append(v[2]); + case "changed": + v.forEach((key, value) -> { + Object resultValue = ((ArrayList) value).get(0); + Object resultValue2 = ((ArrayList) value).get(1); + result.append("\n - ").append(key).append(": ").append(resultValue); + result.append("\n + ").append(key).append(": ").append(resultValue2); + }); break; default: break; diff --git a/app/src/test/java/DifferTests.java b/app/src/test/java/DifferTests.java index bc4202d..1ff6705 100644 --- a/app/src/test/java/DifferTests.java +++ b/app/src/test/java/DifferTests.java @@ -1,68 +1,60 @@ -import static hexlet.code.Differ.generate; import static org.junit.jupiter.api.Assertions.assertEquals; -import hexlet.code.formatters.StylishFormatter; -import org.junit.jupiter.api.BeforeAll; +import hexlet.code.Differ; import org.junit.jupiter.api.Test; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Map; public class DifferTests { - private static String expectedDifferents = ""; - private static StylishFormatter stylishFormatter = null; - @BeforeAll - public static void init() { - expectedDifferents = String.join("\n\t", - "{", - "chars1: [a, b, c]", - "- chars2: [d, e, f]", - "+ chars2: false", - "- checked: false", - "+ checked: true", - "- default: null", - "+ default: [value1, value2]", - "- id: 45", - "+ id: null", - "- key1: value1", - "+ key2: value2", - "numbers1: [1, 2, 3, 4]", - "- numbers2: [2, 3, 4, 5]", - "+ numbers2: [22, 33, 44, 55]", - "- numbers3: [3, 4, 5]", - "+ numbers4: [4, 5, 6]", - "+ obj1: {nestedKey=value, isNested=true}", - "- setting1: Some value", - "+ setting1: Another value", - "- setting2: 200", - "+ setting2: 300", - "- setting3: true", - "+ setting3: none\n}"); - stylishFormatter = new StylishFormatter(); - } + private static final String YAML_PATH = "src/test/resources/test1.yml"; + private static final String YAML_PATH2 = "src/test/resources/test2.yml"; + private static final String JSON_PATH = "src/test/resources/test1.json"; + private static final String JSON_PATH2 = "src/test/resources/test2.json"; @Test public void jsonDiffTestStylish() { - Map diff = generate( - Paths.get("src/test/resources/test1.json").toAbsolutePath().normalize(), - Paths.get("src/test/resources/test2.json").toAbsolutePath().normalize() - ); - assertEquals(stylishFormatter.format(diff), - expectedDifferents - ); } @Test - public void yamlDiffTestStylish() { - Map diff = generate( - Paths.get("src/test/resources/test1.yml").toAbsolutePath().normalize(), - Paths.get("src/test/resources/test2.yml").toAbsolutePath().normalize() + public void jsonDiffTest() throws Exception { + assertEquals( + readFixture("stylishFixture"), + Differ.generate(JSON_PATH, JSON_PATH2, "stylish").trim() ); + assertEquals( + readFixture("jsonFixture"), + Differ.generate(JSON_PATH, JSON_PATH2, "json").trim() + ); + assertEquals( + readFixture("plainFixture"), + Differ.generate(JSON_PATH, JSON_PATH2, "plain").trim() + ); + } - assertEquals(stylishFormatter.format(diff), - expectedDifferents + @Test + public void yamlDiffTest() throws Exception { + assertEquals( + readFixture("stylishFixture"), + Differ.generate(YAML_PATH, YAML_PATH2, "stylish").trim() + ); + assertEquals( + readFixture("jsonFixture"), + Differ.generate(YAML_PATH, YAML_PATH2, "json").trim() + ); + assertEquals( + readFixture("plainFixture"), + Differ.generate(YAML_PATH, YAML_PATH2, "plain").trim() ); } + + private static String readFixture(String fileName) throws Exception { + Path filePath = Paths.get("src", "test", "resources", "fixtures", fileName) + .toAbsolutePath().normalize(); + return Files.readString(filePath).trim(); + } } + diff --git a/app/src/test/resources/fixtures/jsonFixture b/app/src/test/resources/fixtures/jsonFixture new file mode 100644 index 0000000..4e2c1a1 --- /dev/null +++ b/app/src/test/resources/fixtures/jsonFixture @@ -0,0 +1 @@ +{"chars1":["a","b","c"],"chars2":[["d","e","f"],false],"checked":[false,true],"default":["null",["value1","value2"]],"id":[45,"null"],"key1":"value1","key2":"value2","numbers1":[1,2,3,4],"numbers2":[[2,3,4,5],[22,33,44,55]],"numbers3":[3,4,5],"numbers4":[4,5,6],"obj1":{"nestedKey":"value","isNested":true},"setting1":["Some value","Another value"],"setting2":[200,300],"setting3":[true,"none"]} \ No newline at end of file diff --git a/app/src/test/resources/fixtures/plainFixture b/app/src/test/resources/fixtures/plainFixture new file mode 100644 index 0000000..6c5ad6f --- /dev/null +++ b/app/src/test/resources/fixtures/plainFixture @@ -0,0 +1,14 @@ + +Property 'key2' was added with value: value2 +Property 'numbers4' was added with value: [4, 5, 6] +Property 'obj1' was added with value: {nestedKey=value, isNested=true} +Property 'chars2' was updated. From [d, e, f] to false +Property 'checked' was updated. From false to true +Property 'default' was updated. From null to [value1, value2] +Property 'id' was updated. From 45 to null +Property 'numbers2' was updated. From [2, 3, 4, 5] to [22, 33, 44, 55] +Property 'setting1' was updated. From Some value to Another value +Property 'setting2' was updated. From 200 to 300 +Property 'setting3' was updated. From true to none +Property 'key1' was removed +Property 'numbers3' was removed \ No newline at end of file diff --git a/app/src/test/resources/fixtures/stylishFixture b/app/src/test/resources/fixtures/stylishFixture new file mode 100644 index 0000000..0b1b33c --- /dev/null +++ b/app/src/test/resources/fixtures/stylishFixture @@ -0,0 +1,25 @@ +{ + chars1: [a, b, c] + numbers1: [1, 2, 3, 4] + + key2: value2 + + numbers4: [4, 5, 6] + + obj1: {nestedKey=value, isNested=true} + - chars2: [d, e, f] + + chars2: false + - checked: false + + checked: true + - default: null + + default: [value1, value2] + - id: 45 + + id: null + - numbers2: [2, 3, 4, 5] + + numbers2: [22, 33, 44, 55] + - setting1: Some value + + setting1: Another value + - setting2: 200 + + setting2: 300 + - setting3: true + + setting3: none + - key1: value1 + - numbers3: [3, 4, 5] +} \ No newline at end of file