Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,190 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=valentin-osadchii_java-project-71&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=valentin-osadchii_java-project-71)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=valentin-osadchii_java-project-71&metric=coverage)](https://sonarcloud.io/summary/new_code?id=valentin-osadchii_java-project-71)

## Пример использования

### Справка

```bash
$ ./app -h

Usage: gendiff [-hV] [-f=<format>] <filepath1> <filepath2>
Compares two configuration files and shows a difference.
<filepath1> path to first file
<filepath2> path to second file
-f, --format=<format> output format [default: stylish]
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
```

### Сравнение json, вывод в формате по-умолчанию

```bash
$ ./build/install/app/bin/app examples/file1nested.json examples/file2nested.json

Reading file 1: examples/file1nested.json
Reading file 2: examples/file2nested.json
Using format: stylish

File 1 content:
{
"setting1": "Some value",
"setting2": 200,
"setting3": true,
"key1": "value1",
"numbers1": [1, 2, 3, 4],
"numbers2": [2, 3, 4, 5],
"id": 45,
"default": null,
"checked": false,
"numbers3": [3, 4, 5],
"chars1": ["a", "b", "c"],
"chars2": ["d", "e", "f"]
}
File 2 content:
{
"setting1": "Another value",
"setting2": 300,
"setting3": "none",
"key2": "value2",
"numbers1": [1, 2, 3, 4],
"numbers2": [22, 33, 44, 55],
"id": null,
"default": ["value1", "value2"],
"checked": true,
"numbers4": [4, 5, 6],
"chars1": ["a", "b", "c"],
"chars2": false,
"obj1": {
"nestedKey": "value",
"isNested": true
}
}
{
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
}

```

### Сравнение yaml, вывод в plain-формате

```bash
$ ./build/install/app/bin/app -f plain examples/file1nested.yaml examples/file2nested.yaml

Reading file 1: examples/file1nested.yaml
Reading file 2: examples/file2nested.yaml
Using format: plain

File 1 content:
setting1: Some value
setting2: 200
setting3: true
key1: value1
numbers1:
- 1
- 2
- 3
- 4
numbers2:
- 2
- 3
- 4
- 5
id: 45
default: null
checked: false
numbers3:
- 3
- 4
- 5
chars1:
- a
- b
- c
chars2:
- d
- e
- f

File 2 content:
setting1: Another value
setting2: 300
setting3: none
key2: value2
numbers1:
- 1
- 2
- 3
- 4
numbers2:
- 22
- 33
- 44
- 55
id: null
default:
- value1
- value2
checked: true
numbers4:
- 4
- 5
- 6
chars1:
- a
- b
- c
chars2: false
obj1:
nestedKey: value
isNested: true

Property 'chars2' was updated. From [complex value] to false
Property 'checked' was updated. From false to true
Property 'default' was updated. From null to [complex value]
Property 'id' was updated. From 45 to null
Property 'key1' was removed
Property 'key2' was added with value: 'value2'
Property 'numbers2' was updated. From [complex value] to [complex value]
Property 'numbers3' was removed
Property 'numbers4' was added with value: [complex value]
Property 'obj1' was added with value: [complex value]
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'
```


### Сравнение yaml, вывод в json-формате

```bash
$ ./build/install/app/bin/app -f json examples/file1nested.yaml examples/file2nested.yaml


[{"key":"chars1","status":"UNCHANGED","oldValue":["a","b","c"],"newValue":["a","b","c"]},{"key":"chars2","status":"CHANGED","oldValue":["d","e","f"],"newValue":false},{"key":"checked","status":"CHANGED","oldValue":false,"newValue":true},{"key":"default","status":"CHANGED","oldValue":null,"newValue":["value1
","value2"]},{"key":"id","status":"CHANGED","oldValue":45,"newValue":null},{"key":"key1","status":"REMOVED","oldValue":"value1","newValue":null},{"key":"key2","status":"ADDED","oldValue":null,"newValue":"value2"},{"key":"numbers1","status":"UNCHANGED","oldValue":[1,2,3,4],"newValue":[1,2,3,4]},{"key":"numbe
rs2","status":"CHANGED","oldValue":[2,3,4,5],"newValue":[22,33,44,55]},{"key":"numbers3","status":"REMOVED","oldValue":[3,4,5],"newValue":null},{"key":"numbers4","status":"ADDED","oldValue":null,"newValue":[4,5,6]},{"key":"obj1","status":"ADDED","oldValue":null,"newValue":{"nestedKey":"value","isNested":true}},{"key":"setting1","status":"CHANGED","oldValue":"Some value","newValue":"Another value"},{"key":"setting2","status":"CHANGED","oldValue":200,"newValue":300},{"key":"setting3","status":"CHANGED","oldValue":true,"newValue":"none"}]

```
13 changes: 0 additions & 13 deletions app/README.md

This file was deleted.

1 change: 0 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask

plugins {
id("java")
id("com.github.ben-manes.versions") version "0.51.0" // Актуальная версия на май 2025
id("application")
id("checkstyle")
Expand Down
96 changes: 2 additions & 94 deletions app/src/main/java/hexlet/code/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

@Command(
Expand All @@ -22,7 +14,7 @@
version = "gendiff 1.0" // Версия для опции --version
)

public class App implements Callable<Integer> {
public final class App implements Callable<Integer> {

@Parameters(index = "0", description = "path to first file")
private String filepath1;
Expand All @@ -45,98 +37,14 @@ public static void main(String[] args) {
@Override
public Integer call() throws Exception {
try {
// Добавляем проверку на одинаковое расширение файлов
validateFileExtensions();

System.out.println("Reading file 1: " + filepath1);
String file1Content = readFileContent(filepath1);
System.out.println("Reading file 2: " + filepath2);
String file2Content = readFileContent(filepath2);

System.out.println("Using format: " + format);
System.out.println("\nFile 1 content:\n" + file1Content);
System.out.println("File 2 content:\n" + file2Content);

// Парсинг происходит внутри try-блока
Map<String, Object> data1 = Parser.parseFileToMap(file1Content, filepath1);
Map<String, Object> data2 = Parser.parseFileToMap(file2Content, filepath2);

String diff = generateDiff(data1, data2, format);
System.out.println(diff);
Differ.generate(this.filepath1, this.filepath2, this.format);
return 0; // Успешное завершение

} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
return 1; // Код ошибки
}

}

/**
* Проверяет, что оба файла имеют одинаковое расширение.
*/
private void validateFileExtensions() throws IOException {
String ext1 = getFileExtension(filepath1);
String ext2 = getFileExtension(filepath2);

if (!ext1.equals(ext2)) {
throw new IOException("Files must have the same extension. "
+ "First file has ." + ext1 + " extension, "
+ "second file has ." + ext2 + " extension");
}

// Дополнительная проверка на поддерживаемые форматы
if (!ext1.equals("json") && !ext1.equals("yaml") && !ext1.equals("yml")) {
throw new IOException("Unsupported file format: ." + ext1
+ ". Supported formats are: json, yaml, yml");
}
}

/**
* @param filePath Путь к файлу.
* @return Расширение файла в нижнем регистре.
*/
private String getFileExtension(String filePath) {
Path path = Paths.get(filePath);
String fileName = path.getFileName().toString();

int dotIndex = fileName.lastIndexOf('.');
if (dotIndex > 0 && dotIndex < fileName.length() - 1) {
return fileName.substring(dotIndex + 1).toLowerCase();
}
return "";
}


public static String generateDiff(Map<String, Object> data1, Map<String, Object> data2) {
return generateDiff(data1, data2, "stylish");
}

public static String generateDiff(Map<String, Object> data1, Map<String, Object> data2, String formatType) {
DiffBuilder diffBuilder = new DiffBuilder();
List<DiffEntry> diffEntries = diffBuilder.buildDiff(data1, data2);

Formatter formatter = FormatterFactory.create(formatType);
return formatter.format(diffEntries);
}


private String readFileContent(String filePath) throws IOException {
// Преобразуем путь в абсолютный и нормализуем (разрешаем .. и .)
Path path = Paths.get(filePath).toAbsolutePath().normalize();

// Проверка существования файла
if (!Files.exists(path)) {
throw new IOException("File not found: " + filePath);
}

// Проверка, что это файл, а не директория
if (!Files.isRegularFile(path)) {
throw new IOException("Path is not a file: " + filePath);
}

// Чтение содержимого с явным указанием кодировки
return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
}

}
2 changes: 1 addition & 1 deletion app/src/main/java/hexlet/code/DiffBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import java.util.ArrayList;
import java.util.Objects;

public class DiffBuilder {
public final class DiffBuilder {
public List<DiffEntry> buildDiff(Map<String, Object> data1, Map<String, Object> data2) {
Set<String> uniqueKeys = new TreeSet<>(data1.keySet());
uniqueKeys.addAll(data2.keySet());
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/hexlet/code/DiffEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import java.util.Objects;

public class DiffEntry {
public final class DiffEntry {
private final String key;
private final DiffStatus status;
private final Object oldValue;
Expand Down
Loading