// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.opendata.core.io.archive;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Map;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLStreamException;
import org.j7zip.SevenZip.ArchiveExtractCallback;
import org.j7zip.SevenZip.HRESULT;
import org.j7zip.SevenZip.IInStream;
import org.j7zip.SevenZip.MyRandomAccessFile;
import org.j7zip.SevenZip.Archive.IInArchive;
import org.j7zip.SevenZip.Archive.SevenZip.Handler;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.io.IllegalDataException;
import org.openstreetmap.josm.plugins.opendata.core.datasets.AbstractDataSetHandler;
import org.openstreetmap.josm.plugins.opendata.core.util.OdUtils;
public class SevenZipReader extends ArchiveReader {
private final IInArchive archive = new Handler();
public SevenZipReader(InputStream in, AbstractDataSetHandler handler, boolean promptUser) throws IOException {
super(handler, handler != null ? handler.getArchiveHandler() : null, promptUser);
// Write entire 7z file as a temp file on disk as we need random access later, and "in" can be a network stream
File tmpFile = File.createTempFile("7z_", ".7z", OdUtils.createTempDir());
Files.copy(in, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
// random must be kept open for later extracting
@SuppressWarnings("resource")
IInStream random = new MyRandomAccessFile(tmpFile.getPath(), "r");
if (archive.Open(random) != 0) {
String message = "Unable to open 7z archive: "+tmpFile.getPath();
Main.warn(message);
random.close();
if (!tmpFile.delete()) {
tmpFile.deleteOnExit();
}
throw new IOException(message);
}
}
public static DataSet parseDataSet(InputStream in, AbstractDataSetHandler handler, ProgressMonitor instance, boolean promptUser)
throws IOException, XMLStreamException, FactoryConfigurationError, IllegalDataException {
return new SevenZipReader(in, handler, promptUser).parseDoc(instance);
}
public static Map<File, DataSet> parseDataSets(InputStream in, AbstractDataSetHandler handler, ProgressMonitor instance, boolean promptUser)
throws IOException, XMLStreamException, FactoryConfigurationError, IllegalDataException {
return new SevenZipReader(in, handler, promptUser).parseDocs(instance);
}
@Override
protected String getTaskMessage() {
return tr("Reading 7Zip file...");
}
@Override
protected void extractArchive(File temp, List<File> candidates) throws IOException, FileNotFoundException {
archive.Extract(null, -1, IInArchive.NExtract_NAskMode_kExtract, new ExtractCallback(archive, temp, candidates));
archive.close();
}
private class ExtractCallback extends ArchiveExtractCallback {
private final List<File> candidates;
ExtractCallback(IInArchive archive, File tempDir, List<File> candidates) {
Init(archive);
super.outputDir = tempDir.getPath();
this.candidates = candidates;
}
@Override
public int GetStream(int index, OutputStream[] outStream, int askExtractMode) throws IOException {
int res = super.GetStream(index, outStream, askExtractMode);
if (res == HRESULT.S_OK) {
lookForCandidate(_filePath, candidates, file);
}
return res;
}
}
}