diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataDiff.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataDiff.java index 70c8c361..0b874d47 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataDiff.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataDiff.java @@ -1,13 +1,17 @@ package org.qortal.arbitrary; +import com.github.difflib.DiffUtils; +import com.github.difflib.UnifiedDiffUtils; +import com.github.difflib.patch.Patch; +import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qortal.arbitrary.metadata.ArbitraryDataMetadataPatch; import org.qortal.crypto.Crypto; import org.qortal.settings.Settings; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; @@ -133,9 +137,14 @@ public class ArbitraryDataDiff { wasModified = true; } - if (wasAdded | wasModified) { + if (wasAdded) { ArbitraryDataDiff.copyFilePathToBaseDir(after, diffPathAbsolute, filePathAfter); } + if (wasModified) { + // Create patch using java-diff-utils + Path destination = Paths.get(diffPathAbsolute.toString(), filePathAfter.toString()); + ArbitraryDataDiff.createAndCopyDiffUtilsPatch(filePathBefore, after, destination); + } return FileVisitResult.CONTINUE; } @@ -159,7 +168,7 @@ public class ArbitraryDataDiff { } } - private void findRemovedFiles() { + private void findRemovedFiles() throws IOException { try { final Path pathBeforeAbsolute = this.pathBefore.toAbsolutePath(); final Path pathAfterAbsolute = this.pathAfter.toAbsolutePath(); @@ -219,7 +228,7 @@ public class ArbitraryDataDiff { }); } catch (IOException e) { - LOGGER.info("IOException when walking through file tree: {}", e.getMessage()); + throw new IOException(String.format("IOException when walking through file tree: %s", e.getMessage())); } } @@ -231,6 +240,7 @@ public class ArbitraryDataDiff { private void writeMetadata() throws IOException { ArbitraryDataMetadataPatch metadata = new ArbitraryDataMetadataPatch(this.diffPath); + metadata.setPatchType("unified-diff"); metadata.setAddedPaths(this.addedPaths); metadata.setModifiedPaths(this.modifiedPaths); metadata.setRemovedPaths(this.removedPaths); @@ -264,6 +274,50 @@ public class ArbitraryDataDiff { LOGGER.trace("Copying {} to {}", source, dest); Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING); } + + private static void createAndCopyDiffUtilsPatch(Path before, Path after, Path destination) throws IOException { + if (!Files.exists(before)) { + throw new IOException(String.format("File not found (before): %s", before.toString())); + } + if (!Files.exists(after)) { + throw new IOException(String.format("File not found (after): %s", after.toString())); + } + + // Ensure parent folders exist in the destination + File file = new File(destination.toString()); + File parent = file.getParentFile(); + if (parent != null) { + parent.mkdirs(); + } + + // Delete an existing file if it exists + File destFile = destination.toFile(); + if (destFile.exists() && destFile.isFile()) { + Files.delete(destination); + } + + // Load the two files into memory + List original = FileUtils.readLines(before.toFile(), StandardCharsets.UTF_8); + List revised = FileUtils.readLines(after.toFile(), StandardCharsets.UTF_8); + + // Generate diff information + Patch diff = DiffUtils.diff(original, revised); + + // Generate unified diff format + String originalFileName = before.getFileName().toString(); + String revisedFileName = after.getFileName().toString(); + List unifiedDiff = UnifiedDiffUtils.generateUnifiedDiff(originalFileName, revisedFileName, original, diff, 0); + + // Write the diff to the destination directory + FileWriter fileWriter = new FileWriter(destination.toString(), true); + BufferedWriter writer = new BufferedWriter(fileWriter); + for (String line : unifiedDiff) { + writer.append(line); + writer.newLine(); + } + writer.flush(); + writer.close(); + } public Path getDiffPath() { diff --git a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataPatch.java b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataPatch.java index e6f392cb..0f7b6b56 100644 --- a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataPatch.java +++ b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataPatch.java @@ -17,6 +17,7 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata { private static final Logger LOGGER = LogManager.getLogger(ArbitraryDataMetadataPatch.class); + private String patchType; private List addedPaths; private List modifiedPaths; private List removedPaths; @@ -43,6 +44,12 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata { } JSONObject patch = new JSONObject(this.jsonString); + if (patch.has("patchType")) { + String patchType = patch.getString("patchType"); + if (patchType != null) { + this.patchType = patchType; + } + } if (patch.has("prevSig")) { String prevSig = patch.getString("prevSig"); if (prevSig != null) { @@ -94,8 +101,9 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata { changeMap.set(patch, new LinkedHashMap<>()); changeMap.setAccessible(false); } catch (IllegalAccessException | NoSuchFieldException e) { - // Don't worry about failures as this is for ordering only + // Don't worry about failures as this is for optional ordering only } + patch.put("patchType", this.patchType); patch.put("prevSig", Base58.encode(this.previousSignature)); patch.put("prevHash", Base58.encode(this.previousHash)); patch.put("added", new JSONArray(this.addedPaths)); @@ -107,6 +115,14 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata { } + public void setPatchType(String patchType) { + this.patchType = patchType; + } + + public String getPatchType() { + return this.patchType; + } + public void setAddedPaths(List addedPaths) { this.addedPaths = addedPaths; }