1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.build.apkzlib.zip; 18 19 import com.android.tools.build.apkzlib.utils.IOExceptionRunnable; 20 import java.io.IOException; 21 import javax.annotation.Nonnull; 22 import javax.annotation.Nullable; 23 24 /** 25 * An extension of a {@link ZFile}. Extensions are notified when files are open, updated, closed and 26 * when files are added or removed from the zip. These notifications are received after the zip 27 * has been updated in memory for open, when files are added or removed and when the zip has been 28 * updated on disk or closed. 29 * <p> 30 * An extension is also notified before the file is updated, allowing it to modify the file before 31 * the update happens. If it does, then all extensions are notified of the changes on the zip file. 32 * Because the order of the notifications is preserved, all extensions are notified in the same 33 * order. For example, if two extensions E1 and E2 are registered and they both add a file at 34 * update time, this would be the flow: 35 * <ul> 36 * <li>E1 receives {@code beforeUpdate} notification.</li> 37 * <li>E1 adds file F1 to the zip (notifying the addition is suspended because another 38 * notification is in progress).</li> 39 * <li>E2 receives {@code beforeUpdate} notification.</li> 40 * <li>E2 adds file F2 to the zip (notifying the addition is suspended because another 41 * notification is in progress).</li> 42 * <li>E1 is notified F1 was added.</li> 43 * <li>E2 is notified F1 was added.</li> 44 * <li>E1 is notified F2 was added.</li> 45 * <li>E2 is notified F2 was added.</li> 46 * <li>(zip file is updated on disk)</li> 47 * <li>E1 is notified the zip was updated.</li> 48 * <li>E2 is notified the zip was updated.</li> 49 * </ul> 50 * <p> 51 * An extension should not modify the zip file when notified of changes. If allowed, this would 52 * break event notification order in case multiple extensions are registered with the zip file. 53 * To allow performing changes to the zip file, all notification method return a 54 * {@code IOExceptionRunnable} that is invoked when {@link ZFile} has finished notifying all 55 * extensions. 56 */ 57 public abstract class ZFileExtension { 58 59 /** 60 * The zip file has been open and the zip's contents have been read. The default implementation 61 * does nothing and returns {@code null}. 62 * 63 * @return an optional runnable to run when notification of all listeners has ended 64 * @throws IOException failed to process the event 65 */ 66 @Nullable open()67 public IOExceptionRunnable open() throws IOException { 68 return null; 69 } 70 71 /** 72 * The zip will be updated. This method allows the extension to register changes to the zip 73 * file before the file is written. The default implementation does nothing and returns 74 * {@code null}. 75 * <p> 76 * After this notification is received, the extension will receive further 77 * {@link #added(StoredEntry, StoredEntry)} and {@link #removed(StoredEntry)} notifications if 78 * it or other extensions add or remove files before update. 79 * <p> 80 * When no more files are updated, the {@link #entriesWritten()} notification is sent. 81 * 82 * @return an optional runnable to run when notification of all listeners has ended 83 * @throws IOException failed to process the event 84 */ 85 @Nullable beforeUpdate()86 public IOExceptionRunnable beforeUpdate() throws IOException { 87 return null; 88 } 89 90 /** 91 * This notification is sent when all entries have been written in the file but the central 92 * directory and the EOCD have not yet been written. No entries should be added, removed or 93 * updated during this notification. If this method forces an update of either the central 94 * directory or EOCD, then this method will be invoked again for all extensions with the new 95 * central directory and EOCD. 96 * <p> 97 * After this notification, {@link #updated()} is sent. 98 * 99 * @throws IOException failed to process the event 100 */ entriesWritten()101 public void entriesWritten() throws IOException { 102 } 103 104 /** 105 * The zip file has been updated on disk. The default implementation does nothing. 106 * 107 * @throws IOException failed to perform update tasks 108 */ updated()109 public void updated() throws IOException { 110 } 111 112 /** 113 * The zip file has been closed. Note that if {@link ZFile#close()} requires that the zip file 114 * be updated (because it had in-memory changes), {@link #updated()} will be called before 115 * this method. The default implementation does nothing. 116 */ closed()117 public void closed() { 118 } 119 120 /** 121 * A new entry has been added to the zip, possibly replacing an entry in there. The 122 * default implementation does nothing and returns {@code null}. 123 * 124 * @param entry the entry that was added 125 * @param replaced the entry that was replaced, if any 126 * @return an optional runnable to run when notification of all listeners has ended 127 */ 128 @Nullable added(@onnull StoredEntry entry, @Nullable StoredEntry replaced)129 public IOExceptionRunnable added(@Nonnull StoredEntry entry, @Nullable StoredEntry replaced) { 130 return null; 131 } 132 133 /** 134 * An entry has been removed from the zip. This method is not invoked for entries that have 135 * been replaced. Those entries are notified using <em>replaced</em> in 136 * {@link #added(StoredEntry, StoredEntry)}. The default implementation does nothing and 137 * returns {@code null}. 138 * 139 * @param entry the entry that was deleted 140 * @return an optional runnable to run when notification of all listeners has ended 141 */ 142 @Nullable removed(@onnull StoredEntry entry)143 public IOExceptionRunnable removed(@Nonnull StoredEntry entry) { 144 return null; 145 } 146 } 147