Skip to content

Commit

Permalink
add comparison of nested files
Browse files Browse the repository at this point in the history
  • Loading branch information
SaliAbdullaeva committed Sep 6, 2024
1 parent f6b2231 commit adb6775
Show file tree
Hide file tree
Showing 23 changed files with 432 additions and 176 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
[![Maintainability](https://api.codeclimate.com/v1/badges/441c3660842a050f496d/maintainability)](https://codeclimate.com/github/SaliAbdullaeva/java-project-71/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/441c3660842a050f496d/test_coverage)](https://codeclimate.com/github/SaliAbdullaeva/java-project-71/test_coverage)
[![asciicast](https://asciinema.org/a/673710.svg)](https://asciinema.org/a/673710)
[![asciicast](https://asciinema.org/a/674626.svg)](https://asciinema.org/a/674626)
[![asciicast](https://asciinema.org/a/674626.svg)](https://asciinema.org/a/674626)
[![asciicast](https://asciinema.org/a/675009.svg)](https://asciinema.org/a/675009)
34 changes: 20 additions & 14 deletions app/src/main/java/hexlet/code/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,39 @@

@Command(name = "gendiff",
mixinStandardHelpOptions = true,
description = "Calculates the differences between files.")
description = "Compares two configuration files and shows a difference.")
public final class App implements Callable<Integer> {
private static final int SUCCESS_EXIT_CODE = 0;
private static final int ERROR_EXIT_CODE = 15;

public class App implements Callable<Integer> {

@Option(names = {"-f", "--format"}, paramLabel = "format", description = "output format [default: stylish]")
String format = "stylish";
@Parameters(index = "0", paramLabel = "filepath1", description = "Path to first file")
String filepath1;
private String filePath1;

@Parameters(index = "1", paramLabel = "filepath2", description = "Path to second file")
String filepath2;
private String filePath2;

@Option(names = {"-f", "--format"},
paramLabel = "format",
description = "output format [default: stylish]",
defaultValue = "stylish")
private String format;

@Override
public Integer call() {
try {
String result = Differ.generate(filepath1, filepath2, format);
String result = Differ.generate(filePath1, filePath2, format);
System.out.println(result);
return 0; // Успешное завершение
} catch (IOException e) {
System.err.println("Error processing files: " + e.getMessage());
return 2; // Код ошибки
} catch (Exception e) {
System.err.println("Unexpected error: " + e.getMessage());
return 1; // Код ошибки
System.out.println(e.getMessage());
return ERROR_EXIT_CODE;
}

return SUCCESS_EXIT_CODE;
}

public static void main(String[] args) {
int exitCode = new CommandLine(new App()).execute(args);
System.exit(exitCode);
}

}
111 changes: 20 additions & 91 deletions app/src/main/java/hexlet/code/Differ.java
Original file line number Diff line number Diff line change
@@ -1,103 +1,32 @@
package hexlet.code;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public class Differ {
public static String generate(String filePath1, String filePath2, String format) throws Exception {
// Чтение содержимого файлов
String fileContent1 = ReadFile.readFile(Path.of(filePath1));
String fileContent2 = ReadFile.readFile(Path.of(filePath2));
import java.util.Map;
import java.util.List;

// Получение расширения файлов для парсинга
String extension1 = ReadFile.getFileExtension(Path.of(filePath1));
String extension2 = ReadFile.getFileExtension(Path.of(filePath2));
public final class Differ {

// Парсинг содержимого файлов в Map
Map<String, Object> map1 = Parser.parseString(fileContent1, extension1);
Map<String, Object> map2 = Parser.parseString(fileContent2, extension2);
public static String generate(String filePath1, String filePath2, String format) throws IOException {
Path path1 = Reader.getPath(filePath1);
Path path2 = Reader.getPath(filePath2);

// Создание отсортированного набора ключей
Set<String> allKeys = new TreeSet<>();
allKeys.addAll(map1.keySet());
allKeys.addAll(map2.keySet());
Map<String, Object> data1 = Parser.parseString(
Reader.readFile(path1),
Reader.getFileExtension(path1)
);
Map<String, Object> data2 = Parser.parseString(
Reader.readFile(path2),
Reader.getFileExtension(path2)
);

// Формирование результата в зависимости от формата
return switch (format) {
case "plain" -> formatPlain(allKeys, map1, map2);
case "json" -> formatJson(allKeys, map1, map2);
default -> formatStylish(allKeys, map1, map2); // формат по умолчанию
};
List<Map<String, Object>> differDifferNodeList = DifferListComposer.composeList(data1, data2);
return Formatter.formatString(differDifferNodeList, format);
}

private static String formatStylish(Set<String> allKeys, Map<String, Object> map1, Map<String, Object> map2) {
StringBuilder result = new StringBuilder("{\n");

for (String key : allKeys) {
Object value1 = map1.get(key);
Object value2 = map2.get(key);

if (value1 == null && value2 != null) {
result.append(" + ").append(key).append(": ").append(value2).append("\n");
} else if (value1 != null && value2 == null) {
result.append(" - ").append(key).append(": ").append(value1).append("\n");
} else if (value1 != null && value2 != null && !value1.equals(value2)) {
result.append(" - ").append(key).append(": ").append(value1).append("\n");
result.append(" + ").append(key).append(": ").append(value2).append("\n");
} else if (value1 != null && value2 != null && value1.equals(value2)) {
result.append(" ").append(key).append(": ").append(value1).append("\n");
}
}

result.append("}");
return result.toString();
public static String generate(String filePath1, String filePath2) throws IOException {
return generate(filePath1, filePath2, "stylish");
}

private static String formatPlain(Set<String> allKeys, Map<String, Object> map1, Map<String, Object> map2) {
StringBuilder result = new StringBuilder();

for (String key : allKeys) {
Object value1 = map1.get(key);
Object value2 = map2.get(key);

if (value1 == null) {
result.append("Property '").append(key).append("' was added with value: ").append(value2).append("\n");
} else if (value2 == null) {
result.append("Property '").append(key).append("' was removed").append("\n");
} else if (!value1.equals(value2)) {
result.append("Property '").append(key).append("' was updated. From ").append(value1).append(" to ").append(value2).append("\n");
}
}

return result.toString().trim();
}

private static String formatJson(Set<String> allKeys, Map<String, Object> map1, Map<String, Object> map2) {
StringBuilder result = new StringBuilder("{");

for (String key : allKeys) {
Object value1 = map1.get(key);
Object value2 = map2.get(key);

if (value1 == null) {
result.append("\"").append(key).append("\": ").append(value2).append(", ");
} else if (value2 == null) {
result.append("\"").append(key).append("\": ").append(value1).append(", ");
} else if (!value1.equals(value2)) {
result.append("\"").append(key).append("\": ").append(value2).append(", ");
} else {
result.append("\"").append(key).append("\": ").append(value1).append(", ");
}
}

// Удаляем последнюю запятую и пробел, если они есть
if (result.length() > 1) {
result.setLength(result.length() - 2);
}

result.append("}");
return result.toString();
}
}
}
54 changes: 54 additions & 0 deletions app/src/main/java/hexlet/code/DifferListComposer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package hexlet.code;

import java.util.Optional;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.TreeSet;

public final class DifferListComposer {

public static List<Map<String, Object>> composeList(
Map<String, Object> data1,
Map<String, Object> data2
) {
List<Map<String, Object>> differNodeList = new ArrayList<>();

Set<String> keys = new TreeSet<>(data1.keySet());
keys.addAll(data2.keySet());
for (String key : keys) {
differNodeList.add(makeDifferNode(key, data1, data2));
}

return differNodeList;
}

private static Map<String, Object> makeDifferNode(
String key,
Map<String, Object> data1,
Map<String, Object> data2
) {
Map<String, Object> differNode = new LinkedHashMap<>();
Optional<Object> value1 = Optional.ofNullable(data1.get(key));
Optional<Object> value2 = Optional.ofNullable(data2.get(key));

differNode.put("key", key);
if (!data1.containsKey(key)) {
differNode.put("condition", "ADDED");
differNode.put("value", value2.orElse(null));
} else if (!data2.containsKey(key)) {
differNode.put("condition", "DELETED");
differNode.put("value", value1.orElse(null));
} else if (value1.equals(value2)) {
differNode.put("condition", "UNCHANGED");
differNode.put("value", value1.orElse(null));
} else {
differNode.put("condition", "CHANGED");
differNode.put("value1", value1.orElse(null));
differNode.put("value2", value2.orElse(null));
}
return differNode;
}
}
29 changes: 29 additions & 0 deletions app/src/main/java/hexlet/code/Formatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package hexlet.code;

import hexlet.code.formatters.Json;
import hexlet.code.formatters.StringFormatter;
import hexlet.code.formatters.Stylish;
import hexlet.code.formatters.Plain;

import java.io.IOException;
import java.util.List;
import java.util.Map;

public final class Formatter {

public static String formatString(List<Map<String, Object>> differEntryList, String format) throws IOException {
StringFormatter formatter = getFormatter(format);
return formatter.format(differEntryList);
}

private static StringFormatter getFormatter(String format) throws IOException {
return switch (format) {
case "stylish" -> new Stylish();
case "plain" -> new Plain();
case "json" -> new Json();
default ->
throw new IOException("File should have 'stylish', 'plain' or 'json' format! (was '%s')"
.formatted(format));
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.nio.file.Paths;
import java.nio.file.Files;

class ReadFile {
class Reader {
public static String readFile(Path filePath) throws IOException {
return Files.readString(filePath);
}
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/hexlet/code/formatters/Json.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package hexlet.code.formatters;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

import java.util.List;
import java.util.Map;

public final class Json implements StringFormatter {

@Override
public String format(List<Map<String, Object>> differNodeList) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(differNodeList);
}
}
51 changes: 51 additions & 0 deletions app/src/main/java/hexlet/code/formatters/Plain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package hexlet.code.formatters;

import java.util.List;
import java.util.Map;

public final class Plain implements StringFormatter {

@Override
public String format(List<Map<String, Object>> differNodeList) {
List<Map<String, Object>> filteredList = differNodeList.stream()
.filter(node -> !node.get("condition").equals("UNCHANGED"))
.toList();

StringBuilder sb = new StringBuilder();

for (Map<String, Object> node : filteredList) {
String condition = (String) node.get("condition");
switch (condition) {
case "ADDED" -> {
String value = formatComplexValue(node.get("value"));
sb.append("Property '%s' was added with value: %s%n"
.formatted(node.get("key"), value));
}
case "DELETED" -> sb.append("Property '%s' was removed%n"
.formatted(node.get("key")));
case "CHANGED" -> {
String value1 = formatComplexValue(node.get("value1"));
String value2 = formatComplexValue(node.get("value2"));
sb.append("Property '%s' was updated. From %s to %s%n"
.formatted(node.get("key"), value1, value2));
}
default -> throw new IllegalStateException("Unexpected value: " + condition);
}
}

return sb.deleteCharAt(sb.length() - 1).toString();
}

private static String formatComplexValue(Object value) {
if (value == null) {
return null;
}
if (value instanceof List<?> || value instanceof Map<?, ?>) {
return "[complex value]";
}
if (value instanceof String) {
return String.format("'%s'", value);
}
return value.toString();
}
}
9 changes: 9 additions & 0 deletions app/src/main/java/hexlet/code/formatters/StringFormatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package hexlet.code.formatters;

import java.io.IOException;
import java.util.List;
import java.util.Map;

public interface StringFormatter {
String format(List<Map<String, Object>> differEntryList) throws IOException;
}
Loading

0 comments on commit adb6775

Please sign in to comment.