/*
* Copyright 1999-2002 Carnegie Mellon University.
* Portions Copyright 2002 Sun Microsystems, Inc.
* Portions Copyright 2002 Mitsubishi Electric Research Laboratories.
* All Rights Reserved. Use is subject to license terms.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
*
*/
package edu.cmu.sphinx.util;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* Supports the loading and saving of files from different sources, e.g., a ZIP file or a plain directory. Provides
* methods that returns an InputStream or OutputStream to the named file in the given source.
*/
public class StreamFactory {
/** Identifies a ZIP file. */
public static final String ZIP_FILE = "ZIP_FILE";
/** Identifies a plain directory. */
public static final String DIRECTORY = "DIRECTORY";
/**
* Returns an appropriate InputStream of the given file in the given URL location. The location can be a plain
* directory or a ZIP file (these are the only two supported at this point). The <code>resolve</code> method is
* called to resolve whether "location" refers to a ZIP file or a directory.
* <p>
* Suppose you want the InputStream to the file "dict/dictionary.txt" in the ZIP file
* "file:/lab/speech/sphinx4/data/wsj.zip". You will do: <code> StreamFactory.getInputStream(
* "file:/lab/speech/sphinx4/data/wsj.zip", "dict/dictionary.txt"); </code>
* <p>
* Suppose you want the InputStream to the file "dict/dictionary.txt" in the directory
* "file:/lab/speech/sphinx4/data/wsj", you will do: <code> StreamFactory.getInputStream(
* "file:/lab/speech/sphinx4/data/wsj", "dict/dictionary.txt"); </code>
* <p>
* The <code>StreamFactory.resolve()</code> method is called to resolve whether "location" refers to a ZIP file or a
* directory.
*
* @param location the URL location of the input data, it can now be a directory or a ZIP file
* @param file the file in the given location to obtain the InputStream
* @return an InputStream of the given file in the given location
* @throws IOException if IO went wrong
*/
public static InputStream getInputStream(String location,
String file) throws
IOException {
if (location != null) {
return StreamFactory.getInputStream
(StreamFactory.resolve(location), location, file);
} else {
return StreamFactory.getInputStream(StreamFactory.DIRECTORY,
location, file);
}
}
/**
* According to the given data format, returns an appropriate InputStream of the given file in the given URL
* location. The location can be a plain directory or a JAR or ZIP file (these are the only ones supported at this
* point).
* <p>
* Suppose you want the InputStream to the file "dict/dictionary.txt" in the ZIP file
* "file:/lab/speech/sphinx4/data/wsj.zip". You will do: <code> StreamFactory.getInputStream(StreamFactory.ZIP_FILE,
* "file:/lab/speech/sphinx4/data/wsj.zip", "dict/dictionary.txt"); </code>
* <p>
* Suppose you want the InputStream to the file "dict/dictionary.txt" in the directory
* "file:/lab/speech/sphinx4/data/wsj", you will do: <code> StreamFactory.getInputStream(StreamFactory.DIRECTORY,
* "file:/lab/speech/sphinx4/data/wsj", "dict/dictionary.txt"); </code>
*
* @param format the format of the input data, the currently supported formats are: <br>StreamFactory.ZIP_FILE
* <br>StreamFactory.DIRECTORY
* @param location the URL location of the input data, it can now be a directory or a JAR or ZIP file, or null if no
* location is given, which means that the <code>argument</code> also specifies the exact location
* @param file the file in the given location to obtain the InputStream
* @return an InputStream of the given file in the given location
* @throws IOException if IO went wrong
*/
public static InputStream getInputStream(String format,
String location,
String file) throws
IOException {
InputStream stream = null;
String absoluteLocation;
if (location == null) {
absoluteLocation = null;
} else {
// Create a url from the location, possibly relative path
URI uri = URI.create(location);
// Get the scheme and the path
String scheme = uri.getScheme();
String path = uri.getSchemeSpecificPart();
// Create a file with the path (aka scheme-specific part)
File relativeFile = new File(path);
// Make the path absolute and reconstruct the location, with
// the correct scheme
URI absoluteURI = relativeFile.getAbsoluteFile().toURI();
if (scheme == null) {
absoluteLocation = absoluteURI.getSchemeSpecificPart();
} else {
absoluteLocation = scheme + ':' +
absoluteURI.getSchemeSpecificPart();
}
}
if (format.equals(ZIP_FILE)) {
try {
URI newURI = new URI(absoluteLocation);
ZipFile zipFile =
new ZipFile(new File(newURI));
ZipEntry entry = zipFile.getEntry(file);
if (entry != null) {
stream = zipFile.getInputStream(entry);
}
zipFile.close();
} catch (URISyntaxException use) {
use.printStackTrace();
throw new ZipException("URISyntaxException: " +
use.getMessage());
}
} else if (format.equals(DIRECTORY)) {
if (absoluteLocation != null) {
stream = new FileInputStream(absoluteLocation +
File.separator + file);
} else {
stream = new FileInputStream(file);
}
}
return stream;
}
/**
* Returns an appropriate OutputStream of the given file in the given URL location. The location can be a plain
* directory or a ZIP file (these are the only two supported at this point). The <code>resolve</code> method is
* called to resolve whether "location" refers to a ZIP file or a directory. If saving to a zip or jar, the file can
* be appended or overwritten. If saving to a directory, files are always overwritten.
* <p>
* Suppose you want the OutputStream to the file "dict/dictionary.txt" in the ZIP file
* "file:/lab/speech/sphinx4/data/wsj.zip". You will do: <code> StreamFactory.getOutputStream(
* "file:/lab/speech/sphinx4/data/wsj.zip", "dict/dictionary.txt", true); </code>
* <p>
* Suppose you want the OutputStream to the file "dict/dictionary.txt" in the directory
* "file:/lab/speech/sphinx4/data/wsj", you will do: <code> StreamFactory.getOutputStream(
* "file:/lab/speech/sphinx4/data/wsj", "dict/dictionary.txt", false); </code>
* <p>
* The <code>StreamFactory.resolve()</code> method is called to resolve whether "location" refers to a ZIP file or a
* directory.
*
* @param location the URL location of the output data, it can now be a directory or a ZIP file
* @param file the file in the given location to obtain the OutputStream
* @param append if true and saving to a zip file, then file is appended rather than overwritten.
* @return an OutputStream of the given file in the given location
* @throws IOException if IO went wrong
*/
public static OutputStream getOutputStream(String location,
String file,
boolean append) throws
IOException {
if (location != null) {
return StreamFactory.getOutputStream
(StreamFactory.resolve(location), location, file, append);
} else {
return StreamFactory.getOutputStream(StreamFactory.DIRECTORY,
location, file);
}
}
/**
* According to the given data format, returns an appropriate OutputStream of the given file in the given URL
* location. The location can be a plain directory or a JAR or ZIP file (these are the only ones supported at this
* point). If saving to a zip or jar, the file can be appended or overwritten. If saving to a directory, files are
* always overwritten.
* <p>
* Suppose you want the OutputStream to the file "dict/dictionary.txt" in the ZIP file
* "file:/lab/speech/sphinx4/data/wsj.zip". You will do: <code> StreamFactory.getOutputStream(StreamFactory.ZIP_FILE,
* "file:/lab/speech/sphinx4/data/wsj.zip", "dict/dictionary.txt", true); </code>
* <p>
* Suppose you want the OutputStream to the file "dict/dictionary.txt" in the directory
* "file:/lab/speech/sphinx4/data/wsj", you will do: <code> StreamFactory.getOutputStream(StreamFactory.DIRECTORY,
* "file:/lab/speech/sphinx4/data/wsj", "dict/dictionary.txt", false); </code>
*
* @param format the format of the output data, the currently supported formats are: <br>StreamFactory.ZIP_FILE
* <br>StreamFactory.DIRECTORY
* @param location the URL location of the output data, it can now be a directory or a JAR or ZIP file, or null if
* no location is given, which means that the <code>argument</code> also specifies the exact
* location
* @param file the file in the given location to obtain the OutputStream
* @param append if true and saving to a zip file, then file is appended rather than overwritten.
* @return an OutputStream of the given file in the given location
* @throws IOException if IO went wrong
*/
public static OutputStream getOutputStream(String format,
String location,
String file,
boolean append) throws
IOException {
OutputStream stream = null;
if (format.equals(ZIP_FILE)) {
try {
System.out.println("WARNING: ZIP not yet fully supported.!");
File path = new File(location);
File parent = new File(path.getParent());
if (!parent.exists()) {
parent.mkdirs();
}
FileOutputStream fos =
new FileOutputStream(new File(new URI(location)), append);
stream = new ZipOutputStream(new BufferedOutputStream(fos));
ZipEntry entry = new ZipEntry(file);
((ZipOutputStream) stream).putNextEntry(entry);
} catch (URISyntaxException use) {
use.printStackTrace();
throw new ZipException("URISyntaxException: " +
use.getMessage());
}
} else if (format.equals(DIRECTORY)) {
if (location != null) {
File path = new File(location + File.separator + file);
File parent = new File(path.getParent());
if (!parent.exists()) {
parent.mkdirs();
}
stream = new FileOutputStream(location + File.separator + file);
} else {
File path = new File(file);
File parent = new File(path.getParent());
if (!parent.exists()) {
parent.mkdirs();
}
stream = new FileOutputStream(file);
}
} else {
throw new IOException("Format not supported for writing");
}
return stream;
}
/**
* Returns an appropriate OutputStream of the given file in the given URL location. The location can be a plain
* directory or a ZIP file (these are the only two supported at this point). The <code>resolve</code> method is
* called to resolve whether "location" refers to a ZIP file or a directory. Files are overwritten, which may be
* risky for ZIP of JAR files.
* <p>
* Suppose you want the OutputStream to the file "dict/dictionary.txt" in the ZIP file
* "file:/lab/speech/sphinx4/data/wsj.zip". You will do: <code> StreamFactory.getOutputStream(
* "file:/lab/speech/sphinx4/data/wsj.zip", "dict/dictionary.txt"); </code>
* <p>
* Suppose you want the OutputStream to the file "dict/dictionary.txt" in the directory
* "file:/lab/speech/sphinx4/data/wsj", you will do: <code> StreamFactory.getOutputStream(
* "file:/lab/speech/sphinx4/data/wsj", "dict/dictionary.txt"); </code>
* <p>
* The <code>StreamFactory.resolve()</code> method is called to resolve whether "location" refers to a ZIP file or a
* directory.
*
* @param location the URL location of the output data, it can now be a directory or a ZIP file
* @param file the file in the given location to obtain the OutputStream
* @return an OutputStream of the given file in the given location
* @throws IOException if IO went wrong
*/
public static OutputStream getOutputStream(String location,
String file) throws
IOException {
if (location != null) {
return StreamFactory.getOutputStream
(StreamFactory.resolve(location), location, file);
} else {
return StreamFactory.getOutputStream(StreamFactory.DIRECTORY,
location, file);
}
}
/**
* According to the given data format, returns an appropriate OutputStream of the given file in the given URL
* location. The location can be a plain directory or a JAR or ZIP file (these are the only ones supported at this
* point). Files are always overwritten, which can be risky for ZIP or JAR files.
* <p>
* Suppose you want the OutputStream to the file "dict/dictionary.txt" in the ZIP file
* "file:/lab/speech/sphinx4/data/wsj.zip". You will do: <code> StreamFactory.getOutputStream(StreamFactory.ZIP_FILE,
* "file:/lab/speech/sphinx4/data/wsj.zip", "dict/dictionary.txt"); </code>
* <p>
* Suppose you want the OutputStream to the file "dict/dictionary.txt" in the directory
* "file:/lab/speech/sphinx4/data/wsj", you will do: <code> StreamFactory.getOutputStream(StreamFactory.DIRECTORY,
* "file:/lab/speech/sphinx4/data/wsj", "dict/dictionary.txt"); </code>
*
* @param format the format of the output data, the currently supported formats are: <br>StreamFactory.ZIP_FILE
* <br>StreamFactory.DIRECTORY
* @param location the URL location of the output data, it can now be a directory or a JAR or ZIP file, or null if
* no location is given, which means that the <code>argument</code> also specifies the exact
* location
* @param file the file in the given location to obtain the OutputStream
* @return an OutputStream of the given file in the given location
* @throws IOException if IO went wrong
*/
public static OutputStream getOutputStream(String format,
String location,
String file) throws
IOException {
if (format.equals(ZIP_FILE)) {
System.out.println("WARNING: overwriting ZIP or JAR file!");
return StreamFactory.getOutputStream
(StreamFactory.resolve(location), location, file, false);
} else if (format.equals(DIRECTORY)) {
return StreamFactory.getOutputStream(StreamFactory.DIRECTORY,
location, file, false);
} else {
throw new IOException("Format not supported for writing");
}
}
/**
* @param sourceName name of the source
* @return the type of the given data source. The current supported types are: <code> StreamFactory.ZIP_FILE
* StreamFactory.DIRECTORY </code>
*/
public static String resolve(String sourceName) {
if ((sourceName.endsWith(".jar")) || (sourceName.endsWith(".zip"))) {
return StreamFactory.ZIP_FILE;
} else {
return StreamFactory.DIRECTORY;
}
}
}