001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.utils;
021
022import org.apache.commons.io.FileUtils;
023import org.apache.commons.io.IOUtils;
024
025import java.io.*;
026import java.util.Enumeration;
027import java.util.zip.ZipEntry;
028import java.util.zip.ZipFile;
029import java.util.zip.ZipInputStream;
030import java.util.zip.ZipOutputStream;
031
032/**
033 * @since 1.10
034 */
035public final class ZipUtils {
036
037  private static final String ERROR_CREATING_DIRECTORY = "Error creating directory: ";
038
039  private ZipUtils() {
040    // only static methods
041  }
042
043  /**
044   * Unzip a file into a directory. The directory is created if it does not exist.
045   *
046   * @return the target directory
047   */
048  public static File unzip(File zip, File toDir) throws IOException {
049    unzip(zip, toDir, new ZipEntryFilter() {
050      @Override
051      public boolean accept(ZipEntry entry) {
052        return true;
053      }
054    });
055    return toDir;
056  }
057
058  public static File unzip(InputStream zip, File toDir) throws IOException {
059    unzip(zip, toDir, new ZipEntryFilter() {
060      @Override
061      public boolean accept(ZipEntry entry) {
062        return true;
063      }
064    });
065    return toDir;
066  }
067
068  public static File unzip(InputStream stream, File toDir, ZipEntryFilter filter) throws IOException {
069    if (!toDir.exists()) {
070      FileUtils.forceMkdir(toDir);
071    }
072
073    ZipInputStream zipStream = new ZipInputStream(stream);
074    try {
075      ZipEntry entry;
076      while ((entry = zipStream.getNextEntry()) != null) {
077        if (filter.accept(entry)) {
078          File to = new File(toDir, entry.getName());
079          if (entry.isDirectory()) {
080            throwExceptionIfDirectoryIsNotCreatable(to);
081          } else {
082            File parent = to.getParentFile();
083            throwExceptionIfDirectoryIsNotCreatable(parent);
084            copy(zipStream, to);
085          }
086        }
087      }
088      return toDir;
089
090    } finally {
091      zipStream.close();
092    }
093  }
094
095  private static void throwExceptionIfDirectoryIsNotCreatable(File to) throws IOException {
096    if (to != null && !to.exists() && !to.mkdirs()) {
097      throw new IOException(ERROR_CREATING_DIRECTORY + to);
098    }
099  }
100
101  public static File unzip(File zip, File toDir, ZipEntryFilter filter) throws IOException {
102    if (!toDir.exists()) {
103      FileUtils.forceMkdir(toDir);
104    }
105
106    ZipFile zipFile = new ZipFile(zip);
107    try {
108      Enumeration<? extends ZipEntry> entries = zipFile.entries();
109      while (entries.hasMoreElements()) {
110        ZipEntry entry = entries.nextElement();
111        if (filter.accept(entry)) {
112          File to = new File(toDir, entry.getName());
113          if (entry.isDirectory()) {
114            throwExceptionIfDirectoryIsNotCreatable(to);
115          } else {
116            File parent = to.getParentFile();
117            throwExceptionIfDirectoryIsNotCreatable(parent);
118            copy(zipFile, entry, to);
119          }
120        }
121      }
122      return toDir;
123
124    } finally {
125      zipFile.close();
126    }
127  }
128
129  private static void copy(ZipInputStream zipStream, File to) throws IOException {
130    FileOutputStream fos = null;
131    try {
132      fos = new FileOutputStream(to);
133      IOUtils.copy(zipStream, fos);
134    } finally {
135      IOUtils.closeQuietly(fos);
136    }
137  }
138
139  private static void copy(ZipFile zipFile, ZipEntry entry, File to) throws IOException {
140    FileOutputStream fos = new FileOutputStream(to);
141    InputStream input = null;
142    try {
143      input = zipFile.getInputStream(entry);
144      IOUtils.copy(input, fos);
145    } finally {
146      IOUtils.closeQuietly(input);
147      IOUtils.closeQuietly(fos);
148    }
149  }
150
151  public static void zipDir(File dir, File zip) throws IOException {
152    OutputStream out = null;
153    ZipOutputStream zout = null;
154    try {
155      out = FileUtils.openOutputStream(zip);
156      zout = new ZipOutputStream(out);
157      zip(dir, zout);
158
159    } finally {
160      IOUtils.closeQuietly(zout);
161      IOUtils.closeQuietly(out);
162    }
163  }
164
165  private static void doZip(String entryName, InputStream in, ZipOutputStream out) throws IOException {
166    ZipEntry entry = new ZipEntry(entryName);
167    out.putNextEntry(entry);
168    IOUtils.copy(in, out);
169    out.closeEntry();
170  }
171
172  private static void doZip(String entryName, File file, ZipOutputStream out) throws IOException {
173    if (file.isDirectory()) {
174      entryName += '/';
175      ZipEntry entry = new ZipEntry(entryName);
176      out.putNextEntry(entry);
177      out.closeEntry();
178      File[] files = file.listFiles();
179      for (int i = 0, len = files.length; i < len; i++) {
180        doZip(entryName + files[i].getName(), files[i], out);
181      }
182
183    } else {
184      InputStream in = null;
185      try {
186        in = new BufferedInputStream(new FileInputStream(file));
187        doZip(entryName, in, out);
188      } finally {
189        IOUtils.closeQuietly(in);
190      }
191    }
192  }
193
194  private static void zip(File file, ZipOutputStream out) throws IOException {
195    for (File child : file.listFiles()) {
196      String name = child.getName();
197      doZip(name, child, out);
198    }
199  }
200
201  public interface ZipEntryFilter {
202    boolean accept(ZipEntry entry);
203  }
204
205}