blob: d16ffda175b26a868f9df33ed0240f5860974e2d [file] [log] [blame]
/*
* Copyright 2012-present Facebook, Inc.
*
* 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.facebook.buck.dalvik;
import com.facebook.buck.util.DirectoryTraversal;
import com.facebook.buck.util.ZipFileTraversal;
import com.google.common.base.Preconditions;
import com.google.common.hash.HashCode;
import com.google.common.io.Files;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Traversal strategy for traversing a set of paths that themselves are traversed. The provided
* paths can point to zip/jar files, directories of resource/class files, or individual files
* themselves.
* <p>
* For example, given the input paths of { foo.zip, foo/, and foo.txt }, traverse would first
* expand foo.zip and traverse its contents, then list the files recursively in foo/, and finally
* visit the single file foo.txt.
*/
public abstract class ClasspathTraversal {
private final Iterable<File> paths;
public ClasspathTraversal(Iterable<File> paths) {
this.paths = Preconditions.checkNotNull(paths);
}
public abstract void visit(FileLike fileLike) throws IOException;
public final void traverse() throws IOException {
for (File path : paths) {
ClasspathTraverser adapter = createTraversalAdapter(path);
adapter.traverse(this);
}
}
private ClasspathTraverser createTraversalAdapter(File path) {
String extension = Files.getFileExtension(path.getName());
if (extension.equalsIgnoreCase("jar") || extension.equalsIgnoreCase("zip")) {
return new ZipFileTraversalAdapter(path);
} else if (path.isDirectory()) {
return new DirectoryTraversalAdapter(path);
} else if (path.isFile()) {
return new FileTraversalAdapter(path);
} else {
throw new IllegalArgumentException("Unsupported classpath traversal input: " + path);
}
}
private static class ZipFileTraversalAdapter implements ClasspathTraverser {
private final File file;
public ZipFileTraversalAdapter(File file) {
this.file = Preconditions.checkNotNull(file);
}
@Override
public void traverse(final ClasspathTraversal traversal) throws IOException {
ZipFileTraversal impl = new ZipFileTraversal(file) {
@Override
public void visit(ZipFile zipFile, ZipEntry zipEntry) throws IOException {
traversal.visit(new FileLikeInZip(file, zipFile, zipEntry));
}
};
impl.traverse();
}
private static class FileLikeInZip extends AbstractFileLike {
private final File container;
private final ZipFile zipFile;
private final ZipEntry entry;
public FileLikeInZip(File container, ZipFile zipFile, ZipEntry entry) {
this.container = container;
this.zipFile = zipFile;
this.entry = entry;
}
@Override
public File getContainer() {
return container;
}
@Override
public String getRelativePath() {
return entry.getName();
}
@Override
public long getSize() {
return entry.getSize();
}
@Override
public InputStream getInput() throws IOException {
return zipFile.getInputStream(entry);
}
@Override
public HashCode fastHash() throws IOException {
return HashCode.fromLong(entry.getCrc());
}
}
}
private static class DirectoryTraversalAdapter implements ClasspathTraverser {
private final File file;
public DirectoryTraversalAdapter(File file) {
this.file = Preconditions.checkNotNull(file);
}
@Override
public void traverse(final ClasspathTraversal traversal) throws IOException {
DirectoryTraversal impl = new DirectoryTraversal(file) {
@Override
public void visit(File file, String relativePath) throws IOException {
traversal.visit(new FileLikeInDirectory(file, relativePath));
}
};
impl.traverse();
}
}
private static class FileTraversalAdapter implements ClasspathTraverser {
private final File file;
public FileTraversalAdapter(File file) {
this.file = file;
}
@Override
public void traverse(ClasspathTraversal traversal) throws IOException {
traversal.visit(new FileLikeInDirectory(file, file.getName()));
}
}
private static class FileLikeInDirectory extends AbstractFileLike {
private final File file;
private final String relativePath;
public FileLikeInDirectory(File file, String relativePath) {
// Currently, the only instances of FileLikeInDirectory appear to be the .class files
// generated from an R.java in Android. The only exception is in unit tests.
this.file = Preconditions.checkNotNull(file);
this.relativePath = Preconditions.checkNotNull(relativePath);
}
@Override
public File getContainer() {
return file;
}
@Override
public String getRelativePath() {
return relativePath;
}
@Override
public long getSize() {
return file.length();
}
@Override
public InputStream getInput() throws IOException {
return new FileInputStream(file);
}
}
}