Java tutorial
/******************************************************************************* * Copyright (c) 2010, 2011 Tran Nam Quang. * 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: * Tran Nam Quang - initial API and implementation *******************************************************************************/ package net.sourceforge.vaticanfetcher.model.index.file; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Map; import net.sourceforge.vaticanfetcher.model.Path; import net.sourceforge.vaticanfetcher.model.TreeNode; import net.sourceforge.vaticanfetcher.model.index.IndexingConfig; import net.sourceforge.vaticanfetcher.model.index.IndexingError.ErrorType; import net.sourceforge.vaticanfetcher.util.Util; import net.sourceforge.vaticanfetcher.util.annotations.NotNull; import net.sourceforge.vaticanfetcher.util.annotations.Nullable; import com.google.common.collect.Maps; import com.google.common.io.Closeables; import com.google.common.io.NullOutputStream; import de.innosystec.unrar.Archive; import de.innosystec.unrar.exception.RarException; import de.innosystec.unrar.rarfile.FileHeader; final class RarTree extends SolidArchiveTree<FileHeader> { public RarTree(@NotNull File archiveFile, @NotNull IndexingConfig config, @Nullable Path originalPath, @Nullable FailReporter failReporter) throws IOException, ArchiveEncryptedException { super(archiveFile, config, originalPath, failReporter); } public RarTree(@NotNull File archiveFile, @NotNull IndexingConfig config, boolean isHtmlPairing, @Nullable Path originalPath, @Nullable FailReporter failReporter) throws IOException, ArchiveEncryptedException { super(archiveFile, config, isHtmlPairing, originalPath, failReporter); } public void close() throws IOException { // Do nothing } protected ArchiveIterator<FileHeader> getArchiveIterator(File archiveFile, String archivePath) throws IOException, ArchiveEncryptedException { Archive archive = null; try { archive = new Archive(archiveFile); if (archive.isEncrypted()) { Closeables.closeQuietly(archive); throw new ArchiveEncryptedException(archiveFile, archivePath); } final Archive fArchive = archive; return new ArchiveIterator<FileHeader>() { private FileHeader nextFileHeader = fArchive.nextFileHeader(); public FileHeader next() { FileHeader fh = nextFileHeader; nextFileHeader = fArchive.nextFileHeader(); return fh; } public boolean hasNext() { return nextFileHeader != null; } public void finished() { Closeables.closeQuietly(fArchive); } }; } catch (RarException e) { Closeables.closeQuietly(archive); throw new IOException(e); } catch (RuntimeException e) { // J7Zip threw a NullPointerException, as reported in #3437670. Closeables.closeQuietly(archive); throw new IOException(e); } } private static final class RarEntryReader implements ArchiveEntryReader<FileHeader> { private static final RarEntryReader instance = new RarEntryReader(); public String getInnerPath(FileHeader entry) { String subPath = entry.isUnicode() ? entry.getFileNameW() : entry.getFileNameString(); return Util.toForwardSlashes(subPath); } public boolean isDirectory(FileHeader entry) { return entry.isDirectory(); } public long getLastModified(FileHeader entry) { return entry.getMTime().getTime(); } public long getUnpackedSize(FileHeader entry) { return entry.getUnpSize(); } public boolean isEncrypted(FileHeader entry) { return entry.isEncrypted(); } } protected ArchiveEntryReader<FileHeader> getArchiveEntryReader() { return RarEntryReader.instance; } protected Map<Integer, File> doUnpack(Map<Integer, TreeNode> unpackMap, TempFileFactory tempFileFactory) throws IOException { Map<Integer, File> indexFileMap = Maps.newHashMap(); Archive archive = null; try { archive = new Archive(archiveFile); /* * If the archive uses solid compression, all files preceding the target files must be extracted, otherwise JUnRar will throw * errors, such as 'crcError'. In order to save disk space, we can extract unneeded files into a NullOutputStream. * * To find out whether the archive uses solid compression, we'll have to iterate over all file headers and check their solid flags * one by one. - This is necessary, because it is usually not the case that all file headers have the same solid flag! In fact, in * a regular solid archive the first file header is marked 'non-solid', while the remaining file headers are marked 'solid'. */ boolean isSolid = isSolidRarArchive(archive); FileHeader fh = null; NullOutputStream nullOut = isSolid ? new NullOutputStream() : null; for (int i = 0;; i++) { /* * We can abort early if we've extracted all needed files before reaching the end of the archive. */ if (unpackMap.isEmpty()) break; fh = archive.nextFileHeader(); if (fh == null) break; // Last entry reached if (fh.isDirectory()) continue; /* * This was already reported when the tree was constructed, so we can continue silently here. */ if (fh.isEncrypted()) continue; /* * Remove entry from map so we'll know when there are no more files to extract. */ TreeNode treeNode = unpackMap.remove(i); try { if (treeNode != null) { File file = tempFileFactory.createTempFile(treeNode); OutputStream out = new FileOutputStream(file); archive.extractFile(fh, out); Closeables.closeQuietly(out); indexFileMap.put(i, file); } else if (isSolid) { archive.extractFile(fh, nullOut); } } catch (OutOfMemoryError e) { /* * Calling extractFile can throw an OutOfMemoryError. See bug #3443490. */ if (treeNode != null) // Ignore errors for entries written to NullOutputStream failReporter.fail(ErrorType.OUT_OF_MEMORY, treeNode, e); } catch (Exception e) { if (treeNode != null) // Ignore errors for entries written to NullOutputStream failReporter.fail(ErrorType.ARCHIVE_ENTRY, treeNode, e); } } return indexFileMap; } catch (RarException e) { throw new IOException(e); } finally { Closeables.closeQuietly(archive); } } private static boolean isSolidRarArchive(@NotNull Archive archive) { for (FileHeader fh : archive.getFileHeaders()) if (fh.isSolid()) return true; return false; } }