/* * 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; } } }