1 // Copyright 2016 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.google.archivepatcher.generator; 16 17 import com.google.archivepatcher.generator.bsdiff.BsDiffDeltaGenerator; 18 import java.io.BufferedOutputStream; 19 import java.io.File; 20 import java.io.FileOutputStream; 21 import java.io.IOException; 22 import java.io.OutputStream; 23 import java.util.Arrays; 24 import java.util.Collections; 25 import java.util.List; 26 27 /** 28 * Generates file-by-file patches. 29 */ 30 public class FileByFileV1DeltaGenerator implements DeltaGenerator { 31 32 /** Optional modifiers for planning and patch generation. */ 33 private final List<RecommendationModifier> recommendationModifiers; 34 35 /** 36 * Constructs a new generator for File-by-File v1 patches, using the specified configuration. 37 * 38 * @param recommendationModifiers optionally, {@link RecommendationModifier}s to use for modifying 39 * the planning phase of patch generation. These can be used to, e.g., limit the total amount 40 * of recompression that a patch applier needs to do. Modifiers are applied in the order they 41 * are specified. 42 */ FileByFileV1DeltaGenerator(RecommendationModifier... recommendationModifiers)43 public FileByFileV1DeltaGenerator(RecommendationModifier... recommendationModifiers) { 44 if (recommendationModifiers != null) { 45 this.recommendationModifiers = 46 Collections.unmodifiableList(Arrays.asList(recommendationModifiers)); 47 } else { 48 this.recommendationModifiers = Collections.emptyList(); 49 } 50 } 51 52 /** 53 * Generate a V1 patch for the specified input files and write the patch to the specified {@link 54 * OutputStream}. The written patch is <em>raw</em>, i.e. it has not been compressed. Compression 55 * should almost always be applied to the patch, either right in the specified {@link 56 * OutputStream} or in a post-processing step, prior to transmitting the patch to the patch 57 * applier. 58 * 59 * @param oldFile the original old file to read (will not be modified) 60 * @param newFile the original new file to read (will not be modified) 61 * @param patchOut the stream to write the patch to 62 * @throws IOException if unable to complete the operation due to an I/O error 63 * @throws InterruptedException if any thread has interrupted the current thread 64 */ 65 @Override generateDelta(File oldFile, File newFile, OutputStream patchOut)66 public void generateDelta(File oldFile, File newFile, OutputStream patchOut) 67 throws IOException, InterruptedException { 68 try (TempFileHolder deltaFriendlyOldFile = new TempFileHolder(); 69 TempFileHolder deltaFriendlyNewFile = new TempFileHolder(); 70 TempFileHolder deltaFile = new TempFileHolder(); 71 FileOutputStream deltaFileOut = new FileOutputStream(deltaFile.file); 72 BufferedOutputStream bufferedDeltaOut = new BufferedOutputStream(deltaFileOut)) { 73 PreDiffExecutor.Builder builder = 74 new PreDiffExecutor.Builder() 75 .readingOriginalFiles(oldFile, newFile) 76 .writingDeltaFriendlyFiles(deltaFriendlyOldFile.file, deltaFriendlyNewFile.file); 77 for (RecommendationModifier modifier : recommendationModifiers) { 78 builder.withRecommendationModifier(modifier); 79 } 80 PreDiffExecutor executor = builder.build(); 81 PreDiffPlan preDiffPlan = executor.prepareForDiffing(); 82 DeltaGenerator deltaGenerator = getDeltaGenerator(); 83 deltaGenerator.generateDelta( 84 deltaFriendlyOldFile.file, deltaFriendlyNewFile.file, bufferedDeltaOut); 85 bufferedDeltaOut.close(); 86 PatchWriter patchWriter = 87 new PatchWriter( 88 preDiffPlan, 89 deltaFriendlyOldFile.file.length(), 90 deltaFriendlyNewFile.file.length(), 91 deltaFile.file); 92 patchWriter.writeV1Patch(patchOut); 93 } 94 } 95 96 /** 97 * Generate a V1 patch pre diffing plan. 98 * 99 * @param oldFile the original old file to read (will not be modified) 100 * @param newFile the original new file to read (will not be modified) 101 * @return the plan 102 * @throws IOException if unable to complete the operation due to an I/O error 103 * @throws InterruptedException if any thread has interrupted the current thread 104 */ generatePreDiffPlan(File oldFile, File newFile)105 public PreDiffPlan generatePreDiffPlan(File oldFile, File newFile) 106 throws IOException, InterruptedException { 107 try (TempFileHolder deltaFriendlyOldFile = new TempFileHolder(); 108 TempFileHolder deltaFriendlyNewFile = new TempFileHolder()) { 109 PreDiffExecutor.Builder builder = 110 new PreDiffExecutor.Builder() 111 .readingOriginalFiles(oldFile, newFile) 112 .writingDeltaFriendlyFiles(deltaFriendlyOldFile.file, deltaFriendlyNewFile.file); 113 for (RecommendationModifier modifier : recommendationModifiers) { 114 builder.withRecommendationModifier(modifier); 115 } 116 117 PreDiffExecutor executor = builder.build(); 118 119 return executor.prepareForDiffing(); 120 } 121 } 122 123 // Visible for testing only getDeltaGenerator()124 protected DeltaGenerator getDeltaGenerator() { 125 return new BsDiffDeltaGenerator(); 126 } 127 } 128