/* * Created on May 16, 2004 */ package textfile; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import javax.swing.JFileChooser; import MDUtils.StringUtils; /** An InputStream opened from a fileID string, which might be a * full or partial path or a URL, or even a file browsed to by a user. * A serious attempt to locate the file is made. * @author dickey */ public class FileOrURLInputStream extends InputStream { /** Once opened, most everything is delegated to this stream. * */ private InputStream theRealStream; /** The actual path or URL of the file, if it was successfully located. */ private String absPath; /** Given a file name, find the file it refers to it and make it ready * for read-access, with default option: don't prompt for file if it * cannot otherwise be located. See the one-argument * constructor for more information. * @param filename see the one-argument constructor * @returns see the one-argument constructor */ public FileOrURLInputStream(String filename) { this(filename, FileFinderOption.DONT_PROMPT_FOR_FILE); } /** Given a file name or URL, look pretty hard to * find the file it refers to it and make its stream ready * for read-access. * If no file can be opened, or there is an error on the file, then * a console message is printed; in this case the * DelimitedTextFile object is effectively an empty file -- any calls to * retrieve data will return null. * @param filename a full or partial filename or URL; may not be * empty or null. * @param options a FileFinderOption to help control how hard the * file is looked for. * @throws IllegalArgumentException if a parameter value is bad. * */ public FileOrURLInputStream(String filename, FileFinderOption options) { if (filename == null || filename.length() == 0) { throw new IllegalArgumentException("file name cannot be empty or null"); } String partialPath = filename.trim(); this.theRealStream = openStream(partialPath, options); //comes back null if the stream wasn't opened. } /** Tells the name of the file on which this stream is based. * This might be different from the fileID requested when the * stream was opened. * @return the full path or full URL, if the file was actually located * as the basis for this stream; null otherwise (if no file could * actually be located and opened.) */ public String getFileID() { return this.absPath; } /** Search very hard for a file with the given name and open it if * possible. Along the way some informational messages may be printed * on the console. If the file is found, an instance variable is * updated with the full path or URL name. * @param partialPath a full or partial pathname or full URL. * @param options used to control how hard to look. * @return an open stream to the file, positioned at the beginning; * or null if the file could not be located or if a stream could not * be opened to it. */ private InputStream openStream(String partialPath, FileFinderOption options) { if (partialPath == null) { return null; } partialPath = partialPath.trim(); if (partialPath.length() == 0) { return null; } InputStream iStream = null; //eventual return value URL fURL = getURL(partialPath); if (fURL != null) { //maybe this is a valid URL already try { URLConnection uconn = fURL.openConnection(); iStream = uconn.getInputStream(); this.absPath = fURL.toExternalForm(); } catch (IOException e) { //System.out.println(partialPath + " is a valid URL " + //" but could not be opened."); } } else { //definitely not a URL -- try finding a file if (iStream == null) { //maybe the path is OK as is -- for example, absolute File f = new File(partialPath); try { iStream = new FileInputStream(f); //if we got here, it worked absPath = partialPath; } catch (FileNotFoundException e) { } } if (iStream == null) { //Now let the Loader look in all paths on the JVM classpath. try { java.lang.ClassLoader loader = this.getClass().getClassLoader(); { //experimental code class JunkClass {} ClassLoader l2 = JunkClass.class.getClassLoader(); assert l2 == loader; } if (loader == null) { //this supposedly indicates the bootstrap loader was used loader = java.lang.ClassLoader.getSystemClassLoader(); assert loader != null; } java.net.URL urlOfPath = loader.getResource(partialPath); if (urlOfPath != null) { try { File fileFromURL = new File(urlOfPath.getFile()); if (fileFromURL != null && fileFromURL.exists() && fileFromURL.isFile()) { String path = fileFromURL.getAbsolutePath(); iStream = new FileInputStream(path); //if we got here, it all worked this.absPath = path; } } catch (Exception e) { System.out.println(e); System.out.println(partialPath + " is on the classpath but could not be opened"); } } } catch (SecurityException se) { } } if (iStream == null) { //look in the current user directory try { String userDir = System.getProperty("user.dir"); String fs = System.getProperty("file.separator"); if (userDir != null && fs != null) { String trialPath = userDir + fs + partialPath; File userDirFile = new File(trialPath); if (userDirFile.exists() && !userDirFile.isDirectory() && userDirFile.canRead()) { try { iStream = new FileInputStream(userDirFile); this.absPath = userDirFile.getAbsolutePath(); //worked! } catch (FileNotFoundException e) { } } } } catch (SecurityException e) { //could result from the getProperty } } if (iStream == null) { //still don't have a stream System.out.println("Unable to locate or open " + partialPath); System.out.println("Be sure the name is spelled correctly " + "(case-sensitive)" + " and is on the class path."); } } if (iStream == null) { if (options == FileFinderOption.PROMPT_FOR_FILE) { iStream = letUserChooseFile(partialPath); } } return iStream; } /** Let user choose a file; if successfull, remember its path. * @partialPath a file name (for prompting message only). * @return a stream if it could be opened; otherwise, null. */ private InputStream letUserChooseFile(String partialPath) { String chooserDefault = "."; //might not work on all systems JFileChooser chooser = new JFileChooser(chooserDefault); chooser.setDialogTitle("Choose a file in place of " + partialPath); int result = chooser.showDialog(null, "use this file"); if (result == JFileChooser.APPROVE_OPTION) { File chosenFile = chooser.getSelectedFile(); if (chosenFile != null) { String path = chosenFile.getAbsolutePath(); try { InputStream iStream = new FileInputStream(path); this.absPath = path; //it all worked return iStream; } catch (FileNotFoundException e) { } } } return null; //won't get here if the choice succeeded } /** If this is a well-formed URL, return it as such. * @param fileID a string which might be a valid URL. * @return a URL corresponding to the id; null if the id * is not a well-formed URL. A non-null return does NOT * mean the URL can be contacted or opened. */ private URL getURL(String fileID) { try { String encodedURL = StringUtils.urlEncode(fileID); URL fURL = new URL(encodedURL); return fURL; } catch (MalformedURLException e) { return null; } } /* -------------------------------------------------- * Following methods are delegated to the underlying stream. * If that stream doesn't exist, provide simple emulation. */ /** Delegate to the underlying stream (which must be open already). * @see java.io.InputStream#read() */ public int read() throws IOException { if (theRealStream == null) { /* throw new IOException("No underlying stream for the " + this.getClass().getName()); */ return -1; //emulate EOF } return theRealStream.read(); } /** Delegate to the underlying stream (which must be open already) * @see java.io.InputStream#available() */ public int available() throws IOException { if (theRealStream == null) { return 0; //emulate EOF (this is also what InputStream does) } return theRealStream.available(); } /** Delegate to the underlying stream (which must be open already). * @see java.io.InputStream#close() */ public void close() throws IOException { if (theRealStream == null) { return; } theRealStream.close(); } /** Delegate to the underlying stream (which must be open already). * @see java.io.InputStream#mark(int) */ public void mark(int readlimit) { if (theRealStream == null) { return; } theRealStream.mark(readlimit); } /** Delegate to the underlying stream (which must be open already). * @see java.io.InputStream#markSupported() */ public boolean markSupported() { if (theRealStream == null) { return false; } return theRealStream.markSupported(); } /** Delegate to the underlying stream (which must be open already). * @see java.io.InputStream#read(byte[]) */ public int read(byte[] b) throws IOException { if (theRealStream == null) { return -1; //emulate EOF } return theRealStream.read(b); } /** Delegate to the underlying stream (which must be open already). * @see java.io.InputStream#read(byte[], int, int) */ public int read(byte[] b, int off, int len) throws IOException { if (theRealStream == null) { return -1; //emulate EOF } return theRealStream.read(b, off, len); } /** Delegate to the underlying stream (which must be open already). * @see java.io.InputStream#reset() */ public void reset() throws IOException { if (theRealStream == null) { return; } theRealStream.reset(); } /** Delegate to the underlying stream (which must be open already). * @see java.io.InputStream#skip(long) */ public long skip(long n) throws IOException { if (theRealStream == null) { return 0; //emulate EOF } return theRealStream.skip(n); } /** Delegate to the underlying stream (which must be open already). * @see java.lang.Object#toString() */ public String toString() { String streamInfo = ""; if (theRealStream == null) { streamInfo += "no stream"; } else { streamInfo += theRealStream.toString(); } return this.getFileID() + "\n\t" + streamInfo; } }