/** * Copyright (c) 2010, 2012 Darmstadt University of Technology. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marcel Bruch - initial API and implementation. */ package org.eclipse.recommenders.utils; import static com.google.common.base.Optional.*; import static com.google.common.io.ByteStreams.toByteArray; import static com.google.common.io.Files.newInputStreamSupplier; import static org.apache.commons.io.filefilter.DirectoryFileFilter.DIRECTORY; import static org.apache.commons.io.filefilter.FileFileFilter.FILE; import static org.apache.commons.lang3.StringUtils.removeStart; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.Set; import java.util.TreeSet; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.recommenders.utils.names.IMethodName; import org.eclipse.recommenders.utils.names.IPackageName; import org.eclipse.recommenders.utils.names.ITypeName; import org.eclipse.recommenders.utils.names.VmMethodName; import org.eclipse.recommenders.utils.names.VmTypeName; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.io.Closeables; import com.google.common.io.FileWriteMode; import com.google.common.io.Files; import com.google.common.io.InputSupplier; import com.google.common.io.OutputSupplier; public final class Zips { private Zips() { // Not meant to be instantiated } public static ZipFile NULL() { try { File tmp = File.createTempFile("recommenders_null_zip", ".zip"); ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmp)); zos.putNextEntry(new ZipEntry("/")); zos.closeEntry(); zos.close(); return new ZipFile(tmp); } catch (Exception e) { return null; } } /** * This abstraction is used for testing. */ @VisibleForTesting public interface IFileToJarFileConverter { Optional<JarFile> createJarFile(File file); } public static class DefaultJarFileConverter implements IFileToJarFileConverter { @Override public Optional<JarFile> createJarFile(File file) { try { return of(new JarFile(file)); } catch (IOException e) { return absent(); } } } public static void unzip(File zipFile, File destFolder) throws IOException { ZipInputStream zis = null; try { InputSupplier<FileInputStream> fis = Files.newInputStreamSupplier(zipFile); zis = new ZipInputStream(fis.getInput()); ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (!entry.isDirectory()) { final File file = new File(destFolder, entry.getName()); Files.createParentDirs(file); Files.asByteSink(file, FileWriteMode.APPEND).writeFrom(zis); } } } finally { Closeables.close(zis, true); } } public static void zip(File directory, File out) throws IOException { ZipOutputStream zos = null; try { OutputSupplier<FileOutputStream> s = Files.newOutputStreamSupplier(out); zos = new ZipOutputStream(s.getOutput()); for (File f : FileUtils.listFiles(directory, FILE, DIRECTORY)) { String path = removeStart(f.getPath(), directory.getAbsolutePath() + File.separator); path = path.replace(File.separatorChar, '/'); ZipEntry e = new ZipEntry(path); zos.putNextEntry(e); Files.asByteSource(f).copyTo(zos); zos.closeEntry(); } } finally { Closeables.close(zos, false); } } /** * Creates a standard path for the given type name. Package names are replaced by "/". The final name is constructed * as follows: "<package≶/<className≶<extension≶ * <p> * Note: if the path should contain a "." before the extension, it needs to be specified in the extension (e.g., by * specifying ".json" instead of just using "json"). */ public static String path(ITypeName type, @Nullable String suffix) { String name = StringUtils.removeStart(type.getIdentifier(), "L"); return suffix == null ? name : name + suffix; } /** * Returns a path representing this package. The path is actually the package identifier itself. * * @see IPackageName#getIdentifier() */ public static String path(IPackageName pkg, @Nullable String suffix) { String name = pkg.getIdentifier(); return suffix == null ? name : name + suffix; } public static Set<ITypeName> types(ZipFile zip, String suffix) { return types(zip.entries(), suffix); } public static Set<ITypeName> types(Enumeration<? extends ZipEntry> entries, String suffix) { TreeSet<ITypeName> content = new TreeSet<>(); while (entries.hasMoreElements()) { ZipEntry next = entries.nextElement(); if (next.isDirectory() || next.getName().startsWith("META-INF/")) { continue; } ITypeName type = Zips.type(next, suffix); content.add(type); } return content; } public static ITypeName type(ZipEntry entry, @Nullable String suffix) { String name = StringUtils.removeEnd(entry.getName(), suffix); return VmTypeName.get("L" + name); } public static Set<IMethodName> methods(ZipFile zip, String suffix) { return methods(zip.entries(), suffix); } public static Set<IMethodName> methods(Enumeration<? extends ZipEntry> entries, String suffix) { TreeSet<IMethodName> content = new TreeSet<>(); while (entries.hasMoreElements()) { ZipEntry next = entries.nextElement(); if (next.isDirectory() || next.getName().startsWith("META-INF/")) { continue; } IMethodName type = Zips.method(next, suffix); content.add(type); } return content; } public static IMethodName method(ZipEntry e, String suffix) { String name = "L" + StringUtils.substringBefore(e.getName(), suffix); int start = name.lastIndexOf('/'); char[] chars = name.toCharArray(); chars[start] = '.'; for (int i = start + 1; i < chars.length; i++) { if (chars[i] == '.') { chars[i] = '/'; } } return VmMethodName.get(new String(chars)); } public static String path(IMethodName method, @Nullable String suffix) { ITypeName type = method.getDeclaringType(); String name = path(type, null) + "/" + method.getSignature().replaceAll("/", "."); return suffix == null ? name : name + suffix; } /** * Appends the given data to the zip output stream using the specified path. */ public static void append(ZipOutputStream zos, String path, String data) throws IOException { ZipEntry e = new ZipEntry(path); zos.putNextEntry(e); zos.write(data.getBytes(StandardCharsets.UTF_8)); zos.closeEntry(); } /** * Reads the give file into memory. This method may be used by zip based recommenders to speed up data access. */ public static byte[] readFully(File file) throws IOException { return toByteArray(newInputStreamSupplier(file)); } /** * Closes the give zip. Exceptions are printed to System.err. */ public static boolean closeQuietly(ZipFile z) { if (z == null) { return true; } try { z.close(); return true; } catch (IOException e) { Logs.log(LogMessages.LOG_ERROR_CANNOT_CLOSE_RESOURCE, e, z.getName()); return false; } } }