1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.changes;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.Enumeration;
24 import java.util.Iterator;
25 import java.util.LinkedHashSet;
26 import java.util.Set;
27
28 import org.apache.commons.compress.archivers.ArchiveEntry;
29 import org.apache.commons.compress.archivers.ArchiveInputStream;
30 import org.apache.commons.compress.archivers.ArchiveOutputStream;
31 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
32 import org.apache.commons.compress.archivers.zip.ZipFile;
33 import org.apache.commons.compress.changes.Change.ChangeType;
34
35
36
37
38
39
40
41
42
43
44
45 public class ChangeSetPerformer<I extends ArchiveInputStream<E>, O extends ArchiveOutputStream<E>, E extends ArchiveEntry> {
46
47
48
49
50
51
52
53
54
55 private interface ArchiveEntryIterator<E extends ArchiveEntry> {
56
57 InputStream getInputStream() throws IOException;
58
59 boolean hasNext() throws IOException;
60
61 E next();
62 }
63
64 private static final class ArchiveInputStreamIterator<E extends ArchiveEntry> implements ArchiveEntryIterator<E> {
65
66 private final ArchiveInputStream<E> inputStream;
67 private E next;
68
69 ArchiveInputStreamIterator(final ArchiveInputStream<E> inputStream) {
70 this.inputStream = inputStream;
71 }
72
73 @Override
74 public InputStream getInputStream() {
75 return inputStream;
76 }
77
78 @Override
79 public boolean hasNext() throws IOException {
80 return (next = inputStream.getNextEntry()) != null;
81 }
82
83 @Override
84 public E next() {
85 return next;
86 }
87 }
88
89 private static final class ZipFileIterator implements ArchiveEntryIterator<ZipArchiveEntry> {
90
91 private final ZipFile zipFile;
92 private final Enumeration<ZipArchiveEntry> nestedEnumeration;
93 private ZipArchiveEntry currentEntry;
94
95 ZipFileIterator(final ZipFile zipFile) {
96 this.zipFile = zipFile;
97 this.nestedEnumeration = zipFile.getEntriesInPhysicalOrder();
98 }
99
100 @Override
101 public InputStream getInputStream() throws IOException {
102 return zipFile.getInputStream(currentEntry);
103 }
104
105 @Override
106 public boolean hasNext() {
107 return nestedEnumeration.hasMoreElements();
108 }
109
110 @Override
111 public ZipArchiveEntry next() {
112 return currentEntry = nestedEnumeration.nextElement();
113 }
114 }
115
116 private final Set<Change<E>> changes;
117
118
119
120
121
122
123 public ChangeSetPerformer(final ChangeSet<E> changeSet) {
124 this.changes = changeSet.getChanges();
125 }
126
127
128
129
130
131
132
133
134
135 private void copyStream(final InputStream inputStream, final O outputStream, final E archiveEntry) throws IOException {
136 outputStream.putArchiveEntry(archiveEntry);
137 org.apache.commons.io.IOUtils.copy(inputStream, outputStream);
138 outputStream.closeArchiveEntry();
139 }
140
141
142
143
144
145
146
147
148 private boolean isDeletedLater(final Set<Change<E>> workingSet, final E entry) {
149 final String source = entry.getName();
150
151 if (!workingSet.isEmpty()) {
152 for (final Change<E> change : workingSet) {
153 final ChangeType type = change.getType();
154 final String target = change.getTargetFileName();
155 if (type == ChangeType.DELETE && source.equals(target)) {
156 return true;
157 }
158
159 if (type == ChangeType.DELETE_DIR && source.startsWith(target + "/")) {
160 return true;
161 }
162 }
163 }
164 return false;
165 }
166
167
168
169
170
171
172
173
174
175
176
177 private ChangeSetResults perform(final ArchiveEntryIterator<E> entryIterator, final O outputStream) throws IOException {
178 final ChangeSetResults results = new ChangeSetResults();
179
180 final Set<Change<E>> workingSet = new LinkedHashSet<>(changes);
181
182 for (final Iterator<Change<E>> it = workingSet.iterator(); it.hasNext();) {
183 final Change<E> change = it.next();
184
185 if (change.getType() == ChangeType.ADD && change.isReplaceMode()) {
186 @SuppressWarnings("resource")
187 final InputStream inputStream = change.getInputStream();
188 copyStream(inputStream, outputStream, change.getEntry());
189 it.remove();
190 results.addedFromChangeSet(change.getEntry().getName());
191 }
192 }
193
194 while (entryIterator.hasNext()) {
195 final E entry = entryIterator.next();
196 boolean copy = true;
197
198 for (final Iterator<Change<E>> it = workingSet.iterator(); it.hasNext();) {
199 final Change<E> change = it.next();
200
201 final ChangeType type = change.getType();
202 final String name = entry.getName();
203 if (type == ChangeType.DELETE && name != null) {
204 if (name.equals(change.getTargetFileName())) {
205 copy = false;
206 it.remove();
207 results.deleted(name);
208 break;
209 }
210 } else if (type == ChangeType.DELETE_DIR && name != null) {
211
212 if (name.startsWith(change.getTargetFileName() + "/")) {
213 copy = false;
214 results.deleted(name);
215 break;
216 }
217 }
218 }
219
220 if (copy && !isDeletedLater(workingSet, entry) && !results.hasBeenAdded(entry.getName())) {
221 @SuppressWarnings("resource")
222 final InputStream inputStream = entryIterator.getInputStream();
223 copyStream(inputStream, outputStream, entry);
224 results.addedFromStream(entry.getName());
225 }
226 }
227
228
229 for (final Iterator<Change<E>> it = workingSet.iterator(); it.hasNext();) {
230 final Change<E> change = it.next();
231
232 if (change.getType() == ChangeType.ADD && !change.isReplaceMode() && !results.hasBeenAdded(change.getEntry().getName())) {
233 @SuppressWarnings("resource")
234 final InputStream input = change.getInputStream();
235 copyStream(input, outputStream, change.getEntry());
236 it.remove();
237 results.addedFromChangeSet(change.getEntry().getName());
238 }
239 }
240 outputStream.finish();
241 return results;
242 }
243
244
245
246
247
248
249
250
251
252
253
254 public ChangeSetResults perform(final I inputStream, final O outputStream) throws IOException {
255 return perform(new ArchiveInputStreamIterator<>(inputStream), outputStream);
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269 public ChangeSetResults perform(final ZipFile zipFile, final O outputStream) throws IOException {
270 @SuppressWarnings("unchecked")
271 final ArchiveEntryIterator<E> entryIterator = (ArchiveEntryIterator<E>) new ZipFileIterator(zipFile);
272 return perform(entryIterator, outputStream);
273 }
274 }