tmf: Use Apache Common Compress for importing from archive
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / internal / tmf / ui / project / wizards / importtrace / ZipLeveledStructureProvider.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2016 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Red Hat, Inc - Was ZipFileStructureProvider, performed changes from
11 * IImportStructureProvider to ILeveledImportStructureProvider
12 * Mickael Istria (Red Hat Inc.) - Bug 486901
13 * Marc-Andre Laperle <marc-andre.laperle@ericsson.com> - Copied to Trace
14 * Compass to use Apache Common Compress and fix bug 501664
15 *******************************************************************************/
16 package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.util.ArrayList;
21 import java.util.Enumeration;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
27 import org.apache.commons.compress.archivers.zip.ZipFile;
28 import org.eclipse.core.runtime.IPath;
29 import org.eclipse.core.runtime.Path;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.eclipse.tracecompass.common.core.NonNullUtils;
32 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
33 import org.eclipse.ui.internal.wizards.datatransfer.DataTransferMessages;
34 import org.eclipse.ui.internal.wizards.datatransfer.ILeveledImportStructureProvider;
35
36 /**
37 * This class provides information regarding the context structure and content
38 * of specified zip file entry objects.
39 *
40 * This structure provider also makes sure to return safe paths. For example,
41 * if a Zip entry contains a ':' and is extracted on Windows, it will be changed
42 * to a '_'
43 */
44 @SuppressWarnings("restriction")
45 public class ZipLeveledStructureProvider implements
46 ILeveledImportStructureProvider {
47 private ZipFile zipFile;
48
49 private ZipArchiveEntry root = new ZipArchiveEntry("/");//$NON-NLS-1$
50
51 private Map<ZipArchiveEntry, List<ZipArchiveEntry>> children;
52
53 private Map<IPath, ZipArchiveEntry> directoryEntryCache = new HashMap<>();
54
55 private int stripLevel;
56
57 /**
58 * Creates a <code>ZipFileStructureProvider</code>, which will operate on
59 * the passed zip file.
60 *
61 * @param sourceFile
62 * The source file to create the ZipLeveledStructureProvider
63 * around
64 */
65 public ZipLeveledStructureProvider(ZipFile sourceFile) {
66 super();
67 zipFile = sourceFile;
68 stripLevel = 0;
69 }
70
71 /**
72 * Creates a new container zip entry with the specified name, iff it has
73 * not already been created. If the parent of the given element does not
74 * already exist it will be recursively created as well.
75 * @param pathname The path representing the container
76 * @return The element represented by this pathname (it may have already existed)
77 */
78 protected ZipArchiveEntry createContainer(IPath pathname) {
79 ZipArchiveEntry existingEntry = directoryEntryCache.get(pathname);
80 if (existingEntry != null) {
81 return existingEntry;
82 }
83
84 ZipArchiveEntry parent;
85 if (pathname.segmentCount() == 0) {
86 return null;
87 } else if (pathname.segmentCount() == 1) {
88 parent = root;
89 } else {
90 parent = createContainer(pathname.removeLastSegments(1));
91 }
92 ZipArchiveEntry newEntry = new ZipArchiveEntry(pathname.toString());
93 directoryEntryCache.put(pathname, newEntry);
94 List<ZipArchiveEntry> childList = new ArrayList<>();
95 children.put(newEntry, childList);
96
97 List<ZipArchiveEntry> parentChildList = children.get(parent);
98 NonNullUtils.checkNotNull(parentChildList).add(newEntry);
99 return newEntry;
100 }
101
102 /**
103 * Creates a new file zip entry with the specified name.
104 * @param entry the entry to create the file for
105 */
106 protected void createFile(ZipArchiveEntry entry) {
107 IPath pathname = new Path(entry.getName());
108 ZipArchiveEntry parent;
109 if (pathname.segmentCount() == 1) {
110 parent = root;
111 } else {
112 parent = directoryEntryCache.get(pathname
113 .removeLastSegments(1));
114 }
115
116 @Nullable List<ZipArchiveEntry> childList = children.get(parent);
117 NonNullUtils.checkNotNull(childList).add(entry);
118 }
119
120 @Override
121 public List getChildren(Object element) {
122 if (children == null) {
123 initialize();
124 }
125
126 return (children.get(element));
127 }
128
129 @Override
130 public InputStream getContents(Object element) {
131 try {
132 return zipFile.getInputStream((ZipArchiveEntry) element);
133 } catch (IOException e) {
134 IDEWorkbenchPlugin.log(e.getLocalizedMessage(), e);
135 return null;
136 }
137 }
138
139 /*
140 * Strip the leading directories from the path
141 */
142 private String stripPath(String path) {
143 String strippedPath = path;
144 String pathOrig = strippedPath;
145 for (int i = 0; i < stripLevel; i++) {
146 int firstSep = strippedPath.indexOf('/');
147 // If the first character was a separator we must strip to the next
148 // separator as well
149 if (firstSep == 0) {
150 strippedPath = strippedPath.substring(1);
151 firstSep = strippedPath.indexOf('/');
152 }
153 // No separator was present so we're in a higher directory right
154 // now
155 if (firstSep == -1) {
156 return pathOrig;
157 }
158 strippedPath = strippedPath.substring(firstSep);
159 }
160 return strippedPath;
161 }
162
163 @Override
164 public String getFullPath(Object element) {
165 String name = ((ZipArchiveEntry) element).getName();
166 return ArchiveUtil.toValidNamesPath(name).toOSString();
167 }
168
169 @Override
170 public String getLabel(Object element) {
171 if (element.equals(getRoot())) {
172 return ((ZipArchiveEntry) element).getName();
173 }
174 String name = ((ZipArchiveEntry) element).getName();
175 return stripPath(ArchiveUtil.toValidNamesPath(name).lastSegment());
176 }
177
178 /**
179 * Returns the entry that this importer uses as the root sentinel.
180 *
181 * @return ZipArchiveEntry
182 */
183 @Override
184 public Object getRoot() {
185 return root;
186 }
187
188 /**
189 * Returns the zip file that this provider provides structure for.
190 *
191 * @return The zip file
192 */
193 public ZipFile getZipFile() {
194 return zipFile;
195 }
196
197
198 @Override
199 public boolean closeArchive(){
200 try {
201 getZipFile().close();
202 } catch (IOException e) {
203 IDEWorkbenchPlugin.log(DataTransferMessages.ZipImport_couldNotClose
204 + getZipFile(), e);
205 return false;
206 }
207 return true;
208 }
209
210 /**
211 * Initializes this object's children table based on the contents of the
212 * specified source file.
213 */
214 protected void initialize() {
215 children = new HashMap<>(1000);
216
217 children.put(root, new ArrayList<>());
218 Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
219 while (entries.hasMoreElements()) {
220 ZipArchiveEntry entry = entries.nextElement();
221 IPath path = new Path(entry.getName()).addTrailingSeparator();
222
223 if (entry.isDirectory()) {
224 createContainer(path);
225 } else
226 {
227 // Ensure the container structure for all levels above this is initialized
228 // Once we hit a higher-level container that's already added we need go no further
229 int pathSegmentCount = path.segmentCount();
230 if (pathSegmentCount > 1) {
231 createContainer(path.uptoSegment(pathSegmentCount - 1));
232 }
233 createFile(entry);
234 }
235 }
236 }
237
238 @Override
239 public boolean isFolder(Object element) {
240 return ((ZipArchiveEntry) element).isDirectory();
241 }
242
243 @Override
244 public void setStrip(int level) {
245 stripLevel = level;
246 }
247
248 @Override
249 public int getStrip() {
250 return stripLevel;
251 }
252 }
This page took 0.03708 seconds and 5 git commands to generate.