diff --git a/resources/bundles/org.eclipse.core.filesystem/plugin.xml b/resources/bundles/org.eclipse.core.filesystem/plugin.xml index a91ee65ed6d..de665c388f7 100644 --- a/resources/bundles/org.eclipse.core.filesystem/plugin.xml +++ b/resources/bundles/org.eclipse.core.filesystem/plugin.xml @@ -16,4 +16,11 @@ + + + + + diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/filesystem/ZipFileUtil.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/filesystem/ZipFileUtil.java new file mode 100644 index 00000000000..e87ca9b7140 --- /dev/null +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/filesystem/ZipFileUtil.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.filesystem; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.zip.ZipInputStream; +import org.eclipse.core.internal.filesystem.zip.ZipFileStore; +import org.eclipse.core.runtime.CoreException; + +/** + * Utility class for zip files. + * + * @since 1.11 + */ +public class ZipFileUtil { + + + public static boolean isInsideOpenZipFile(IFileStore store) { + return store instanceof ZipFileStore; + } + + public static boolean isInsideOpenZipFile(URI locationURI) { + IFileStore store; + try { + if (locationURI != null) { + store = EFS.getStore(locationURI); + } else { + return false; + } + } catch (CoreException e) { + return false; + } + return isInsideOpenZipFile(store); + } + + /** + * Determines if the given {@link IFileStore} represents an open ZIP file. + * This can be used to check if operations on a ZIP file should be allowed or handled differently. + * + * @param store The file store to check. + * @return true if the store is an instance of {@link ZipFileStore}, false otherwise. + */ + public static boolean isOpenZipFile(IFileStore store) { + if (isInsideOpenZipFile(store)) { + ZipFileStore zipStore = (ZipFileStore) store; + return zipStore.getPath().isEmpty(); //if path is empty its the root + } + return false; + } + + /** + * @see ZipFileUtil#isOpenZipFile(IFileStore) + */ + public static boolean isOpenZipFile(URI locationURI) { + IFileStore store; + try { + store = EFS.getStore(locationURI); + } catch (CoreException e) { + return false; + } + return isOpenZipFile(store); + } + + public static boolean isNested(URI fileURI) { + if (fileURI.getScheme().contains("zip")) { //$NON-NLS-1$ + return true; + } + return false; + } + + /** + * Checks if the provided {@link InputStream} represents a ZIP archive + * by attempting to open it as a ZIP archive. + * This method throws {@link IOException} if the stream does not represent a valid ZIP archive. + * + * @param fis The {@link InputStream} of the file to check. + * @throws IOException If the stream does not represent a valid ZIP archive + * or an I/O error occurs during reading from the stream. + */ + public static void canZipFileBeOpened(InputStream fis) throws IOException { + // Use ZipInputStream to try reading the InputStream as a ZIP file + try (ZipInputStream zipStream = new ZipInputStream(fis)) { + // Attempt to read the first entry from the zip stream + if (zipStream.getNextEntry() == null) { + // If there are no entries, then it might not be a ZIP file or it's empty + throw new IOException(); + } + // Successfully reading an entry implies it's likely a valid ZIP file + } + } +} diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipEntryFileVisitor.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipEntryFileVisitor.java new file mode 100644 index 00000000000..2211352acd0 --- /dev/null +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipEntryFileVisitor.java @@ -0,0 +1,49 @@ +package org.eclipse.core.internal.filesystem.zip; + +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; + +public class ZipEntryFileVisitor extends SimpleFileVisitor { + private final Path zipRoot; + private final List entryList; + + public ZipEntryFileVisitor(Path zipRoot) { + this.zipRoot = zipRoot; + this.entryList = new ArrayList<>(); + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + String entryName = zipRoot.relativize(file).toString(); + if (!Files.isDirectory(file)) { + ZipEntry zipEntry = new ZipEntry(entryName); + zipEntry.setSize(attrs.size()); + zipEntry.setTime(attrs.lastModifiedTime().toMillis()); + zipEntry.setMethod(ZipEntry.DEFLATED); + entryList.add(zipEntry); + } else { + entryList.add(new ZipEntry(entryName + "/")); //$NON-NLS-1$ + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + if (!dir.equals(zipRoot)) { + String dirName = zipRoot.relativize(dir).toString() + "/"; //$NON-NLS-1$ + entryList.add(new ZipEntry(dirName)); + return FileVisitResult.SKIP_SUBTREE; // Skip the subdirectories + } + return FileVisitResult.CONTINUE; + } + + public List getEntries() { + return entryList; + } +} diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileStore.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileStore.java new file mode 100644 index 00000000000..7bf55c54a76 --- /dev/null +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileStore.java @@ -0,0 +1,527 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.internal.filesystem.zip; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileInfo; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.filesystem.provider.FileInfo; +import org.eclipse.core.filesystem.provider.FileStore; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.FrameworkUtil; + +/** + * File store implementation representing a file or directory inside a zip file. + * @since 1.11 + */ +public class ZipFileStore extends FileStore { + + /** + * A thread-safe map that associates each zip file's URI with a corresponding {@link ReentrantLock}. + *

+ * This map is used to ensure that each zip file is accessed by only one thread at a time, preventing + * concurrent access issues. The keys in the map are {@link URI} objects representing the zip files, and + * the values are {@link ReentrantLock} objects that are used to control access to the corresponding zip file. + * The map itself is wrapped with {@link Collections#synchronizedMap(Map)} to ensure thread safety + * when accessing the map. + *

+ */ + private static final Map uriLockMap = Collections.synchronizedMap(new HashMap<>()); + + /** + * The path of this store within the zip file. + */ + private final IPath path; + + /** + * The file store that represents the actual zip file. + */ + private final IFileStore rootStore; + + public ZipFileStore(IFileStore rootStore, IPath path) { + this.rootStore = rootStore; + this.path = path.makeRelative(); + } + + private ZipEntry[] childEntries(IProgressMonitor monitor) throws CoreException { + try (FileSystem zipFs = openZipFileSystem()) { + Path zipRoot = zipFs.getPath(path.toString()); + ZipEntryFileVisitor visitor = new ZipEntryFileVisitor(zipRoot); + Files.walkFileTree(zipRoot, visitor); + return visitor.getEntries().toArray(new ZipEntry[0]); + } catch (IOException | URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error reading ZIP file", e)); //$NON-NLS-1$ + } finally { + unlock(); + } + } + + @Override + public IFileInfo[] childInfos(int options, IProgressMonitor monitor) throws CoreException { + ZipEntry[] entries = childEntries(monitor); + int entryCount = entries.length; + IFileInfo[] infos = new IFileInfo[entryCount]; + for (int i = 0; i < entryCount; i++) { + infos[i] = convertZipEntryToFileInfo(entries[i]); + } + return infos; + } + + @Override + public String[] childNames(int options, IProgressMonitor monitor) throws CoreException { + ZipEntry[] entries = childEntries(monitor); + int entryCount = entries.length; + String[] names = new String[entryCount]; + for (int i = 0; i < entryCount; i++) { + names[i] = computeName(entries[i]); + } + return names; + } + + private static String computeName(ZipEntry entry) { + String name = entry.getName(); + // removes "/" at the end + if (name.endsWith("/")) { //$NON-NLS-1$ + name = name.substring(0, name.length() - 1); + } + + int lastIndex = name.lastIndexOf('/'); + + if (lastIndex != -1) { + return name.substring(lastIndex + 1); + } + //No '/' found + return name; + } + + private IFileInfo convertToIFileInfo(Path zipEntryPath, BasicFileAttributes attrs) { + Path namePath = zipEntryPath.getFileName(); + String name = namePath != null ? namePath.toString() : ""; //$NON-NLS-1$ + FileInfo info = new FileInfo(name); + info.setExists(true); + info.setDirectory(attrs.isDirectory()); + info.setLastModified(attrs.lastModifiedTime().toMillis()); + info.setLength(attrs.size()); + return info; + } + + private static IFileInfo convertZipEntryToFileInfo(ZipEntry entry) { + FileInfo info = new FileInfo(computeName(entry)); + if (entry.isDirectory()) { + info.setLastModified(EFS.NONE); + } else { + info.setLastModified(entry.getTime()); + } + + info.setExists(true); + info.setDirectory(entry.isDirectory()); + info.setLength(entry.getSize()); + return info; + } + + @Override + protected void copyDirectory(IFileInfo sourceInfo, IFileStore destination, int options, IProgressMonitor monitor) throws CoreException { + if (!(destination instanceof ZipFileStore)) { + super.copyDirectory(sourceInfo, destination, options, monitor); + return; + } + + if (!sourceInfo.isDirectory()) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Source is not a directory")); //$NON-NLS-1$ + } + + try (FileSystem zipFs = openZipFileSystem()) { + Path sourceDir = zipFs.getPath(this.path.toString()); + FileSystem destFs = ((ZipFileStore) destination).openZipFileSystem(); + Path destDir = destFs.getPath(((ZipFileStore) destination).path.toString()); + + // Use Files.walk to iterate over each entry in the directory + Files.walk(sourceDir).forEach(sourcePath -> { + try { + Path destPath = destDir.resolve(sourceDir.relativize(sourcePath)); + if (Files.isDirectory(sourcePath)) { + Files.createDirectories(destPath); + } else { + Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + throw new RuntimeException("Error copying directory contents", e); //$NON-NLS-1$ + } + }); + } catch (IOException | URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error copying directory within ZIP", e)); //$NON-NLS-1$ + } finally { + unlock(); + } + } + + @Override + protected void copyFile(IFileInfo sourceInfo, IFileStore destination, int options, IProgressMonitor monitor) throws CoreException { + if (!(destination instanceof ZipFileStore)) { + super.copyFile(sourceInfo, destination, options, monitor); + return; + } + + if (sourceInfo.isDirectory()) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Source is a directory, not a file")); //$NON-NLS-1$ + } + + try (FileSystem zipFs = openZipFileSystem()) { + Path sourcePath = zipFs.getPath(this.path.toString()); + FileSystem destFs = ((ZipFileStore) destination).openZipFileSystem(); + Path destPath = destFs.getPath(((ZipFileStore) destination).path.toString()); + + // Copy the file with REPLACE_EXISTING option to overwrite if it already exists + Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException | URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error copying file within ZIP", e)); //$NON-NLS-1$ + } finally { + unlock(); + } + } + + @Override + public void delete(int options, IProgressMonitor monitor) throws CoreException { + Path toDelete = null; + try (FileSystem zipFs = openZipFileSystem()) { + toDelete = zipFs.getPath(path.toString()); + if (Files.exists(toDelete)) { + deleteRecursive(toDelete); + } + } catch (IOException | URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error deleting file from zip: " + toDelete, e)); //$NON-NLS-1$ + } finally { + unlock(); + } + } + + private void deleteRecursive(Path pathToDelete) throws IOException { + if (Files.isDirectory(pathToDelete)) { + // Use try-with-resources to close the directory stream automatically + try (Stream entries = Files.walk(pathToDelete)) { + // We need to sort it in reverse order so directories come after their contents + List sortedPaths = entries.sorted(Comparator.reverseOrder()).collect(Collectors.toList()); + for (Path entry : sortedPaths) { + Files.delete(entry); + } + } + } else { + Files.delete(pathToDelete); + } + } + + @Override + public IFileInfo fetchInfo(int options, IProgressMonitor monitor) throws CoreException { + try (FileSystem zipFs = openZipFileSystem()) { + Path zipEntryPath = zipFs.getPath(path.toString()); + if (Files.exists(zipEntryPath)) { + BasicFileAttributes attrs = Files.readAttributes(zipEntryPath, BasicFileAttributes.class); + return convertToIFileInfo(zipEntryPath, attrs); + } + } catch (IOException | URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error accessing ZIP file", e)); //$NON-NLS-1$ + } finally { + unlock(); + } + + // Correctly set up FileInfo before returning + FileInfo notFoundInfo = new FileInfo(path.lastSegment()); + notFoundInfo.setExists(false); + return notFoundInfo; + } + + /** + * Finds the zip entry with the given name in this zip file. Returns the + * entry and leaves the input stream open positioned at the beginning of the + * bytes of that entry. Returns null if the entry could not be found. + */ + private ZipEntry findEntry(String name, ZipInputStream in) throws IOException { + ZipEntry current; + while ((current = in.getNextEntry()) != null) { + if (current.getName().equals(name)) { + return current; + } + } + return null; + } + + @Override + public IFileStore getChild(String name) { + return new ZipFileStore(rootStore, path.append(name)); + } + + @Override + public String getName() { + String name = path.lastSegment(); + return name == null ? "" : name; //$NON-NLS-1$ + } + + @Override + public IFileStore getParent() { + if (path.segmentCount() > 0) { + return new ZipFileStore(rootStore, path.removeLastSegments(1)); + } + // the root entry has no parent + return null; + } + + private String getPluginId() { + return FrameworkUtil.getBundle(this.getClass()).getSymbolicName(); + } + + /** + * Returns the path of this file store. + */ + public IPath getPath() { + return path; + } + + @Override + public IFileStore mkdir(int options, IProgressMonitor monitor) throws CoreException { + // Assuming the directory to create is represented by 'this.path' + try (FileSystem zipFs = openZipFileSystem()) { + Path dirInZipPath = zipFs.getPath(this.path.toString()); + if (Files.notExists(dirInZipPath)) { + Files.createDirectories(dirInZipPath); + } + } catch (IOException | URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error creating directory in ZIP file", e)); //$NON-NLS-1$ + } finally { + unlock(); + } + + // Return a file store representing the newly created directory. + return new ZipFileStore(rootStore, this.path); + } + + @Override + public void move(IFileStore destination, int options, IProgressMonitor monitor) throws CoreException { + if (!(destination instanceof ZipFileStore)) { + super.move(destination, options, monitor); + return; + } + ZipFileStore destZipFileStore = (ZipFileStore) destination; + + try (FileSystem srcFs = openZipFileSystem(); FileSystem destFs = destZipFileStore.openZipFileSystem()) { + Path srcPath = srcFs.getPath(this.path.toString()); + Path destPath = destFs.getPath(destZipFileStore.path.toString()); + + if (destPath.getParent() != null) { + Files.createDirectories(destPath.getParent()); + } + + if (Files.isDirectory(srcPath)) { + moveDirectory(srcPath, destPath, srcFs, destFs); + } else { + Files.move(srcPath, destPath, StandardCopyOption.COPY_ATTRIBUTES); + } + } catch (IOException | URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error moving entry within ZIP", e)); //$NON-NLS-1$ + } finally { + unlock(); + } + } + + private void moveDirectory(Path srcPath, Path destPath, FileSystem srcFs, FileSystem destFs) throws IOException { + // Ensure the destination directory structure is ready + if (destPath.getParent() != null) { + Files.createDirectories(destPath.getParent()); + } + + // Recursively move the contents + Files.walk(srcPath).forEach(source -> { + try { + Path destination = destPath.resolve(srcPath.relativize(source)); + if (Files.isDirectory(source)) { + if (!Files.exists(destination)) { + Files.createDirectories(destination); + } + } else { + Files.move(source, destination, StandardCopyOption.COPY_ATTRIBUTES); + } + } catch (IOException e) { + throw new RuntimeException("Failed to move files", e); //$NON-NLS-1$ + } + }); + + // Delete the source directory after moving its contents + Files.walk(srcPath).sorted(Comparator.reverseOrder()).forEach(pathToMove -> { + try { + Files.delete(pathToMove); + } catch (IOException e) { + throw new RuntimeException("Failed to delete original files after move", e); //$NON-NLS-1$ + } + }); + } + + @Override + public InputStream openInputStream(int options, IProgressMonitor monitor) throws CoreException { + try { + ZipInputStream in = new ZipInputStream(rootStore.openInputStream(EFS.NONE, monitor)); + ZipEntry entry = findEntry(path.toString(), in); + if (entry == null) { + throw new CoreException(Status.error("File not found: " + rootStore.toString())); //$NON-NLS-1$ + } + if (entry.isDirectory()) { + throw new CoreException(Status.error("Resource is not a file: " + rootStore.toString())); //$NON-NLS-1$ + } + return in; + } catch (IOException e) { + throw new CoreException(Status.error("Could not read file: " + rootStore.toString(), e)); //$NON-NLS-1$ + } + } + + @Override + public OutputStream openOutputStream(int options, IProgressMonitor monitor) { + // Creating a ByteArrayOutputStream to capture the data written to the + // OutputStream + return new ByteArrayOutputStream() { + @Override + public void close() throws IOException { + try (FileSystem zipFs = openZipFileSystem()) { + Path entryPath = zipFs.getPath(path.toString()); + // Ensure parent directories exist + Path parentPath = entryPath.getParent(); + if (parentPath != null) { + Files.createDirectories(parentPath); + } + // Write the ByteArrayOutputStream's data to the entry + // in the ZIP file + Files.write(entryPath, this.toByteArray(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } catch (Exception e) { + throw new IOException("Failed to integrate data into ZIP file", e); //$NON-NLS-1$ + } finally { + try { + unlock(); + } catch (CoreException e) { + throw new IOException("Error accessing ZIP file", e); //$NON-NLS-1$ + } + } + } + }; + } + + private static ReentrantLock getLockForURI(URI uri) { + return uriLockMap.computeIfAbsent(uri, k -> new ReentrantLock()); + } + + private FileSystem openZipFileSystem() throws URISyntaxException, IOException { + Map env = new HashMap<>(); + URI nioURI = toNioURI(); + ReentrantLock lock = getLockForURI(nioURI); + if (!lock.isHeldByCurrentThread()) { + lock.lock(); + } + try { + return FileSystems.getFileSystem(nioURI); + } catch (FileSystemNotFoundException e) { + return FileSystems.newFileSystem(nioURI, env); + } + } + + @Override + public void putInfo(IFileInfo info, int options, IProgressMonitor monitor) throws CoreException { + if (monitor != null) { + monitor.beginTask("Updating Zip Entry Information", 1); //$NON-NLS-1$ + } + try (FileSystem zipFs = openZipFileSystem()) { + Path filePath = zipFs.getPath(path.toString()); + // Check options for what information is requested to be updated + if ((options & EFS.SET_ATTRIBUTES) != 0) { + boolean isHidden = info.getAttribute(EFS.ATTRIBUTE_HIDDEN); + boolean isArchive = info.getAttribute(EFS.ATTRIBUTE_ARCHIVE); + + if (ZipFileSystem.getOS().startsWith("Windows")) { //$NON-NLS-1$ + Files.setAttribute(filePath, "dos:hidden", isHidden); //$NON-NLS-1$ + Files.setAttribute(filePath, "dos:archive", isArchive); //$NON-NLS-1$ + } + } + if ((options & EFS.SET_LAST_MODIFIED) != 0) { + FileTime lastModified = FileTime.fromMillis(info.getLastModified()); + Files.setLastModifiedTime(filePath, lastModified); + } + + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error updating ZIP file entry information", e)); //$NON-NLS-1$ + } finally { + unlock(); + if (monitor != null) { + monitor.done(); + } + } + } + + @Override + public URI toURI() { + String scheme = ZipFileSystem.SCHEME_ZIP; + String pathString = path.makeAbsolute().toString(); + URI rootStoreURI = rootStore.toURI(); + String rootStoreScheme = rootStoreURI.getScheme(); + String rootStorePath = rootStoreURI.getPath(); + String rootStoreQuery = rootStoreScheme + ":" + rootStorePath; //$NON-NLS-1$ + try { + return new URI(scheme, null, pathString, rootStoreQuery, null); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + private URI toNioURI() throws URISyntaxException { + String nioScheme = "jar:"; //$NON-NLS-1$ + String rootPath = rootStore.toURI().toString(); + + String suffix = "!/"; //$NON-NLS-1$ + String ret = nioScheme + rootPath + suffix; + return new URI(ret); + } + + private void unlock() throws CoreException { + try { + ReentrantLock lock = getLockForURI(toNioURI()); + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } catch (URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, getPluginId(), "Error accessing ZIP file", e)); //$NON-NLS-1$ + } + } +} diff --git a/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystem.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystem.java similarity index 79% rename from resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystem.java rename to resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystem.java index 386fdda01ef..b030d13ddc7 100644 --- a/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystem.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystem.java @@ -21,6 +21,9 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; +/** + * @since 1.11 + */ public class ZipFileSystem extends FileSystem { /** * Scheme constant (value "zip") indicating the zip file system scheme. @@ -41,4 +44,22 @@ public IFileStore getStore(URI uri) { } return EFS.getNullFileSystem().getStore(uri); } + + /** + * Returns the current OS. This is equivalent to Platform.getOS(), but + * is tolerant of the platform runtime not being present. + */ + static String getOS() { + return System.getProperty("osgi.os", ""); //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public boolean canDelete() { + return true; + } + + @Override + public boolean canWrite() { + return true; + } } diff --git a/resources/bundles/org.eclipse.core.resources/plugin.properties b/resources/bundles/org.eclipse.core.resources/plugin.properties index 09190635c2e..7e662951051 100644 --- a/resources/bundles/org.eclipse.core.resources/plugin.properties +++ b/resources/bundles/org.eclipse.core.resources/plugin.properties @@ -26,6 +26,7 @@ filterMatchers=Filter Matchers preferencesExtPtName=Resource Preferences resourceModelName=File System Resources variableProviders=Variable Providers +zipfileContentTypeName = Zip File markerName = Marker problemName = Problem diff --git a/resources/bundles/org.eclipse.core.resources/plugin.xml b/resources/bundles/org.eclipse.core.resources/plugin.xml index 74b386a109c..33ad123d686 100644 --- a/resources/bundles/org.eclipse.core.resources/plugin.xml +++ b/resources/bundles/org.eclipse.core.resources/plugin.xml @@ -41,6 +41,15 @@ id="preferences" base-type="org.eclipse.core.runtime.properties"/> + + + + + + { + try (InputStream fis = file.getContents()) { + ZipFileUtil.canZipFileBeOpened(fis); + URI zipURI = new URI("zip", null, "/", file.getLocationURI().toString(), null); //$NON-NLS-1$ //$NON-NLS-2$ + IFolder link = file.getParent().getFolder(IPath.fromOSString(file.getName())); + ((Resource) file).deleteResource(false, null); + workspace.broadcastPostChange(); + link.createLink(zipURI, IResource.BACKGROUND_REFRESH, monitor); + project.refreshLocal(IResource.DEPTH_INFINITE, monitor); + } catch (IOException e) { + if (e instanceof ZipException && e.getMessage().equals("encrypted ZIP entry not supported")) { //$NON-NLS-1$ + throw new CoreException(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, + "Opening encrypted ZIP files is not supported: " + file.getName(), e)); //$NON-NLS-1$ + } + throw new CoreException(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, + "The file is either empty or doesn't represent a ZIP file: " + file.getName(), e)); //$NON-NLS-1$ + } catch (CoreException | URISyntaxException e) { + throw new CoreException( + new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, "Zip File could not be opened")); //$NON-NLS-1$ + } + }; + workspace.run(runnable, rule, IWorkspace.AVOID_UPDATE, null); + } +} diff --git a/resources/examples/org.eclipse.ui.examples.filesystem/plugin.xml b/resources/examples/org.eclipse.ui.examples.filesystem/plugin.xml index f39770e3dc9..d90588a4312 100644 --- a/resources/examples/org.eclipse.ui.examples.filesystem/plugin.xml +++ b/resources/examples/org.eclipse.ui.examples.filesystem/plugin.xml @@ -1,31 +1,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - entries = new HashMap<>(); - String myName = path.toString(); - try (ZipInputStream in = new ZipInputStream(rootStore.openInputStream(EFS.NONE, monitor))) { - ZipEntry current; - while ((current = in.getNextEntry()) != null) { - final String currentPath = current.getName(); - if (isParent(myName, currentPath)) { - entries.put(currentPath, current); - } else if (isAncestor(myName, currentPath)) { - int myNameLength = myName.length() + 1; - int nameEnd = currentPath.indexOf('/', myNameLength); - String dirName = nameEnd == -1 ? currentPath : currentPath.substring(0, nameEnd + 1); - if (!entries.containsKey(dirName)) - entries.put(dirName, new ZipEntry(dirName)); - } - } - } catch (IOException e) { - throw new CoreException(Status.error("Could not read file: " + rootStore.toString(), e)); - } - return entries.values().toArray(new ZipEntry[entries.size()]); - } - - @Override - public IFileInfo[] childInfos(int options, IProgressMonitor monitor) throws CoreException { - ZipEntry[] entries = childEntries(monitor); - int entryCount = entries.length; - IFileInfo[] infos = new IFileInfo[entryCount]; - for (int i = 0; i < entryCount; i++) { - infos[i] = convertZipEntryToFileInfo(entries[i]); - } - return infos; - } - - @Override - public String[] childNames(int options, IProgressMonitor monitor) throws CoreException { - ZipEntry[] entries = childEntries(monitor); - int entryCount = entries.length; - String[] names = new String[entryCount]; - for (int i = 0; i < entryCount; i++) { - names[i] = computeName(entries[i]); - } - return names; - } - - /** - * Computes the simple file name for a given zip entry. - */ - private String computeName(ZipEntry entry) { - //the entry name is a relative path, with an optional trailing separator - //We need to strip off the trailing slash, and then take everything after the - //last separator as the name - String name = entry.getName(); - int end = name.length() - 1; - if (name.charAt(end) == '/') { - end--; - } - return name.substring(name.lastIndexOf('/', end) + 1, end + 1); - } - - /** - * Creates a file info object corresponding to a given zip entry - * - * @param entry the zip entry - * @return The file info for a zip entry - */ - private IFileInfo convertZipEntryToFileInfo(ZipEntry entry) { - FileInfo info = new FileInfo(computeName(entry)); - info.setLastModified(entry.getTime()); - info.setExists(true); - info.setDirectory(entry.isDirectory()); - info.setLength(entry.getSize()); - return info; - } - - @Override - public IFileInfo fetchInfo(int options, IProgressMonitor monitor) throws CoreException { - try (ZipInputStream in = new ZipInputStream(rootStore.openInputStream(EFS.NONE, monitor))) { - String myPath = path.toString(); - ZipEntry current; - while ((current = in.getNextEntry()) != null) { - String currentPath = current.getName(); - if (myPath.equals(currentPath)) { - return convertZipEntryToFileInfo(current); - } - //directories don't always have their own entry, but it is implied by the existence of a child - if (isAncestor(myPath, currentPath)) { - return createDirectoryInfo(getName()); - } - } - } catch (IOException e) { - throw new CoreException(Status.error("Could not read file: " + rootStore.toString(), e)); - } - //does not exist - return new FileInfo(getName()); - } - - /** - * @return A directory info for this file store - */ - private IFileInfo createDirectoryInfo(String name) { - FileInfo result = new FileInfo(name); - result.setExists(true); - result.setDirectory(true); - return result; - } - - /** - * Finds the zip entry with the given name in this zip file. Returns the - * entry and leaves the input stream open positioned at the beginning of - * the bytes of that entry. Returns null if the entry could not be found. - */ - private ZipEntry findEntry(String name, ZipInputStream in) throws IOException { - ZipEntry current; - while ((current = in.getNextEntry()) != null) { - if (current.getName().equals(name)) { - return current; - } - } - return null; - } - - @Override - public IFileStore getChild(String name) { - return new ZipFileStore(rootStore, path.append(name)); - } - - @Override - public String getName() { - String name = path.lastSegment(); - return name == null ? "" : name; //$NON-NLS-1$ - } - - @Override - public IFileStore getParent() { - if (path.segmentCount() > 0) { - return new ZipFileStore(rootStore, path.removeLastSegments(1)); - } - //the root entry has no parent - return null; - } - - /** - * Returns whether ancestor is a parent of child. - * @param ancestor the potential ancestor - * @param child the potential child - * @return true or false - */ - private boolean isAncestor(String ancestor, String child) { - //children will start with myName and have no child path - int ancestorLength = ancestor.length(); - if (ancestorLength == 0) { - return true; - } - return child.startsWith(ancestor) && child.length() > ancestorLength && child.charAt(ancestorLength) == '/'; - } - - /** - * Returns whether parent is the immediate parent of child. - * @param parent the potential parent - * @param child the potential child - * @return true or false - */ - private boolean isParent(String parent, String child) { - //children will start with myName and have no child path - int chop = parent.length() + 1; - return child.startsWith(parent) && child.length() > chop && child.substring(chop).indexOf('/') == -1; - } - - @Override - public InputStream openInputStream(int options, IProgressMonitor monitor) throws CoreException { - try (ZipInputStream in = new ZipInputStream(rootStore.openInputStream(EFS.NONE, monitor))) { - ZipEntry entry = findEntry(path.toString(), in); - if (entry == null) { - throw new CoreException(Status.error("File not found: " + rootStore.toString())); - } - if (entry.isDirectory()) { - throw new CoreException(Status.error("Resource is not a file: " + rootStore.toString())); - } - return in; - } catch (IOException e) { - throw new CoreException(Status.error("Could not read file: " + rootStore.toString(), e)); - } - } - - @Override - public URI toURI() { - try { - return new URI(ZipFileSystem.SCHEME_ZIP, null, path.makeAbsolute().toString(), rootStore.toURI().toString(), null); - } catch (URISyntaxException e) { - //should not happen - throw new RuntimeException(e); - } - } -} diff --git a/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystemContributor.java b/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystemContributor.java deleted file mode 100644 index d8516a8b60a..00000000000 --- a/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/core/internal/filesystem/zip/ZipFileSystemContributor.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ - -package org.eclipse.core.internal.filesystem.zip; - -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; - -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.ide.fileSystem.FileSystemContributor; - -/** - * ZipFileSystemContributor is the zip example of a file system contributor. - */ -public class ZipFileSystemContributor extends FileSystemContributor { - - public ZipFileSystemContributor() { - super(); - } - - @Override - public URI getURI(String pathString) { - try { - if (pathString.startsWith(ZipFileSystem.SCHEME_ZIP)) - return new URI(pathString); - } catch (URISyntaxException e1) { - return null; - } - if (File.separatorChar != '/') - pathString = pathString.replace(File.separatorChar, '/'); - final int length = pathString.length(); - StringBuffer pathBuf = new StringBuffer(length + 1); - pathBuf.append("file:"); //$NON-NLS-1$ - // There must be a leading slash in a hierarchical URI - if (length > 0 && (pathString.charAt(0) != '/')) - pathBuf.append('/'); - // additional double-slash for UNC paths to distinguish from host - // separator - if (pathString.startsWith("//")) //$NON-NLS-1$ - pathBuf.append('/').append('/'); - pathBuf.append(pathString); - try { - //scheme, host, path, query, fragment - return new URI(ZipFileSystem.SCHEME_ZIP, null, "/", pathBuf.toString(), null); //$NON-NLS-1$ - } catch (URISyntaxException e) { - return null; - } - } - - /* (non-Javadoc) - * @see org.eclipse.ui.ide.fileSystem.FileSystemContributor#browseFileSystem(java.lang.String, org.eclipse.swt.widgets.Shell) - */ - @Override - public URI browseFileSystem(String initialPath, Shell shell) { - - FileDialog dialog = new FileDialog(shell); - - if (initialPath.length() > 0) - dialog.setFilterPath(initialPath); - - dialog.setFilterExtensions(new String[] {"*.zip"});//$NON-NLS-1$ - - String selectedFile = dialog.open(); - if (selectedFile == null) - return null; - return getURI(selectedFile); - } - -} diff --git a/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/ui/examples/filesystem/CollapseZipHandler.java b/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/ui/examples/filesystem/CollapseZipHandler.java deleted file mode 100644 index 9b75ba8bf0d..00000000000 --- a/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/ui/examples/filesystem/CollapseZipHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - * Patrick Ziegler - Migration from a JFace Action to a Command Handler, - * in order to be used with the 'org.eclipse.ui.menus' - * extension point. - *******************************************************************************/ -package org.eclipse.ui.examples.filesystem; - -import java.net.URI; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.core.filesystem.EFS; -import org.eclipse.core.filesystem.IFileStore; -import org.eclipse.core.filesystem.URIUtil; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.IPath; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.handlers.HandlerUtil; - -public class CollapseZipHandler extends AbstractHandler { - - private void collapseZip(IFolder folder, Shell shell) { - try { - URI zipURI = new URI(folder.getLocationURI().getQuery()); - //check if the zip file is physically stored below the folder in the workspace - IFileStore parentStore = EFS.getStore(folder.getParent().getLocationURI()); - URI childURI = parentStore.getChild(folder.getName()).toURI(); - if (URIUtil.equals(zipURI, childURI)) { - //the zip file is in the workspace so just delete the link - // and refresh the parent to create the resource - folder.delete(IResource.NONE, null); - folder.getParent().refreshLocal(IResource.DEPTH_INFINITE, null); - } else { - //otherwise the zip file must be a linked resource - IFile file = folder.getParent().getFile(IPath.fromOSString(folder.getName())); - file.createLink(zipURI, IResource.REPLACE, null); - } - } catch (Exception e) { - MessageDialog.openError(shell, "Error", "Error opening zip file"); - e.printStackTrace(); - } - } - - @Override - public Object execute(ExecutionEvent event) throws ExecutionException { - Shell shell = HandlerUtil.getActiveShell(event); - ISelection selection = HandlerUtil.getCurrentSelection(event); - - if (!(selection instanceof IStructuredSelection)) { - return null; - } - - Object element = ((IStructuredSelection) selection).getFirstElement(); - - if (!(element instanceof IFolder)) { - return null; - } - - collapseZip((IFolder) element, shell); - return null; - } - -} diff --git a/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/ui/examples/filesystem/ExpandZipHandler.java b/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/ui/examples/filesystem/ExpandZipHandler.java deleted file mode 100644 index 7fde7131080..00000000000 --- a/resources/examples/org.eclipse.ui.examples.filesystem/src/org/eclipse/ui/examples/filesystem/ExpandZipHandler.java +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - * Patrick Ziegler - Migration from a JFace Action to a Command Handler, - * in order to be used with the 'org.eclipse.ui.menus' - * extension point. - *******************************************************************************/ -package org.eclipse.ui.examples.filesystem; - -import java.net.URI; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.core.internal.filesystem.zip.ZipFileSystem; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.IPath; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.handlers.HandlerUtil; - -public class ExpandZipHandler extends AbstractHandler { - - private void expandZip(IFile file, Shell shell) { - try { - URI zipURI = new URI(ZipFileSystem.SCHEME_ZIP, null, "/", file.getLocationURI().toString(), null); - IFolder link = file.getParent().getFolder(IPath.fromOSString(file.getName())); - link.createLink(zipURI, IResource.REPLACE, null); - } catch (Exception e) { - MessageDialog.openError(shell, "Error", "Error opening zip file"); - e.printStackTrace(); - } - } - - @Override - public Object execute(ExecutionEvent event) throws ExecutionException { - Shell shell = HandlerUtil.getActiveShell(event); - ISelection selection = HandlerUtil.getCurrentSelection(event); - - if (!(selection instanceof IStructuredSelection)) { - return null; - } - - Object element = ((IStructuredSelection) selection).getFirstElement(); - - if (!(element instanceof IFile)) { - return null; - } - - expandZip((IFile) element, shell); - return null; - } - -} diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.jar b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.jar new file mode 100644 index 00000000000..b35136e36b2 Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.jar differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.war b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.war new file mode 100644 index 00000000000..852d10c1a2e Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.war differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.zip b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.zip new file mode 100644 index 00000000000..b33117fa405 Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText.zip differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.jar b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.jar new file mode 100644 index 00000000000..b35136e36b2 Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.jar differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.war b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.war new file mode 100644 index 00000000000..852d10c1a2e Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.war differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.zip b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.zip new file mode 100644 index 00000000000..b33117fa405 Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/BasicText2.zip differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/DeepNested.zip b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/DeepNested.zip new file mode 100644 index 00000000000..4a65ed604fe Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/DeepNested.zip differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/Empty.zip b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/Empty.zip new file mode 100644 index 00000000000..15cb0ecb3e2 Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/Empty.zip differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/Fake.zip b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/Fake.zip new file mode 100644 index 00000000000..945948d8236 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/Fake.zip @@ -0,0 +1 @@ +THIS IS NOT A ZIP! \ No newline at end of file diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/NestedZipFileParent.zip b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/NestedZipFileParent.zip new file mode 100644 index 00000000000..6af194f1ce7 Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/NestedZipFileParent.zip differ diff --git a/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/PasswordProtected.zip b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/PasswordProtected.zip new file mode 100644 index 00000000000..6030f241b4a Binary files /dev/null and b/resources/tests/org.eclipse.core.tests.resources/resources/ZipFileSystem/PasswordProtected.zip differ diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/AllZipFileSystemTests.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/AllZipFileSystemTests.java new file mode 100644 index 00000000000..f54297acd23 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/AllZipFileSystemTests.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; + +/** + * Class for collecting all test classes that deal with the zip file system API. + */ +@Suite +@SelectClasses({ CloseTest.class, CopyTest.class, CreateTest.class, DeleteTest.class, MoveTest.class, + RenameTest.class, SetupTest.class, OpenTest.class }) +public class AllZipFileSystemTests { +} + diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CloseTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CloseTest.java new file mode 100644 index 00000000000..33de38cb5e5 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CloseTest.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import static org.junit.Assert.assertTrue; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.ZipFileTransformer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class CloseTest { + + @BeforeEach + public void setup() throws Exception { + ZipFileSystemTestSetup.setup(); + } + + @AfterEach + public void teardown() throws Exception { + ZipFileSystemTestSetup.teardown(); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCloseZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + ZipFileSystemTestSetup.ensureExists(openedZipFile); + ZipFileTransformer.closeZipFile(openedZipFile); + IFile zipFile = ZipFileSystemTestSetup.projects.get(0) + .getFile(zipFileName); + // Don't use Utility method ensureDoesNotExist because the fileStore is still + // available after closing. The fileStore is the File itself in the local file + // system that still exists after closing. + assertTrue("folder was not properly deleted: " + openedZipFile, !openedZipFile.exists()); + ZipFileSystemTestSetup.ensureExists(zipFile); + } + + /* + * Test for a bug that breaks the opened zip file underneath the zip file that + * is closing. The zip file underneath converts to a linked file but the local + * file in the project is deleted so the linked file has no target. + */ + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCloseZipFileWithZipFileUnderneath(String zipFileName) throws Exception { + IFolder firstZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + String secondZipFileName = zipFileName.replace(".", "2."); + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), secondZipFileName); + ZipFileTransformer.openZipFile(ZipFileSystemTestSetup.projects.get(0).getFile(secondZipFileName), true); + IFolder secondZipFile = ZipFileSystemTestSetup.projects.get(0).getFolder(secondZipFileName); + + ZipFileTransformer.closeZipFile(firstZipFile); + IFile closedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFile(zipFileName); + // Don't use Utility method ensureDoesNotExist because the fileStore is still + // available after closing. The fileStore is the File itself in the local file + // system that still exists after closing. + assertTrue("folder was not properly deleted: " + firstZipFile, !firstZipFile.exists()); + ZipFileSystemTestSetup.ensureExists(closedZipFile); + ZipFileSystemTestSetup.ensureExists(secondZipFile); + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CopyTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CopyTest.java new file mode 100644 index 00000000000..aa184b3c409 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CopyTest.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import java.io.ByteArrayInputStream; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * + */ +public class CopyTest { + + @BeforeEach + public void setup() throws Exception { + ZipFileSystemTestSetup.setup(); + } + + @AfterEach + public void teardown() throws Exception { + ZipFileSystemTestSetup.teardown(); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCopyZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + ZipFileSystemTestSetup.ensureExists(openedZipFile); + IFolder destinationFolder = ZipFileSystemTestSetup.projects.get(0).getFolder("Folder"); + destinationFolder.create(true, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(destinationFolder); + IFolder copyDestination = ZipFileSystemTestSetup.projects.get(0) + .getFolder("Folder" + "/" + zipFileName); + openedZipFile.copy(copyDestination.getFullPath(), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(copyDestination); + ZipFileSystemTestSetup.ensureExists(openedZipFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCopyFileInsideOfZipFile(String zipFileName) throws Exception { + IFile textFile = ZipFileSystemTestSetup.projects.get(0).getFile( + zipFileName + "/" + ZipFileSystemTestSetup.TEXT_FILE_NAME); + ZipFileSystemTestSetup.ensureExists(textFile); + IFolder destinationFolder = ZipFileSystemTestSetup.projects.get(0).getFolder("Folder"); + destinationFolder.create(true, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(destinationFolder); + IFile copyDestination = ZipFileSystemTestSetup.projects.get(0) + .getFile("Folder" + "/" + ZipFileSystemTestSetup.TEXT_FILE_NAME); + textFile.copy(copyDestination.getFullPath(), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(copyDestination); + ZipFileSystemTestSetup.ensureExists(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCopyFolderInsideOfZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + ZipFileSystemTestSetup.ensureExists(openedZipFile); + IFolder newFolder = ZipFileSystemTestSetup.projects.get(0).getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + IFile textFile = newFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + textFile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + IFolder copyDestination = openedZipFile.getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(copyDestination); + newFolder.copy(copyDestination.getFullPath(), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(copyDestination); + ZipFileSystemTestSetup.ensureExists(newFolder); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCopyFileIntoZipFile(String zipFileName) throws Exception { + IFile textFile = ZipFileSystemTestSetup.projects.get(0).getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + textFile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + IFile copyDestination = ZipFileSystemTestSetup.projects.get(0) + .getFile(zipFileName + "/" + "NewFile.txt"); + textFile.copy(copyDestination.getFullPath(), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(copyDestination); + ZipFileSystemTestSetup.ensureExists(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCopyFolderIntoZipFile(String zipFileName) throws Exception { + IFile textFile = ZipFileSystemTestSetup.projects.get(0).getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + textFile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + IFile copyDestination = ZipFileSystemTestSetup.projects.get(0) + .getFile(zipFileName + "/" + "NewFile.txt"); + textFile.copy(copyDestination.getFullPath(), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(copyDestination); + ZipFileSystemTestSetup.ensureExists(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCopyFileFromOutsideOfZipFIleIntoFolderInZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder newFolder = openedZipFile.getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + IFile textFile = ZipFileSystemTestSetup.projects.get(0).getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + textFile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + IFile copyDestination = newFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(copyDestination); + textFile.copy(copyDestination.getFullPath(), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(copyDestination); + ZipFileSystemTestSetup.ensureExists(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCopyFolderIntoFolderInZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder firstNewFolder = openedZipFile.getFolder("FirstNewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(firstNewFolder); + firstNewFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(firstNewFolder); + IFolder secondNewFolder = openedZipFile.getFolder("SecondNewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(secondNewFolder); + secondNewFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(secondNewFolder); + IFile textFile = firstNewFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + textFile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + IFolder copyDestination = secondNewFolder.getFolder("FirstNewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(copyDestination); + firstNewFolder.copy(copyDestination.getFullPath(), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(copyDestination); + ZipFileSystemTestSetup.ensureExists(firstNewFolder); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCopyFileFromOneFolderToOtherFolderInsideofZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder firstNewFolder = openedZipFile.getFolder("FirstNewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(firstNewFolder); + firstNewFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(firstNewFolder); + IFolder secondNewFolder = openedZipFile.getFolder("SecondNewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(secondNewFolder); + secondNewFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(secondNewFolder); + IFile textFile = firstNewFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + textFile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + IFile copyDestination = secondNewFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(copyDestination); + textFile.copy(copyDestination.getFullPath(), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(copyDestination); + ZipFileSystemTestSetup.ensureExists(textFile); + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CreateTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CreateTest.java new file mode 100644 index 00000000000..13e2daeb3e7 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/CreateTest.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import java.io.ByteArrayInputStream; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class CreateTest { + + @BeforeEach + public void setup() throws Exception { + ZipFileSystemTestSetup.setup(); + } + + @AfterEach + public void teardown() throws Exception { + ZipFileSystemTestSetup.teardown(); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCreateFileInsideOfZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFile textFile = openedZipFile.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + textFile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testCreateFolderInsideOfZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder newFolder = openedZipFile.getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/DeleteTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/DeleteTest.java new file mode 100644 index 00000000000..714cd07b41a --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/DeleteTest.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class DeleteTest { + + @BeforeEach + public void setup() throws Exception { + ZipFileSystemTestSetup.setup(); + } + + @AfterEach + public void teardown() throws Exception { + ZipFileSystemTestSetup.teardown(); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testDeleteZipFile(String zipFileName) throws CoreException, IOException { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + ZipFileSystemTestSetup.ensureExists(openedZipFile); + openedZipFile.delete(false, false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(openedZipFile); + IFile zipFile = ZipFileSystemTestSetup.projects.get(0) + .getFile(zipFileName); + ZipFileSystemTestSetup.ensureDoesNotExist(zipFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testDeleteFileInsideOfZipFile(String zipFileName) throws CoreException, IOException { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFile textFile = openedZipFile.getFile(ZipFileSystemTestSetup.TEXT_FILE_NAME); + ZipFileSystemTestSetup.ensureExists(textFile); + textFile.delete(true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testDeleteEmptyFolder(String zipFileName) throws CoreException, IOException { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder folder = openedZipFile.getFolder("FolderToDelete"); + ZipFileSystemTestSetup.ensureDoesNotExist(folder); + folder.create(true, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(folder); + folder.delete(true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(folder); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testDeleteFolderWithChildren(String zipFileName) throws CoreException, IOException { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder folder = openedZipFile.getFolder("FolderToDelete"); + ZipFileSystemTestSetup.ensureDoesNotExist(folder); + folder.create(true, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(folder); + IFile textFile = folder.getFile(ZipFileSystemTestSetup.TEXT_FILE_NAME); + textFile.create(new ByteArrayInputStream("Hello World!".getBytes()), true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + folder.delete(true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(folder); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/MoveTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/MoveTest.java new file mode 100644 index 00000000000..d5236c515e9 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/MoveTest.java @@ -0,0 +1,332 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.ZipFileTransformer; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + + +public class MoveTest { + + @BeforeEach + public void setup() throws Exception { + ZipFileSystemTestSetup.setup(); + } + + @AfterEach + public void teardown() throws Exception { + ZipFileSystemTestSetup.teardown(); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveZipFileWithinProject(String zipFileName) throws CoreException, IOException { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder destinationFolder = ZipFileSystemTestSetup.projects.get(0).getFolder("destinationFolder"); + destinationFolder.create(false, true, new NullProgressMonitor()); + IFolder destination = ZipFileSystemTestSetup.projects.get(0) + .getFolder("destinationFolder/" + zipFileName); + openedZipFile.move(destination.getFullPath(), false, new NullProgressMonitor()); + IFolder newFolder = ZipFileSystemTestSetup.projects.get(0) + .getFolder(destinationFolder.getName() + "/" + zipFileName); + ZipFileSystemTestSetup.ensureExists(newFolder); + ZipFileSystemTestSetup.ensureDoesNotExist(openedZipFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveZipFileToOtherProject(String zipFileName) throws CoreException, IOException { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder destination = ZipFileSystemTestSetup.projects.get(1) + .getFolder(zipFileName); + destination.delete(true, new NullProgressMonitor()); + openedZipFile.move(destination.getFullPath(), false, new NullProgressMonitor()); + IFolder newFolder = ZipFileSystemTestSetup.projects.get(1) + .getFolder(zipFileName); + ZipFileSystemTestSetup.ensureExists(newFolder); + ZipFileSystemTestSetup.ensureDoesNotExist(openedZipFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveZipFileToOtherProjectFolder(String zipFileName) throws CoreException, IOException { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder destinationFolder = ZipFileSystemTestSetup.projects.get(1).getFolder("destinationFolder"); + destinationFolder.create(false, true, new NullProgressMonitor()); + IFolder destination = ZipFileSystemTestSetup.projects.get(1) + .getFolder("destinationFolder/" + zipFileName); + openedZipFile.move(destination.getFullPath(), false, new NullProgressMonitor()); + IFolder newFolder = ZipFileSystemTestSetup.projects.get(1) + .getFolder(destinationFolder.getName() + "/" + zipFileName); + ZipFileSystemTestSetup.ensureExists(newFolder); + ZipFileSystemTestSetup.ensureDoesNotExist(openedZipFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFileIntoZipFile(String zipFileName) throws Exception { + IFile textFile = ZipFileSystemTestSetup.projects.get(0).getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + String text = "Foo"; + InputStream stream = new ByteArrayInputStream(text.getBytes()); + textFile.create(stream, false, new NullProgressMonitor()); + stream.close(); + ZipFileSystemTestSetup.ensureExists(textFile); + IFile destinationFile = ZipFileSystemTestSetup.projects.get(0) + .getFile(zipFileName + "/" + "NewFile.txt"); + textFile.move(destinationFile.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(destinationFile); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFolderIntoZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder destinationFolder = openedZipFile.getFolder("destinationFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(destinationFolder); + destinationFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(destinationFolder); + IFolder newFolder = ZipFileSystemTestSetup.projects.get(0).getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + IFolder newFolderDestination = destinationFolder.getFolder("NewFolder"); + newFolder.move(newFolderDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + ZipFileSystemTestSetup.ensureExists(newFolderDestination); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFolderWithContentIntoZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder destinationFolder = openedZipFile.getFolder("destinationFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(destinationFolder); + destinationFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(destinationFolder); + IFolder newFolder = ZipFileSystemTestSetup.projects.get(0).getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + IFile textFile = newFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + String text = "Foo"; + InputStream stream = new ByteArrayInputStream(text.getBytes()); + textFile.create(stream, false, new NullProgressMonitor()); + stream.close(); + ZipFileSystemTestSetup.ensureExists(textFile); + IFolder newFolderDestination = destinationFolder.getFolder("NewFolder"); + newFolder.move(newFolderDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + ZipFileSystemTestSetup.ensureExists(newFolderDestination); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFileFromZipFile(String zipFileName) throws Exception { + IFile textFile = ZipFileSystemTestSetup.projects.get(0) + .getFile(zipFileName + "/" + + ZipFileSystemTestSetup.TEXT_FILE_NAME); + ZipFileSystemTestSetup.ensureExists(textFile); + IFile destinationFile = ZipFileSystemTestSetup.projects.get(0).getFile(ZipFileSystemTestSetup.TEXT_FILE_NAME); + textFile.move(destinationFile.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(destinationFile); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFolderFromZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder newFolder = openedZipFile.getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + IFolder folderDestination = ZipFileSystemTestSetup.projects.get(0).getFolder("NewFolder"); + newFolder.move(folderDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + ZipFileSystemTestSetup.ensureExists(folderDestination); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFolderWithContentFromZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder newFolder = openedZipFile.getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + IFile textFile = newFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + String text = "Foo"; + InputStream stream = new ByteArrayInputStream(text.getBytes()); + textFile.create(stream, false, new NullProgressMonitor()); + stream.close(); + ZipFileSystemTestSetup.ensureExists(textFile); + IFolder folderDestination = ZipFileSystemTestSetup.projects.get(0).getFolder("NewFolder"); + newFolder.move(folderDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + ZipFileSystemTestSetup.ensureExists(folderDestination); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFolderWithContentFromZipFileIntoOtherZipFile(String zipFileName) throws Exception { + IFolder firstZipFile = ZipFileSystemTestSetup.projects.get(0).getFolder(zipFileName); + String secondZipFileName = zipFileName.replace(".", "2."); + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), secondZipFileName); + ZipFileTransformer.openZipFile(ZipFileSystemTestSetup.projects.get(0).getFile(secondZipFileName), true); + IFolder secondZipFile = ZipFileSystemTestSetup.projects.get(0).getFolder(secondZipFileName); + + IFolder newFolder = firstZipFile.getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + IFile textFile = newFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + String text = "Foo"; + InputStream stream = new ByteArrayInputStream(text.getBytes()); + textFile.create(stream, false, new NullProgressMonitor()); + stream.close(); + ZipFileSystemTestSetup.ensureExists(textFile); + IFolder movedFolderDestination = secondZipFile.getFolder("NewFolder"); + newFolder.move(movedFolderDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + ZipFileSystemTestSetup.ensureExists(movedFolderDestination); + IFile movedTextFile = movedFolderDestination.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureExists(movedTextFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFolderWithContentFromZipFileIntoOtherZipFileTwice(String zipFileName) throws Exception { + IFolder firstZipFile = ZipFileSystemTestSetup.projects.get(0).getFolder(zipFileName); + String secondZipFileName = zipFileName.replace(".", "2."); + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), secondZipFileName); + ZipFileTransformer.openZipFile(ZipFileSystemTestSetup.projects.get(0).getFile(secondZipFileName), true); + IFolder secondZipFile = ZipFileSystemTestSetup.projects.get(0).getFolder(secondZipFileName); + + IFolder newFolder = firstZipFile.getFolder("NewFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + newFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(newFolder); + IFile textFile = newFolder.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + String text = "Foo"; + InputStream stream = new ByteArrayInputStream(text.getBytes()); + textFile.create(stream, false, new NullProgressMonitor()); + stream.close(); + ZipFileSystemTestSetup.ensureExists(textFile); + IFolder movedFolderDestination = secondZipFile.getFolder("NewFolder"); + newFolder.move(movedFolderDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(newFolder); + ZipFileSystemTestSetup.ensureExists(movedFolderDestination); + IFile movedTextFile = movedFolderDestination.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureExists(movedTextFile); + + // Move second time + IFolder originDestination = newFolder; + movedFolderDestination.move(originDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureDoesNotExist(movedFolderDestination); + ZipFileSystemTestSetup.ensureExists(originDestination); + movedTextFile = originDestination.getFile("NewFile.txt"); + ZipFileSystemTestSetup.ensureExists(movedTextFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveFileInsideOfZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder destinationFolder = openedZipFile.getFolder("destinationFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(destinationFolder); + destinationFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(destinationFolder); + IFile textFile = openedZipFile.getFile(ZipFileSystemTestSetup.TEXT_FILE_NAME); + ZipFileSystemTestSetup.ensureExists(textFile); + IFile fileDestination = destinationFolder.getFile(ZipFileSystemTestSetup.TEXT_FILE_NAME); + ZipFileSystemTestSetup.ensureDoesNotExist(fileDestination); + textFile.move(fileDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(fileDestination); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveZipFileIntoZipFile(String zipFileName) throws Exception { + IFolder firstZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + String secondZipFileName = zipFileName.replace(".", "2."); + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), secondZipFileName); + ZipFileTransformer.openZipFile(ZipFileSystemTestSetup.projects.get(0).getFile(secondZipFileName), true); + IFolder secondZipFile = ZipFileSystemTestSetup.projects.get(0).getFolder(secondZipFileName); + + // move second ZipFile into first ZipFile + IFile secondZipFileDestination = firstZipFile.getFile(secondZipFileName); + secondZipFile.move(secondZipFileDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(secondZipFileDestination); + ZipFileSystemTestSetup.ensureDoesNotExist(secondZipFile); + } + + /** + * When moving or expanding an opened zip file that contains a folder with + * content. errors can occur. This is because the local name of the resources + * inside the folder contains "\" seperators that are not allowed when + * refreshing the Workspace. This test checks if this specific error is handeled + * correctly in RefreshLocalVisitor#visit() + */ + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testMoveZipFileWithFolder(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + String contentFolderPath = zipFileName + "/" + "Folder"; + IFolder contentFolder = ZipFileSystemTestSetup.projects.get(0).getFolder(contentFolderPath); + ZipFileSystemTestSetup.ensureDoesNotExist(contentFolder); + contentFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(contentFolder); + String text = "Foo"; + InputStream stream = new ByteArrayInputStream(text.getBytes()); + IFile textFile = ZipFileSystemTestSetup.projects.get(0).getFile(contentFolderPath + "/" + "textFile"); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + textFile.create(stream, false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(textFile); + IFolder destinationFolder = ZipFileSystemTestSetup.projects.get(0).getFolder("destinationFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(destinationFolder); + destinationFolder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(destinationFolder); + IFolder zipFileDestination = ZipFileSystemTestSetup.projects.get(0) + .getFolder("destinationFolder/" + zipFileName); + ZipFileSystemTestSetup.ensureDoesNotExist(zipFileDestination); + openedZipFile.move(zipFileDestination.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(zipFileDestination); + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/OpenTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/OpenTest.java new file mode 100644 index 00000000000..e5cc8f08bf3 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/OpenTest.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.net.URISyntaxException; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ZipFileTransformer; +import org.eclipse.core.runtime.CoreException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class OpenTest { + + @BeforeEach + public void setup() throws Exception { + ZipFileSystemTestSetup.setup(); + } + + @AfterEach + public void teardown() throws Exception { + ZipFileSystemTestSetup.teardown(); + } + + @Test + public void testOpenEmptyZipFile() throws IOException, CoreException, URISyntaxException { + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), + ZipFileSystemTestSetup.EMPTY_ZIP_FILE_NAME); + IProject project = ZipFileSystemTestSetup.projects.get(0); + IFile zipFile = project.getFile(ZipFileSystemTestSetup.EMPTY_ZIP_FILE_NAME); + ZipFileSystemTestSetup.ensureExists(zipFile); + + try { + ZipFileTransformer.openZipFile(zipFile, true); + } catch (CoreException e) { + ZipFileSystemTestSetup.ensureExists(zipFile); + String expectedMessage = "The file is either empty or doesn't represent a ZIP file:"; + assertTrue(e.getMessage().contains(expectedMessage)); + } + } + + @Test + public void testOpenNestedZipFileParent() throws IOException, CoreException, URISyntaxException { + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), + ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + IFile nestedZipFileParent = ZipFileSystemTestSetup.projects.get(0) + .getFile(ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + ZipFileSystemTestSetup.ensureExists(nestedZipFileParent); + ZipFileTransformer.openZipFile(nestedZipFileParent, true); + IFolder openedNestedZipFileParent = ZipFileSystemTestSetup.projects.get(0) + .getFolder(ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + ZipFileSystemTestSetup.ensureExists(openedNestedZipFileParent); + } + + @Test + public void testOpenNestedZipFileChild() throws IOException, CoreException, URISyntaxException { + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), + ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + IFile nestedZipFileParent = ZipFileSystemTestSetup.projects.get(0) + .getFile(ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + ZipFileSystemTestSetup.ensureExists(nestedZipFileParent); + ZipFileTransformer.openZipFile(nestedZipFileParent, true); + IFolder openedNestedZipFileParent = ZipFileSystemTestSetup.projects.get(0) + .getFolder(ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + ZipFileSystemTestSetup.ensureExists(openedNestedZipFileParent); + IFile nestedZipFileChild = openedNestedZipFileParent.getFile(ZipFileSystemTestSetup.NESTED_ZIP_FILE_CHILD_NAME); + ZipFileSystemTestSetup.ensureExists(nestedZipFileChild); + + // Attempt to open the nested ZIP file and expect an exception + try { + ZipFileTransformer.openZipFile(nestedZipFileChild, true); + fail("Expected a CoreException to be thrown when opening a nested ZIP file"); + } catch (CoreException e) { + // Verify that the expected exception was thrown + assertTrue("Expected CoreException to be thrown when opening a nested ZIP file", + e.getMessage().contains("Nested ZIP files are not allowed to be opened")); + + IFolder openedNestedZipFileChild = ZipFileSystemTestSetup.projects.get(0) + .getFolder(ZipFileSystemTestSetup.NESTED_ZIP_FILE_CHILD_NAME); + ZipFileSystemTestSetup.ensureDoesNotExist(openedNestedZipFileChild); + } + } + + @Test + public void testOpenDeepNestedTextFile() throws IOException, CoreException, URISyntaxException { + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), + ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + IFile nestedZipFileParent = ZipFileSystemTestSetup.projects.get(0) + .getFile(ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + ZipFileSystemTestSetup.ensureExists(nestedZipFileParent); + ZipFileTransformer.openZipFile(nestedZipFileParent, true); + IFolder openedNestedZipFileParent = ZipFileSystemTestSetup.projects.get(0) + .getFolder(ZipFileSystemTestSetup.NESTED_ZIP_FILE_PARENT_NAME); + ZipFileSystemTestSetup.ensureExists(openedNestedZipFileParent); + + String nestedPath = "sub1/Text.txt"; + IFile nestedFile = openedNestedZipFileParent.getFile(nestedPath); + ZipFileSystemTestSetup.ensureExists(nestedFile); + + String nestedPathShouldFail = "sub2"; + IFolder nestedFileShouldFail = openedNestedZipFileParent.getFolder(nestedPathShouldFail); + ZipFileSystemTestSetup.ensureDoesNotExist(nestedFileShouldFail); + + String deepNestedPath = "sub1/sub2/sub3/sub4/sub5/sub6/sub8/sub9/sub10/Text.txt"; + IFile deepNestedFile = openedNestedZipFileParent.getFile(deepNestedPath); + ZipFileSystemTestSetup.ensureExists(deepNestedFile); + } + + @Test + public void testOpenFakeZip() { + try { + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), + ZipFileSystemTestSetup.FAKE_ZIP_FILE_NAME); + IFile fakeZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFile(ZipFileSystemTestSetup.FAKE_ZIP_FILE_NAME); + ZipFileSystemTestSetup.ensureExists(fakeZipFile); + + ZipFileTransformer.openZipFile(fakeZipFile, true); + fail("Expected an IOException due to incorrect file header."); + } catch (CoreException e) { + String expectedMessage = "The file is either empty or doesn't represent a ZIP file"; + assertTrue(e.getMessage().contains(expectedMessage)); + } catch (Exception e) { + fail("Expected a CoreException, but got a different type of exception."); + } + } + + @Test + public void testOpenPasswordProtectedZip() { + try { + ZipFileSystemTestSetup.copyZipFileIntoProject(ZipFileSystemTestSetup.projects.get(0), + ZipFileSystemTestSetup.PASSWORD_PROTECTED_ZIP_FILE_NAME); + IFile passwordProtectedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFile(ZipFileSystemTestSetup.PASSWORD_PROTECTED_ZIP_FILE_NAME); + ZipFileSystemTestSetup.ensureExists(passwordProtectedZipFile); + + ZipFileTransformer.openZipFile(passwordProtectedZipFile, true); + fail("Expected an IOException due to password protection."); + } catch (CoreException e) { + String expectedMessage = "Opening encrypted ZIP files is not supported:"; + assertTrue("Expected message: " + expectedMessage, e.getMessage().contains(expectedMessage)); + } catch (Exception e) { + fail("Expected a CoreException, but got a different type of exception."); + } + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/RenameTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/RenameTest.java new file mode 100644 index 00000000000..5a8852e3fe6 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/RenameTest.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class RenameTest { + + @BeforeEach + public void setup() throws Exception { + ZipFileSystemTestSetup.setup(); + } + + @AfterEach + public void teardown() throws Exception { + ZipFileSystemTestSetup.teardown(); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testRenameZipFile(String zipFileName) throws Exception { + // IFolder is renamed by moving with the new path + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder renamedOpenZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName + "Renamed"); + openedZipFile.move(renamedOpenZipFile.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(renamedOpenZipFile); + ZipFileSystemTestSetup.ensureDoesNotExist(openedZipFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testRenameFileInsideOfZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFile textFile = openedZipFile.getFile(ZipFileSystemTestSetup.TEXT_FILE_NAME); + IFile renamedTextFile = openedZipFile.getFile(textFile.getName() + "Renamed"); + textFile.move(renamedTextFile.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(renamedTextFile); + ZipFileSystemTestSetup.ensureDoesNotExist(textFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testRenameFolderInsideOfZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + IFolder folder = openedZipFile.getFolder("newFolder"); + ZipFileSystemTestSetup.ensureDoesNotExist(folder); + folder.create(false, true, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(folder); + IFolder renamedFolder = openedZipFile.getFolder(folder.getName() + "Renamed"); + folder.move(renamedFolder.getFullPath(), false, new NullProgressMonitor()); + ZipFileSystemTestSetup.ensureExists(renamedFolder); + ZipFileSystemTestSetup.ensureDoesNotExist(folder); + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/SetupTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/SetupTest.java new file mode 100644 index 00000000000..30a13b86625 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/SetupTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class SetupTest { + + @BeforeEach + public void setup() throws Exception { + ZipFileSystemTestSetup.setup(); + } + + @AfterEach + public void teardown() throws Exception { + ZipFileSystemTestSetup.teardown(); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testZipFileInProject(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + ZipFileSystemTestSetup.ensureExists(openedZipFile); + } + + @ParameterizedTest + @MethodSource("org.eclipse.core.tests.filesystem.zip.ZipFileSystemTestSetup#zipFileNames") + public void testTextFileInZipFile(String zipFileName) throws Exception { + IFolder openedZipFile = ZipFileSystemTestSetup.projects.get(0) + .getFolder(zipFileName); + + IFile textFile = openedZipFile.getFile(ZipFileSystemTestSetup.TEXT_FILE_NAME); + ZipFileSystemTestSetup.ensureExists(textFile); + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/ZipFileSystemTestSetup.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/ZipFileSystemTestSetup.java new file mode 100644 index 00000000000..15c20fde553 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/zip/ZipFileSystemTestSetup.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.core.tests.filesystem.zip; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.ZipFileTransformer; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; + +class ZipFileSystemTestSetup { + static final String ZIP_FILE_NAME = "BasicText.zip"; + static final String JAR_FILE_NAME = "BasicText.jar"; + static final String WAR_FILE_NAME = "BasicText.war"; + + static final String EMPTY_ZIP_FILE_NAME = "Empty.zip"; + static final String FAKE_ZIP_FILE_NAME = "Fake.zip"; + static final String PASSWORD_PROTECTED_ZIP_FILE_NAME = "PasswordProtected.zip"; + static final String NESTED_ZIP_FILE_PARENT_NAME = "NestedZipFileParent.zip"; + static final String NESTED_ZIP_FILE_CHILD_NAME = "NestedZipFileChild.zip"; + static final String DEEP_NESTED_ZIP_FILE_NAME = "DeepNested.zip"; + + static final String TEXT_FILE_NAME = "Text.txt"; + + static List projects = new ArrayList<>(); + static List zipFileNames = List.of(ZIP_FILE_NAME, JAR_FILE_NAME, WAR_FILE_NAME); + + public static Stream zipFileNames() { + return zipFileNames.stream(); + } + + static void setup() throws Exception { + for (int i = 0; i <= 1; i++) { + projects.add(createProject("Project" + i)); + for (String zipFileName : zipFileNames) { + copyZipFileIntoProject(projects.get(i), zipFileName); + ZipFileTransformer.openZipFile(projects.get(i).getFile(zipFileName), true); + } + } + } + + static void teardown() throws Exception { + deleteProjects(); + } + + private static void deleteProjects() throws CoreException { + for (IProject project : projects) { + if (project != null && project.exists()) { + project.delete(true, true, new NullProgressMonitor()); + project = null; + } + } + } + + static IProject createProject(String projectName) throws CoreException { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IProject project = workspace.getRoot().getProject(projectName); + + if (!project.exists()) { + project.create(new NullProgressMonitor()); + } + project.open(new NullProgressMonitor()); + return project; + } + + static void copyZipFileIntoProject(IProject project, String zipFileName) throws IOException, CoreException { + try { + URL zipFileUrl = Platform.getBundle("org.eclipse.core.tests.resources") + .getEntry("resources/ZipFileSystem/" + zipFileName); + Path sourcePath = Paths.get(FileLocator.resolve(zipFileUrl).toURI()); + Path targetPath = project.getLocation().append(zipFileName).toFile().toPath(); + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); + project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + } catch (URISyntaxException e) { + throw new IOException("Failed to resolve URI for the ZIP file", e); + } + } + + static void ensureExistence(IResource resource, boolean shouldExist) throws CoreException, IOException { + IFileStore fileStore = EFS.getStore(resource.getLocationURI()); + boolean fileStoreExists = fileStore.fetchInfo().exists(); + assertTrue("File store existence check failed for: " + fileStore, fileStoreExists == shouldExist); + + if (resource instanceof IFile file) { + assertTrue("File existence check failed for: " + file, file.exists() == shouldExist); + } else if (resource instanceof IFolder folder) { + assertTrue("Folder existence check failed for: " + folder, folder.exists() == shouldExist); + } + } + + static void ensureExists(IResource resource) throws CoreException, IOException { + ensureExistence(resource, true); + } + + static void ensureDoesNotExist(IResource resource) throws CoreException, IOException { + ensureExistence(resource, false); + } +} diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/AutomatedResourceTests.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/AutomatedResourceTests.java index 38d3f7545fe..3c899db1ab2 100644 --- a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/AutomatedResourceTests.java +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/AutomatedResourceTests.java @@ -23,6 +23,7 @@ @Suite @SelectClasses({ // org.eclipse.core.tests.filesystem.AllFileSystemTests.class, // + org.eclipse.core.tests.filesystem.zip.AllZipFileSystemTests.class, // org.eclipse.core.tests.internal.alias.AllAliasTests.class, // org.eclipse.core.tests.internal.builders.AllBuilderTests.class, // org.eclipse.core.tests.internal.dtree.AllDtreeTests.class, // diff --git a/runtime/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/ZipFileContentDescriber.java b/runtime/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/ZipFileContentDescriber.java new file mode 100644 index 00000000000..ff1e9f88eee --- /dev/null +++ b/runtime/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/ZipFileContentDescriber.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License 2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.runtime.content; + +import java.io.IOException; +import java.io.InputStream; +import org.eclipse.core.internal.content.TextContentDescriber; + +/** + * @since 3.10 + */ +public class ZipFileContentDescriber extends TextContentDescriber { + + @Override + public int describe(InputStream contents, IContentDescription description) throws IOException { + + IContentType type = description.getContentType(); + if (type == null || description == null) + return VALID; + + if (type.getId().equals("zipfile")) { //$NON-NLS-1$ + return VALID; + } + return INVALID; + } +}