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) || type == ChangeType.DELETE_DIR && source.startsWith(target + "/")) {
156 return true;
157 }
158 }
159 }
160 return false;
161 }
162
163
164
165
166
167
168
169
170
171
172
173 private ChangeSetResults perform(final ArchiveEntryIterator<E> entryIterator, final O outputStream) throws IOException {
174 final ChangeSetResults results = new ChangeSetResults();
175
176 final Set<Change<E>> workingSet = new LinkedHashSet<>(changes);
177
178 for (final Iterator<Change<E>> it = workingSet.iterator(); it.hasNext();) {
179 final Change<E> change = it.next();
180
181 if (change.getType() == ChangeType.ADD && change.isReplaceMode()) {
182 @SuppressWarnings("resource")
183 final InputStream inputStream = change.getInputStream();
184 copyStream(inputStream, outputStream, change.getEntry());
185 it.remove();
186 results.addedFromChangeSet(change.getEntry().getName());
187 }
188 }
189
190 while (entryIterator.hasNext()) {
191 final E entry = entryIterator.next();
192 boolean copy = true;
193
194 for (final Iterator<Change<E>> it = workingSet.iterator(); it.hasNext();) {
195 final Change<E> change = it.next();
196
197 final ChangeType type = change.getType();
198 final String name = entry.getName();
199 if (type == ChangeType.DELETE && name != null) {
200 if (name.equals(change.getTargetFileName())) {
201 copy = false;
202 it.remove();
203 results.deleted(name);
204 break;
205 }
206 } else
207 if (type == ChangeType.DELETE_DIR && name != null && name.startsWith(change.getTargetFileName() + "/")) {
208 copy = false;
209 results.deleted(name);
210 break;
211 }
212 }
213
214 if (copy && !isDeletedLater(workingSet, entry) && !results.hasBeenAdded(entry.getName())) {
215 @SuppressWarnings("resource")
216 final InputStream inputStream = entryIterator.getInputStream();
217 copyStream(inputStream, outputStream, entry);
218 results.addedFromStream(entry.getName());
219 }
220 }
221
222
223 for (final Iterator<Change<E>> it = workingSet.iterator(); it.hasNext();) {
224 final Change<E> change = it.next();
225
226 if (change.getType() == ChangeType.ADD && !change.isReplaceMode() && !results.hasBeenAdded(change.getEntry().getName())) {
227 @SuppressWarnings("resource")
228 final InputStream input = change.getInputStream();
229 copyStream(input, outputStream, change.getEntry());
230 it.remove();
231 results.addedFromChangeSet(change.getEntry().getName());
232 }
233 }
234 outputStream.finish();
235 return results;
236 }
237
238
239
240
241
242
243
244
245
246
247
248 public ChangeSetResults perform(final I inputStream, final O outputStream) throws IOException {
249 return perform(new ArchiveInputStreamIterator<>(inputStream), outputStream);
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263 public ChangeSetResults perform(final ZipFile zipFile, final O outputStream) throws IOException {
264 @SuppressWarnings("unchecked")
265 final ArchiveEntryIterator<E> entryIterator = (ArchiveEntryIterator<E>) new ZipFileIterator(zipFile);
266 return perform(entryIterator, outputStream);
267 }
268 }