package bytecode.patchfile; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class PatchHunk { public String path; public int oldStart, oldCount, newStart, newCount; public List<String> oldLines = new ArrayList<>(); public List<String> newLines = new ArrayList<>(); public boolean canApply(List<String> lines, int start) { if(start + oldCount > lines.size() + 1) return false; if(start < 1) return false; for(int k = 0; k < oldCount; k++) { String actualLine = lines.get(start-1 + k); String requiredLine = oldLines.get(k); if(!actualLine.trim().equals(requiredLine.trim())) { /*if(start == newStart) { System.err.println(); for(int i = 0; i <= 5; i++) System.err.println("CONTEXT: "+(i+1)+": "+lines.get(i)); for(int i = -5; i <= 5; i++) if(start+k+i-1 < lines.size()) System.err.println("CONTEXT: "+(start+i+k)+": "+lines.get(start+k+i-1)); System.err.println("XXX: "+(start+k)+": "+actualLine); System.err.println("XXX: "+(start+k)+": "+oldLines.get(k)); }*/ //throw new RuntimeException("patch failed, old line "+(start+k)+" was '"+actualLine+"', need '"+oldLines.get(k)+"'"); return false; } } return true; } public int findStart(List<String> lines) { if(canApply(lines, newStart)) return newStart; for(int k = 0; k < lines.size(); k++) { if(canApply(lines, newStart+k)) return newStart+k; if(canApply(lines, newStart-k)) return newStart-k; } List<String> actual = lines.subList(newStart, newStart+oldCount); List<String> expected = oldLines; System.err.println(); System.err.println("== EXPECTED LINES =="); for(String s : expected) System.err.println(s); System.err.println(); System.err.println("== ACTUAL LINES =="); for(String s : actual) System.err.println(s); System.err.println(); throw new RuntimeException("can't find place to apply patch"); } public void apply(List<String> lines) { // check context int at = findStart(lines); if(at != newStart + (newCount == 0 ? 1 : 0)) System.err.println("Applying hunk -"+oldStart+","+oldCount+" at line "+at+" instead of "+newStart+" in "+path); at--; //for(int k = 0; k < oldCount; k++) // lines.remove(at); //for(int k = 0; k < newCount; k++) // lines.add(at+k, newLines.get(k)); if(oldCount != 0) lines.subList(at, at+oldCount).clear(); if(newLines.size() != 0) lines.addAll(at, newLines); } public void applyStreaming(StreamingPatchContext ctx) throws IOException { // can't search around when streaming - must be an exact match ctx.passThroughUntil(oldStart + (oldCount == 0 ? 1 : 0), newStart + (newCount == 0 ? 1 : 0)); for(int k = 0; k < oldCount; k++) { String actual = ctx.readLine(); String expected = oldLines.get(k); if(!actual.equals(expected)) throw new IOException("Invalid patch? Expected line '"+expected+"', found '"+actual+"' on line "+(oldStart + k)); } for(String s : newLines) ctx.writeLine(s); } }