| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.util.io; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.io.*; |
| import java.util.Enumeration; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| import java.util.zip.ZipInputStream; |
| import java.util.zip.ZipOutputStream; |
| |
| public class ZipUtil { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.util.io.ZipUtil"); |
| |
| private ZipUtil() {} |
| |
| public interface FileContentProcessor { |
| |
| FileContentProcessor STANDARD = new FileContentProcessor() { |
| @Override |
| public InputStream getContent(File file) throws IOException { |
| return new FileInputStream(file); |
| } |
| }; |
| |
| InputStream getContent(File file) throws IOException; |
| } |
| |
| public static boolean addFileToZip(@NotNull ZipOutputStream zos, |
| @NotNull File file, |
| @NotNull String relativeName, |
| @Nullable Set<String> writtenItemRelativePaths, |
| @Nullable FileFilter fileFilter) throws IOException { |
| return addFileToZip(zos, file, relativeName, writtenItemRelativePaths, fileFilter, FileContentProcessor.STANDARD); |
| } |
| |
| /* |
| * Adds a new file entry to the ZIP output stream. |
| */ |
| public static boolean addFileToZip(@NotNull ZipOutputStream zos, |
| @NotNull File file, |
| @NotNull String relativeName, |
| @Nullable Set<String> writtenItemRelativePaths, |
| @Nullable FileFilter fileFilter, |
| @NotNull FileContentProcessor contentProcessor) throws IOException { |
| while (!relativeName.isEmpty() && relativeName.charAt(0) == '/') { |
| relativeName = relativeName.substring(1); |
| } |
| |
| boolean isDir = file.isDirectory(); |
| if (isDir && !StringUtil.endsWithChar(relativeName, '/')) { |
| relativeName += "/"; |
| } |
| if (fileFilter != null && !FileUtil.isFilePathAcceptable(file, fileFilter)) return false; |
| if (writtenItemRelativePaths != null && !writtenItemRelativePaths.add(relativeName)) return false; |
| |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Add "+file+" as "+relativeName); |
| } |
| |
| long size = isDir ? 0 : file.length(); |
| ZipEntry e = new ZipEntry(relativeName); |
| e.setTime(file.lastModified()); |
| if (size == 0) { |
| e.setMethod(ZipEntry.STORED); |
| e.setSize(0); |
| e.setCrc(0); |
| } |
| zos.putNextEntry(e); |
| if (!isDir) { |
| InputStream is = contentProcessor.getContent(file); |
| try { |
| FileUtil.copy(is, zos); |
| } |
| finally { |
| is.close(); |
| } |
| } |
| zos.closeEntry(); |
| return true; |
| } |
| |
| public static boolean addFileOrDirRecursively(@NotNull ZipOutputStream jarOutputStream, |
| @Nullable File jarFile, |
| @NotNull File file, |
| @NotNull String relativePath, |
| @Nullable FileFilter fileFilter, |
| @Nullable Set<String> writtenItemRelativePaths) throws IOException { |
| if (file.isDirectory()) { |
| return addDirToZipRecursively(jarOutputStream, jarFile, file, relativePath, fileFilter, writtenItemRelativePaths); |
| } |
| addFileToZip(jarOutputStream, file, relativePath, writtenItemRelativePaths, fileFilter); |
| return true; |
| } |
| |
| public static boolean addDirToZipRecursively(@NotNull ZipOutputStream outputStream, |
| @Nullable File jarFile, |
| @NotNull File dir, |
| @NotNull String relativePath, |
| @Nullable FileFilter fileFilter, |
| @Nullable Set<String> writtenItemRelativePaths) throws IOException { |
| if (jarFile != null && FileUtil.isAncestor(dir, jarFile, false)) { |
| return false; |
| } |
| if (!relativePath.isEmpty()) { |
| addFileToZip(outputStream, dir, relativePath, writtenItemRelativePaths, fileFilter); |
| } |
| final File[] children = dir.listFiles(); |
| if (children != null) { |
| for (File child : children) { |
| final String childRelativePath = (relativePath.isEmpty() ? "" : relativePath + "/") + child.getName(); |
| addFileOrDirRecursively(outputStream, jarFile, child, childRelativePath, fileFilter, writtenItemRelativePaths); |
| } |
| } |
| return true; |
| } |
| |
| public static void extract(@NotNull File file, @NotNull File outputDir, @Nullable FilenameFilter filenameFilter) throws IOException { |
| extract(file, outputDir, filenameFilter, true); |
| } |
| |
| public static void extract(@NotNull File file, @NotNull File outputDir, @Nullable FilenameFilter filenameFilter, boolean overwrite) throws IOException { |
| final ZipFile zipFile = new ZipFile(file); |
| try { |
| extract(zipFile, outputDir, filenameFilter, overwrite); |
| } |
| finally { |
| zipFile.close(); |
| } |
| } |
| |
| public static void extract(@NotNull final ZipFile zipFile, |
| @NotNull File outputDir, |
| @Nullable FilenameFilter filenameFilter) throws IOException { |
| extract(zipFile, outputDir, filenameFilter, true); |
| } |
| |
| public static void extract(@NotNull final ZipFile zipFile, |
| @NotNull File outputDir, |
| @Nullable FilenameFilter filenameFilter, |
| boolean overwrite) throws IOException { |
| final Enumeration entries = zipFile.entries(); |
| while (entries.hasMoreElements()) { |
| ZipEntry entry = (ZipEntry)entries.nextElement(); |
| final File file = new File(outputDir, entry.getName()); |
| if (filenameFilter == null || filenameFilter.accept(file.getParentFile(), file.getName())) { |
| extractEntry(entry, zipFile.getInputStream(entry), outputDir, overwrite); |
| } |
| } |
| } |
| |
| public static void extractEntry(ZipEntry entry, final InputStream inputStream, File outputDir) throws IOException { |
| extractEntry(entry, inputStream, outputDir, true); |
| } |
| |
| public static void extractEntry(ZipEntry entry, final InputStream inputStream, File outputDir, boolean overwrite) throws IOException { |
| final boolean isDirectory = entry.isDirectory(); |
| final String relativeName = entry.getName(); |
| final File file = new File(outputDir, relativeName); |
| if (file.exists() && !overwrite) return; |
| |
| FileUtil.createParentDirs(file); |
| if (isDirectory) { |
| file.mkdir(); |
| } |
| else { |
| final BufferedInputStream is = new BufferedInputStream(inputStream); |
| final BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file)); |
| try { |
| FileUtil.copy(is, os); |
| } |
| finally { |
| os.close(); |
| is.close(); |
| } |
| } |
| } |
| |
| public static boolean isZipContainsFolder(File zip) throws IOException { |
| ZipFile zipFile = new ZipFile(zip); |
| try { |
| Enumeration en = zipFile.entries(); |
| |
| while (en.hasMoreElements()) { |
| ZipEntry zipEntry = (ZipEntry)en.nextElement(); |
| |
| // we do not necessarily get a separate entry for the subdirectory when the file |
| // in the ZIP archive is placed in a subdirectory, so we need to check if the slash |
| // is found anywhere in the path |
| if (zipEntry.getName().indexOf('/') >= 0) { |
| return true; |
| } |
| } |
| zipFile.close(); |
| return false; |
| } |
| finally { |
| zipFile.close(); |
| } |
| } |
| |
| public static boolean isZipContainsEntry(File zip, String relativePath) throws IOException { |
| ZipFile zipFile = new ZipFile(zip); |
| try { |
| Enumeration en = zipFile.entries(); |
| |
| while (en.hasMoreElements()) { |
| ZipEntry zipEntry = (ZipEntry)en.nextElement(); |
| if (relativePath.equals(zipEntry.getName())) { |
| return true; |
| } |
| } |
| zipFile.close(); |
| return false; |
| } |
| finally { |
| zipFile.close(); |
| } |
| } |
| |
| /* |
| * update an existing jar file. Adds/replace files specified in relpathToFile map |
| */ |
| public static void update(InputStream in, OutputStream out, Map<String, File> relpathToFile) throws IOException { |
| ZipInputStream zis = new ZipInputStream(in); |
| ZipOutputStream zos = new ZipOutputStream(out); |
| |
| try { |
| // put the old entries first, replace if necessary |
| ZipEntry e; |
| while ((e = zis.getNextEntry()) != null) { |
| String name = e.getName(); |
| |
| if (!relpathToFile.containsKey(name)) { // copy the old stuff |
| // do our own compression |
| ZipEntry e2 = new ZipEntry(name); |
| e2.setMethod(e.getMethod()); |
| e2.setTime(e.getTime()); |
| e2.setComment(e.getComment()); |
| e2.setExtra(e.getExtra()); |
| if (e.getMethod() == ZipEntry.STORED) { |
| e2.setSize(e.getSize()); |
| e2.setCrc(e.getCrc()); |
| } |
| zos.putNextEntry(e2); |
| FileUtil.copy(zis, zos); |
| } |
| else { // replace with the new files |
| final File file = relpathToFile.get(name); |
| //addFile(file, name, zos); |
| relpathToFile.remove(name); |
| addFileToZip(zos, file, name, null, null); |
| } |
| } |
| |
| // add the remaining new files |
| for (final String path : relpathToFile.keySet()) { |
| File file = relpathToFile.get(path); |
| addFileToZip(zos, file, path, null, null); |
| } |
| } |
| finally { |
| zis.close(); |
| zos.close(); |
| } |
| } |
| } |