package net.sf.openrocket.file;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.OpenRocketDocumentFactory;
import net.sf.openrocket.file.openrocket.importt.OpenRocketLoader;
import net.sf.openrocket.file.rocksim.importt.RocksimLoader;
import net.sf.openrocket.util.ArrayUtils;
import net.sf.openrocket.util.TextUtil;
/**
* A rocket loader that auto-detects the document type and uses the appropriate
* loading. Supports loading of GZIPed files as well with transparent
* uncompression.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class GeneralRocketLoader {
protected final WarningSet warnings = new WarningSet();
private static final int READ_BYTES = 300;
private static final byte[] GZIP_SIGNATURE = { 31, -117 }; // 0x1f, 0x8b
private static final byte[] ZIP_SIGNATURE = TextUtil.asciiBytes("PK");
private static final byte[] OPENROCKET_SIGNATURE = TextUtil.asciiBytes("<openrocket");
private static final byte[] ROCKSIM_SIGNATURE = TextUtil.asciiBytes("<RockSimDoc");
private final OpenRocketLoader openRocketLoader = new OpenRocketLoader();
private final RocksimLoader rocksimLoader = new RocksimLoader();
private File baseFile;
private URL jarURL;
private boolean isContainer;
private final MotorFinder motorFinder;
private AttachmentFactory attachmentFactory;
private final OpenRocketDocument doc = OpenRocketDocumentFactory.createEmptyRocket();
public GeneralRocketLoader(File file) {
this.baseFile = file;
this.jarURL = null;
this.motorFinder = new DatabaseMotorFinder();
}
public GeneralRocketLoader(URL jarURL) {
this.baseFile = null;
this.jarURL = jarURL;
this.motorFinder = new DatabaseMotorFinder();
}
/**
* Loads a rocket from the File object used in the constructor
*/
public final OpenRocketDocument load() throws RocketLoadException {
warnings.clear();
InputStream stream = null;
try {
stream = new BufferedInputStream(new FileInputStream(baseFile));
load(stream);
return doc;
} catch (Exception e) {
throw new RocketLoadException("Exception loading file: " + baseFile + " , " + e.getMessage(), e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public final OpenRocketDocument load(InputStream source) throws RocketLoadException {
try {
loadStep1(source);
return doc;
} catch (Exception e) {
throw new RocketLoadException("Exception loading stream: " + e.getMessage(), e);
}
}
public final WarningSet getWarnings() {
return warnings;
}
/**
* This method determines the type file contained in the stream then calls the appropriate loading mecahnism.
*
* If the stream is a gzip file, the argument is wrapped in a GzipInputStream and the rocket loaded.
*
* If the stream is a zip container, the first zip entry with name ending in .ork or .rkt is loaded as the rocket.
*
* If the stream is neither, then it is assumed to be an xml file containing either an ork or rkt format rocket.
*
* @param source
* @throws IOException
* @throws RocketLoadException
*/
private void loadStep1(InputStream source) throws IOException, RocketLoadException {
// Check for mark() support
if (!source.markSupported()) {
source = new BufferedInputStream(source);
}
// Read using mark()
byte[] buffer = new byte[READ_BYTES];
int count;
source.mark(READ_BYTES + 10);
count = source.read(buffer);
source.reset();
if (count < 10) {
throw new RocketLoadException("Unsupported or corrupt file.");
}
// Detect the appropriate loader
// Check for GZIP
if (buffer[0] == GZIP_SIGNATURE[0] && buffer[1] == GZIP_SIGNATURE[1]) {
isContainer = false;
setAttachmentFactory();
loadRocket(new GZIPInputStream(source));
return;
}
// Check for ZIP (for future compatibility)
if (buffer[0] == ZIP_SIGNATURE[0] && buffer[1] == ZIP_SIGNATURE[1]) {
OpenRocketDocument doc;
isContainer = true;
setAttachmentFactory();
// Search for entry with name *.ork
ZipInputStream in = new ZipInputStream(source);
while (true) {
ZipEntry entry = in.getNextEntry();
if (entry == null) {
throw new RocketLoadException("Unsupported or corrupt file.");
}
if (entry.getName().matches(".*\\.[oO][rR][kK]$")) {
loadRocket(in);
return;
} else if (entry.getName().matches(".*\\.[rR][kK][tT]$")) {
loadRocket(in);
return;
}
}
}
isContainer = false;
setAttachmentFactory();
loadRocket(source);
return;
}
private void loadRocket(InputStream source) throws IOException, RocketLoadException {
// Check for mark() support
if (!source.markSupported()) {
source = new BufferedInputStream(source);
}
// Read using mark()
byte[] buffer = new byte[READ_BYTES];
int count;
source.mark(READ_BYTES + 10);
count = source.read(buffer);
source.reset();
if (count < 10) {
throw new RocketLoadException("Unsupported or corrupt file.");
}
// Check for OpenRocket
int match = 0;
for (int i = 0; i < count; i++) {
if (buffer[i] == OPENROCKET_SIGNATURE[match]) {
match++;
if (match == OPENROCKET_SIGNATURE.length) {
loadUsing(openRocketLoader, source);
return;
}
} else {
match = 0;
}
}
byte[] typeIdentifier = ArrayUtils.copyOf(buffer, ROCKSIM_SIGNATURE.length);
if (Arrays.equals(ROCKSIM_SIGNATURE, typeIdentifier)) {
loadUsing(rocksimLoader, source);
return;
}
throw new RocketLoadException("Unsupported or corrupt file.");
}
private void setAttachmentFactory() {
attachmentFactory = new FileSystemAttachmentFactory(null);
if (jarURL != null && isContainer) {
attachmentFactory = new ZipFileAttachmentFactory(jarURL);
} else {
if (isContainer) {
try {
attachmentFactory = new ZipFileAttachmentFactory(baseFile.toURI().toURL());
} catch (MalformedURLException mex) {
}
} else if (baseFile != null) {
attachmentFactory = new FileSystemAttachmentFactory(baseFile.getParentFile());
}
}
}
private void loadUsing(RocketLoader loader, InputStream source) throws RocketLoadException {
warnings.clear();
DocumentLoadingContext context = new DocumentLoadingContext();
context.setOpenRocketDocument(doc);
context.setMotorFinder(motorFinder);
context.setAttachmentFactory(attachmentFactory);
loader.load(context, source);
warnings.addAll(loader.getWarnings());
}
}