package prefuse.util.io;
import java.awt.Component;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import javax.swing.JFileChooser;
import prefuse.data.Graph;
import prefuse.data.Table;
import prefuse.data.io.CSVTableReader;
import prefuse.data.io.DelimitedTextTableReader;
import prefuse.data.io.GraphMLReader;
import prefuse.data.io.GraphReader;
import prefuse.data.io.TableReader;
import prefuse.data.io.TreeMLReader;
import prefuse.util.StringLib;
import prefuse.util.collections.ByteArrayList;
/**
* Library routines for input/output tasks.
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class IOLib {
private IOLib() {
// disallow instantiation
}
/**
* Indicates if a given String is a URL string. Checks to see if the string
* begins with the "http:/", "ftp:/", or "file:/" protocol strings.
* @param s the string to check
* @return true if a url string matching the listed protocols,
* false otherwise
*/
public static boolean isUrlString(String s) {
return s.startsWith("http:/") ||
s.startsWith("ftp:/") ||
s.startsWith("file:/");
}
/**
* From a string description, attempt to generate a URL object. The string
* may point to an Internet location (e.g., http:// or ftp:// URL),
* a resource on the class path (resulting in a resource URL that points
* into the current classpath), or a file on the local filesystem
* (resulting in a file:// URL). The String will be checked in that order
* in an attempt to resolve it to a valid URL.
* @param location the location string for which to get a URL object
* @return a URL object, or null if the location string could not be
* resolved
*/
public static URL urlFromString(String location) {
return urlFromString(location, null, true);
}
/**
* From a string description, attempt to generate a URL object. The string
* may point to an Internet location (e.g., http:// or ftp:// URL),
* a resource on the class path (resulting in a resource URL that points
* into the current classpath), or, if the <code>includeFileSystem</code>
* flag is true, a file on the local filesystem
* (resulting in a file:// URL). The String will be checked in that order
* in an attempt to resolve it to a valid URL.
* @param location the location string for which to get a URL object
* @param referrer the class to check for classpath resource items, the
* location string will be resolved against the package/folder containing
* this class
* @param includeFileSystem indicates if the file system should be
* included in the search to resolve the location String
* @return a URL object, or null if the location string could not be
* resolved
*/
public static URL urlFromString(String location, Class referrer,
boolean includeFileSystem)
{
URL url = null;
if ( isUrlString(location) ) {
// explicit URL string
try {
url = new URL(location);
} catch ( Exception e ) {
e.printStackTrace();
}
} else {
// attempt to get a URL pointing into the classpath
if ( referrer != null )
url = referrer.getResource(location);
else
url = IOLib.class.getResource(location);
if ( url == null && !location.startsWith("/") )
url = IOLib.class.getResource("/"+location);
if ( includeFileSystem && url == null ) {
// if still not found, check the file system
File f = new File(location);
if ( f.exists() ) {
try {
url = f.toURI().toURL();
} catch ( Exception e ) {}
}
}
}
return url;
}
/**
* Get an input string corresponding to the given location string. The
* string will first be resolved to a URL and an input stream will be
* requested from the URL connection. If this fails, the location will
* be resolved against the file system. Also, if a gzip file is found,
* the input stream will also be wrapped by a GZipInputStream. If the
* location string can not be resolved, a null value is returned
* @param location the location string
* @return an InputStream for the resolved location string
* @throws IOException if an input/ouput error occurs
*/
public static InputStream streamFromString(String location)
throws IOException
{
InputStream is = null;
// try to get a working url from the string
URL url = urlFromString(location, null, false);
if ( url != null ) {
is = url.openStream();
} else {
// if that failed, try the file system
File f = new File(location);
if ( f.exists() )
is = new FileInputStream(f);
}
if ( is == null ) {
return null; // couldn't find it
} else if ( isGZipFile(location) ) {
return new GZIPInputStream(is);
} else {
return is;
}
}
/**
* Returns the extension for a file or null if there is none
* @param f the input file
* @return the file extension, or null if none
*/
public static String getExtension(File f) {
return (f != null ? getExtension(f.getName()) : null);
}
/**
* Indicates if the given file ends with a file extension of
* ".gz" or ".Z", indicating a GZip file.
* @param file a String of the filename or URL of the file
* @return true if the extension is ".gz" or ".Z", false otherwise
*/
public static boolean isGZipFile(String file) {
String ext = getExtension(file);
return "gz".equals(ext) || "z".equals(ext);
}
/**
* Indicates if the given file ends with a file extension of
* ".zip", indicating a Zip file.
* @param file a String of the filename or URL of the file
* @return true if the extension is ".zip", false otherwise
*/
public static boolean isZipFile(String file) {
return "zip".equals(getExtension(file));
}
/**
* Returns the extension for a file or null if there is none
* @param filename the input filename
* @return the file extension, or null if none
*/
public static String getExtension(String filename) {
int i = filename.lastIndexOf('.');
if ( i>0 && i<filename.length()-1 ) {
return filename.substring(i+1).toLowerCase();
} else {
return null;
}
}
/**
* Reads an input stream into a list of byte values.
* @param is the input stream to read
* @return a ByteArrayList containing the contents of the input stream
* @throws IOException if an input/ouput error occurs
*/
public static ByteArrayList readAsBytes(InputStream is) throws IOException {
ByteArrayList buf = new ByteArrayList();
byte[] b = new byte[8192];
int nread = -1;
while ( (nread=is.read(b)) >= 0 ) {
buf.add(b, 0, nread);
}
return buf;
}
/**
* Reads an input stream into a single String result.
* @param is the input stream to read
* @return a String containing the contents of the input stream
* @throws IOException if an input/ouput error occurs
*/
public static String readAsString(InputStream is) throws IOException {
StringBuffer buf = new StringBuffer();
byte[] b = new byte[8192];
int nread = -1;
while ( (nread=is.read(b)) >= 0 ) {
String s = new String(b, 0, nread);
buf.append(s);
}
return buf.toString();
}
/**
* Reads data pulled from the given location string into a single String
* result. The method attempts to retrieve an InputStream using the
* {@link #streamFromString(String)} method, then read the input stream
* into a String result.
* @param location the location String
* @return a String with the requested data
* @throws IOException if an input/ouput error occurs
* @see #streamFromString(String)
*/
public static String readAsString(String location) throws IOException {
return readAsString(streamFromString(location));
}
// ------------------------------------------------------------------------
/**
* Present a file chooser dialog for loading a Table data set.
* @param c user interface component from which the request is being made
* @return a newly loaded Table, or null if not found or action canceled
*/
public static Table getTableFile(Component c) {
JFileChooser jfc = new JFileChooser();
jfc.setDialogType(JFileChooser.OPEN_DIALOG);
jfc.setDialogTitle("Open Table File");
jfc.setAcceptAllFileFilterUsed(false);
SimpleFileFilter ff;
// TODO: have this generate automatically
// tie into PrefuseConfig??
// CSV
ff = new SimpleFileFilter("csv",
"Comma Separated Values (CSV) File (*.csv)",
new CSVTableReader());
ff.addExtension("gz");
jfc.setFileFilter(ff);
// Pipe-Delimited
ff = new SimpleFileFilter("txt",
"Pipe-Delimited Text File (*.txt)",
new DelimitedTextTableReader("|"));
ff.addExtension("gz");
jfc.setFileFilter(ff);
// Tab-Delimited
ff = new SimpleFileFilter("txt",
"Tab-Delimited Text File (*.txt)",
new DelimitedTextTableReader());
ff.addExtension("gz");
jfc.setFileFilter(ff);
int retval = jfc.showOpenDialog(c);
if (retval != JFileChooser.APPROVE_OPTION)
return null;
File f = jfc.getSelectedFile();
ff = (SimpleFileFilter)jfc.getFileFilter();
TableReader tr = (TableReader)ff.getUserData();
try {
return tr.readTable(streamFromString(f.getAbsolutePath()));
} catch ( Exception e ) {
Logger.getLogger(IOLib.class.getName()).warning(
e.getMessage() + "\n" + StringLib.getStackTrace(e));
return null;
}
}
/**
* Present a file chooser dialog for loading a Graph or Tree data set.
* @param c user interface component from which the request is being made
* @return a newly loaded Graph, or null if not found or action canceled
*/
public static Graph getGraphFile(Component c) {
JFileChooser jfc = new JFileChooser();
jfc.setDialogType(JFileChooser.OPEN_DIALOG);
jfc.setDialogTitle("Open Graph or Tree File");
jfc.setAcceptAllFileFilterUsed(false);
SimpleFileFilter ff;
// TODO: have this generate automatically
// tie into PrefuseConfig??
// TreeML
ff = new SimpleFileFilter("xml",
"TreeML File (*.xml, *.treeml)",
new TreeMLReader());
ff.addExtension("treeml");
ff.addExtension("gz");
jfc.setFileFilter(ff);
// GraphML
ff = new SimpleFileFilter("xml",
"GraphML File (*.xml, *.graphml)",
new GraphMLReader());
ff.addExtension("graphml");
ff.addExtension("gz");
jfc.setFileFilter(ff);
int retval = jfc.showOpenDialog(c);
if (retval != JFileChooser.APPROVE_OPTION)
return null;
File f = jfc.getSelectedFile();
ff = (SimpleFileFilter)jfc.getFileFilter();
GraphReader gr = (GraphReader)ff.getUserData();
try {
return gr.readGraph(streamFromString(f.getAbsolutePath()));
} catch ( Exception e ) {
Logger.getLogger(IOLib.class.getName()).warning(
e.getMessage() + "\n" + StringLib.getStackTrace(e));
return null;
}
}
} // end of class IOLib