summaryrefslogtreecommitdiff
path: root/src/main/java/com/android/apkzlib/zip/ZFileExtension.java
blob: fdb6ca4b765c1f869fa730118acd3d814ee8054a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.apkzlib.zip;

import com.android.apkzlib.utils.IOExceptionRunnable;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * An extension of a {@link ZFile}. Extensions are notified when files are open, updated, closed and
 * when files are added or removed from the zip. These notifications are received after the zip
 * has been updated in memory for open, when files are added or removed and when the zip has been
 * updated on disk or closed.
 * <p>
 * An extension is also notified before the file is updated, allowing it to modify the file before
 * the update happens. If it does, then all extensions are notified of the changes on the zip file.
 * Because the order of the notifications is preserved, all extensions are notified in the same
 * order. For example, if two extensions E1 and E2 are registered and they both add a file at
 * update time, this would be the flow:
 * <ul>
 *     <li>E1 receives {@code beforeUpdate} notification.</li>
 *     <li>E1 adds file F1 to the zip (notifying the addition is suspended because another
 *     notification is in progress).</li>
 *     <li>E2 receives {@code beforeUpdate} notification.</li>
 *     <li>E2 adds file F2 to the zip (notifying the addition is suspended because another
 *     notification is in progress).</li>
 *     <li>E1 is notified F1 was added.</li>
 *     <li>E2 is notified F1 was added.</li>
 *     <li>E1 is notified F2 was added.</li>
 *     <li>E2 is notified F2 was added.</li>
 *     <li>(zip file is updated on disk)</li>
 *     <li>E1 is notified the zip was updated.</li>
 *     <li>E2 is notified the zip was updated.</li>
 * </ul>
 * <p>
 * An extension should not modify the zip file when notified of changes. If allowed, this would
 * break event notification order in case multiple extensions are registered with the zip file.
 * To allow performing changes to the zip file, all notification method return a
 * {@code IOExceptionRunnable} that is invoked when {@link ZFile} has finished notifying all
 * extensions.
 */
public abstract class ZFileExtension {

    /**
     * The zip file has been open and the zip's contents have been read. The default implementation
     * does nothing and returns {@code null}.
     *
     * @return an optional runnable to run when notification of all listeners has ended
     * @throws IOException failed to process the event
     */
    @Nullable
    public IOExceptionRunnable open() throws IOException {
        return null;
    }

    /**
     * The zip will be updated. This method allows the extension to register changes to the zip
     * file before the file is written. The default implementation does nothing and returns
     * {@code null}.
     * <p>
     * After this notification is received, the extension will receive further
     * {@link #added(StoredEntry, StoredEntry)} and {@link #removed(StoredEntry)} notifications if
     * it or other extensions add or remove files before update.
     * <p>
     * When no more files are updated, the {@link #entriesWritten()} notification is sent.
     *
     * @return an optional runnable to run when notification of all listeners has ended
     * @throws IOException failed to process the event
     */
    @Nullable
    public IOExceptionRunnable beforeUpdate() throws IOException {
        return null;
    }

    /**
     * This notification is sent when all entries have been written in the file but the central
     * directory and the EOCD have not yet been written. No entries should be added, removed or
     * updated during this notification. If this method forces an update of either the central
     * directory or EOCD, then this method will be invoked again for all extensions with the new
     * central directory and EOCD.
     * <p>
     * After this notification, {@link #updated()} is sent.
     *
     * @throws IOException failed to process the event
     */
    public void entriesWritten() throws IOException {
    }

    /**
     * The zip file has been updated on disk. The default implementation does nothing.
     *
     * @throws IOException failed to perform update tasks
     */
    public void updated() throws IOException {
    }

    /**
     * The zip file has been closed. Note that if {@link ZFile#close()} requires that the zip file
     * be updated (because it had in-memory changes), {@link #updated()} will be called before
     * this method. The default implementation does nothing.
     */
    public void closed() {
    }

    /**
     * A new entry has been added to the zip, possibly replacing an entry in there. The
     * default implementation does nothing and returns {@code null}.
     *
     * @param entry the entry that was added
     * @param replaced the entry that was replaced, if any
     * @return an optional runnable to run when notification of all listeners has ended
     */
    @Nullable
    public IOExceptionRunnable added(@Nonnull StoredEntry entry, @Nullable StoredEntry replaced) {
        return null;
    }

    /**
     * An entry has been removed from the zip. This method is not invoked for entries that have
     * been replaced. Those entries are notified using <em>replaced</em> in
     * {@link #added(StoredEntry, StoredEntry)}. The default implementation does nothing and
     * returns {@code null}.
     *
     * @param entry the entry that was deleted
     * @return an optional runnable to run when notification of all listeners has ended
     */
    @Nullable
    public IOExceptionRunnable removed(@Nonnull StoredEntry entry) {
        return null;
    }
}