Skip to content

Commit

Permalink
Merge pull request #778 from shraddha761/jsonDifference
Browse files Browse the repository at this point in the history
#754 | Implementer friendliness for avni
  • Loading branch information
himeshr committed Sep 20, 2024
2 parents b9a82de + acc8e13 commit 40f8dc3
Show file tree
Hide file tree
Showing 7 changed files with 601 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.avni.server.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@Service
public class MetadataBundleAndFileHandler {

private static final ObjectMapper objectMapper = new ObjectMapper();

protected File extractZip(MultipartFile zipFile) throws IOException {
File tempDir = Files.createTempDirectory("metadata-zip").toFile();

try (ZipInputStream zipInputStream = new ZipInputStream(zipFile.getInputStream())) {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
if (!entry.isDirectory()) {
File file = new File(tempDir, entry.getName());
File parentDir = file.getParentFile();
if (!parentDir.exists()) {
parentDir.mkdirs();
}

try (OutputStream outputStream = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int length;
while ((length = zipInputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
}
}
zipInputStream.closeEntry();
}
}
return tempDir;
}

protected List<File> listJsonFiles(File directory) {
List<File> jsonFiles = new ArrayList<>();
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
jsonFiles.addAll(listJsonFiles(file));
} else if (file.isFile() && file.getName().toLowerCase().endsWith(".json")) {
jsonFiles.add(file);
}
}
}
return jsonFiles;
}

protected Map<String, Map<String, Object>> parseJsonFiles(List<File> files, File rootDir) throws IOException {
Map<String, Map<String, Object>> jsonMap = new HashMap<>();

for (File file : files) {
String relativePath = getRelativePath(file, rootDir);
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
StringBuilder jsonContent = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
jsonContent.append(line);
}

Map<String, Object> jsonMapFile = new HashMap<>();
if (jsonContent.toString().trim().startsWith("[")) {
List<Map<String, Object>> jsonArray = objectMapper.readValue(jsonContent.toString(), new TypeReference<List<Map<String, Object>>>() {});
for (Map<String, Object> jsonObject : jsonArray) {
String uuid = (String) jsonObject.get("uuid");
if (uuid != null) {
jsonObject.remove("filename");
jsonMapFile.put(uuid, jsonObject);
}
}
} else {
Map<String, Object> jsonObject = objectMapper.readValue(jsonContent.toString(), new TypeReference<Map<String, Object>>() {});
String uuid = (String) jsonObject.get("uuid");
if (uuid != null) {
jsonObject.remove("filename");
jsonMapFile.put(uuid, jsonObject);
}
}
jsonMap.put(relativePath, jsonMapFile);
}
}
return jsonMap;
}
private String getRelativePath(File file, File rootDir) {
String filePath = file.getPath();
String rootPath = rootDir.getPath();
return filePath.substring(rootPath.length() + 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package org.avni.server.service;

import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class MetadataDiffChecker {

public static final String MODIFIED = "modified";
public static final String ADDED = "added";
public static final String REMOVED = "removed";
public static final String NO_MODIFICATION = "noModification";

MetadataDiffOutputGenerator metadataDiffOutputGenerator;

public MetadataDiffChecker(MetadataDiffOutputGenerator metadataDiffOutputGenerator) {
this.metadataDiffOutputGenerator = metadataDiffOutputGenerator;
}

protected Map<String, Object> findDifferences(Map<String, Object> jsonMap1, Map<String, Object> jsonMap2) {
Map<String, Object> differences = new HashMap<>();
boolean hasDifferences = false;
String uuid = "null";
for (Map.Entry<String, Object> entry : jsonMap1.entrySet()) {
uuid = entry.getKey();
Object json1 = entry.getValue();
Object json2 = jsonMap2.get(uuid);
if (json2 != null) {
Map<String, Object> diff = findJsonDifferences(castToStringObjectMap(json1), castToStringObjectMap(json2));
if (!diff.isEmpty()) {
differences.put(uuid, diff);
hasDifferences = true;
}
} else {
differences.put(uuid, metadataDiffOutputGenerator.createFieldDiff( json1, null,REMOVED));
hasDifferences = true;
}
}

for (Map.Entry<String, Object> entry : jsonMap2.entrySet()) {
String uuid2 = entry.getKey();
if (!jsonMap1.containsKey(uuid2)) {
differences.put(uuid2, metadataDiffOutputGenerator.createFieldDiff(null, entry.getValue(), ADDED));
hasDifferences = true;
}
}

if (!hasDifferences) {
differences.put(uuid, metadataDiffOutputGenerator.createFieldDiff(null, null, NO_MODIFICATION));
}
return differences;
}

protected Map<String, Object> findJsonDifferences(Map<String, Object> json1, Map<String, Object> json2) {
Map<String, Object> differences = new LinkedHashMap<>();
if (json1 == null && json2 == null) {
return differences;
}

if (json1 == null) {
json2.forEach((key, value) -> differences.put(key, metadataDiffOutputGenerator.createFieldDiff(null, value, ADDED)));
return differences;
}

if (json2 == null) {
json1.forEach((key, value) -> differences.put(key, metadataDiffOutputGenerator.createFieldDiff(value, null, REMOVED)));
return differences;
}

for (Map.Entry<String, Object> entry : json1.entrySet()) {
String key = entry.getKey();
Object value1 = entry.getValue();
Object value2 = json2.get(key);

if (key.equals("id")) {
continue;
}
if (value2 == null) {
differences.put(key, metadataDiffOutputGenerator.createFieldDiff(value1, null, REMOVED));
} else {
if (value1 instanceof Map && value2 instanceof Map) {
Map<String, Object> subDiff = findJsonDifferences((Map<String, Object>) value1, (Map<String, Object>) value2);
if (!subDiff.isEmpty()) {
differences.put(key, metadataDiffOutputGenerator.createObjectDiff(subDiff, MODIFIED));
}
} else if (value1 instanceof List && value2 instanceof List) {
List<Map<String, Object>> listDiff = findArrayDifferences((List<Object>) value1, (List<Object>) value2);
if (!listDiff.isEmpty()) {
differences.put(key, metadataDiffOutputGenerator.createArrayDiff(listDiff, MODIFIED));
}
} else if (!value1.equals(value2)) {
differences.put(key, metadataDiffOutputGenerator.createFieldDiff(value1, value2, MODIFIED));
}
}
}

for (Map.Entry<String, Object> entry : json2.entrySet()) {
String key = entry.getKey();
if (!json1.containsKey(key)) {
differences.put(key, metadataDiffOutputGenerator.createFieldDiff(null, entry.getValue(), ADDED));
}
}

return differences;
}

protected List<Map<String, Object>> findArrayDifferences(List<Object> array1, List<Object> array2) {
List<Map<String, Object>> differences = new ArrayList<>();

Function<Map<String, Object>, String> getUuid = obj -> (String) obj.get("uuid");

Map<String, Map<String, Object>> map1 = array1.stream()
.filter(obj -> obj instanceof Map)
.map(obj -> (Map<String, Object>) obj)
.collect(Collectors.toMap(getUuid, Function.identity(), (e1, e2) -> e1));

Map<String, Map<String, Object>> map2 = array2.stream()
.filter(obj -> obj instanceof Map)
.map(obj -> (Map<String, Object>) obj)
.collect(Collectors.toMap(getUuid, Function.identity(), (e1, e2) -> e1));

for (String uuid : map2.keySet()) {
if (!map1.containsKey(uuid)) {
differences.add(metadataDiffOutputGenerator.createFieldDiff(null, map2.get(uuid), ADDED));
} else {
Map<String, Object> obj1 = map1.get(uuid);
Map<String, Object> obj2 = map2.get(uuid);

Map<String, Object> subDiff = findJsonDifferences(obj1, obj2);
if (!subDiff.isEmpty()) {
differences.add(metadataDiffOutputGenerator.createObjectDiff(subDiff, MODIFIED));
}
}
}

for (String uuid : map1.keySet()) {
if (!map2.containsKey(uuid)) {
differences.add(metadataDiffOutputGenerator.createFieldDiff(map1.get(uuid), null, REMOVED));
}
}
return differences;
}
private Map<String, Object> castToStringObjectMap(Object obj) {
if (obj instanceof Map) {
return (Map<String, Object>) obj;
}
return new HashMap<>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.avni.server.service;

import org.springframework.stereotype.Service;

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

@Service
public class MetadataDiffOutputGenerator {

public static final String NO_MODIFICATION = "noModification";
public static final String OLD_VALUE = "oldValue";
public static final String NEW_VALUE = "newValue";
public static final String CHANGE_TYPE = "changeType";
public static final String DATA_TYPE = "dataType";
public static final String OBJECT = "object";
public static final String FIELD = "field";
public static final String ARRAY = "array";
public static final String PRIMITIVE = "primitive";
public static final String ITEMS = "items";

protected Map<String, Object> createFieldDiff(Object oldValue, Object newValue, String changeType) {
Map<String, Object> fieldDiff = new LinkedHashMap<>();

if(!NO_MODIFICATION.equals(changeType)) {
if (oldValue == null && newValue != null) {
fieldDiff.put(DATA_TYPE, getDataType(newValue));
} else if (oldValue != null && newValue == null) {
fieldDiff.put(DATA_TYPE, getDataType(oldValue));
} else if (oldValue != null && newValue != null) {
fieldDiff.put(DATA_TYPE, getDataType(newValue));
} else {
fieldDiff.put(DATA_TYPE, OBJECT);
}
}
fieldDiff.put(CHANGE_TYPE, changeType);
if (oldValue != null) {
fieldDiff.put(OLD_VALUE, oldValue);
}
if (newValue != null) {
fieldDiff.put(NEW_VALUE, newValue);
}
return fieldDiff;
}

protected Map<String, Object> createObjectDiff(Map<String, Object> fieldsDiff, String changeType) {
Map<String, Object> objectDiff = new LinkedHashMap<>();

if (!fieldsDiff.isEmpty() && !NO_MODIFICATION.equals(changeType)) {
objectDiff.put(DATA_TYPE, OBJECT);
objectDiff.put(CHANGE_TYPE, changeType);
objectDiff.put(FIELD, fieldsDiff);
}
return objectDiff;
}

protected Map<String, Object> createArrayDiff(List<Map<String, Object>> itemsDiff, String changeType) {
Map<String, Object> arrayDiff = new LinkedHashMap<>();

if (!itemsDiff.isEmpty() && !NO_MODIFICATION.equals(changeType)) {
arrayDiff.put(DATA_TYPE, ARRAY);
arrayDiff.put(CHANGE_TYPE, changeType);
arrayDiff.put(ITEMS, itemsDiff);
}
return arrayDiff;
}
private String getDataType(Object value) {
if (value instanceof Map) {
return OBJECT;
} else if (value instanceof List) {
return ARRAY;
} else {
return PRIMITIVE;
}
}
}
Loading

0 comments on commit 40f8dc3

Please sign in to comment.