package com.limegroup.gnutella.util;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.StringTokenizer;
import com.limegroup.gnutella.MediaType;
import com.limegroup.gnutella.settings.URLHandlerSettings;
/**
* This class launches files in their associated applications and opens
* urls in the default browser for different operating systems. This
* really only works meaningfully for the Mac and Windows.<p>
*
* Acknowledgement goes to Eric Albert for demonstrating the general
* technique for loading the MRJ classes in his frequently-used
* "BrowserLauncher" code.
* <p>
* This code is Copyright 1999-2001 by Eric Albert (ejalbert@cs.stanford.edu)
* and may be redistributed or modified in any form without restrictions as
* long as the portion of this comment from this paragraph through the end of
* the comment is not removed. The author requests that he be notified of any
* application, applet, or other binary that makes use of this code, but that's
* more out of curiosity than anything and is not required. This software
* includes no warranty. The author is not repsonsible for any loss of data
* or functionality or any adverse or unexpected effects of using this software.
* <p>
* Credits:
* <br>Steven Spencer, JavaWorld magazine
* (<a href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java Tip 66</a>)
* <br>Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum,
* Andrea Cantatore, Larry Barowski, Trevor Bedzek, Frank Miedrich, and Ron
* Rabakukk
*
* @author Eric Albert
* (<a href="mailto:ejalbert@cs.stanford.edu">ejalbert@cs.stanford.edu</a>)
* @version 1.4b1 (Released June 20, 2001)
*/
//2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678|
public final class Launcher {
/**
* <tt>boolean</tt> specifying whether or not the necessary Mac
* classes were loaded successfully.
*/
private static boolean _macClassesLoadedSuccessfully = true;
/**
* The openURL method of com.apple.mrj.MRJFileUtils.
*/
private static Method _openURL;
/**
* Loads the necessary Mac classes if running on Mac.
*/
static {
if(CommonUtils.isMacOSX()) {
try {
loadMacClasses();
} catch(IOException ioe) {
_macClassesLoadedSuccessfully = false;
}
}
}
/**
* This class should be never be instantiated; this just ensures so.
*/
private Launcher() {}
/**
* Opens the specified url in a browser.
*
* <p>A browser will only be opened if the underlying operating system
* recognizes the url as one that should be opened in a browser,
* namely a url that ends in .htm or .html.
*
* @param url The url to open
*
* @return An int indicating the success of the browser launch
*
* @throws IOException if the url cannot be loaded do to an IO problem
*/
public static int openURL(String url) throws IOException {
if(CommonUtils.isWindows()) {
return openURLWindows(url);
}
else if(CommonUtils.isMacOSX()) {
openURLMac(url);
}
else {
// Other OS
launchFileOther(url);
}
return -1;
}
/**
* Opens the default web browser on windows, passing it the specified
* url.
*
* @param url the url to open in the browser
* @return the error code of the native call, -1 if the call failed
* for any reason
*/
private static int openURLWindows(String url) throws IOException {
return new WindowsLauncher().openURL(url);
}
/**
* Opens the specified url in the default browser on the Mac.
* This makes use of the dynamically-loaded MRJ classes.
*
* @param url the url to load
*
* @throws <tt>IOException</tt> if the necessary mac classes were not
* loaded successfully or if another exception was
* throws -- it wraps these exceptions in an <tt>IOException</tt>
*/
private static void openURLMac(String url) throws IOException {
if(!_macClassesLoadedSuccessfully) throw new IOException();
try {
Object[] params = new Object[] {url};
_openURL.invoke(null, params);
}
catch (NoSuchMethodError err) {
throw new IOException();
// this can occur when earlier versions of MRJ are used which
// do not support the openURL method.
} catch (NoClassDefFoundError err) {
throw new IOException();
// this can occur under runtime environments other than MRJ.
} catch (IllegalAccessException iae) {
throw new IOException();
} catch (InvocationTargetException ite) {
throw new IOException();
}
}
/**
* Launches the file whose abstract path is specified in the
* <tt>File</tt> parameter. This method will not launch any file
* with .exe, .vbs, .lnk, .bat, .sys, or .com extensions, diplaying
* an error if one of the file is of one of these types.
*
* @param path The path of the file to launch
*
* @return An int indicating the success of the browser launch
*
* @throws IOException if the file cannot be launched do to an IO problem
*/
public static int launchFile(File file) throws IOException, SecurityException {
String path = file.getCanonicalPath();
String extCheckString = path.toLowerCase();
// Expand pmf files before display
if ( extCheckString.endsWith(".pmf") ) {
file = PackagedMediaFileUtils.preparePMFFile(file.toString());
// Don't launch an invalid file
if ( file == null )
return -1;
path = file.getCanonicalPath();
extCheckString = path.toLowerCase();
}
if(!extCheckString.endsWith(".exe") &&
!extCheckString.endsWith(".vbs") &&
!extCheckString.endsWith(".lnk") &&
!extCheckString.endsWith(".bat") &&
!extCheckString.endsWith(".sys") &&
!extCheckString.endsWith(".com")) {
if(CommonUtils.isWindows()) {
return launchFileWindows(path);
}
else if(CommonUtils.isMacOSX()) {
launchFileMacOSX(path);
}
else {
// Other OS, use helper apps
launchFileOther(path);
}
}
else {
throw new SecurityException();
}
return -1;
}
/**
* Windows: Launches the Explorer and highlights the file.
* Mac OS X: Launches the Finder and opens the file if it is
* a directory or its parent if it is a file
*
* @param file
* @return true on success
* @throws IOException
*/
public static boolean launchExplorer(File file) throws IOException, SecurityException {
if (CommonUtils.isWindows()) {
String explorePath = file.getPath();
try {
explorePath = file.getCanonicalPath();
} catch(IOException ioe) { }
//launches explorer and highlights the file
Runtime.getRuntime().exec(new String[] {"explorer","/select,", explorePath });
return true;
} else if (CommonUtils.isMacOSX()) {
if(file.isFile()) {
launchFile(file.getParentFile());
} else {
launchFile(file);
}
return true;
}
return false;
}
/**
* Launches the given file on Windows.
*
* @param path the path of the file to launch
*
* @return an int for the exit code of the native method
*/
private static int launchFileWindows(String path) throws IOException {
return new WindowsLauncher().launchFile(path);
}
/**
* Launches a file on OSX, appending the full path of the file to the
* "open" command that opens files in their associated applications
* on OSX.
*
* @param file the <tt>File</tt> instance denoting the abstract pathname
* of the file to launch
* @throws IOException if an I/O error occurs in making the runtime.exec()
* call or in getting the canonical path of the file
*/
private static void launchFileMacOSX(final String file) throws IOException {
Runtime.getRuntime().exec(new String[]{"open", file});
}
/**
* Loads specialized classes for the Mac needed to launch files.
*
* @return <tt>true</tt> if initialization succeeded,
* <tt>false</tt> if initialization failed
*
* @throws <tt>IOException</tt> if an exception occurs loading the
* necessary classes
*/
private static void loadMacClasses() throws IOException {
try {
Class mrjAdapter = Class.forName("net.roydesign.mac.MRJAdapter");
_openURL = mrjAdapter.getDeclaredMethod("openURL", new Class[]{String.class});
} catch (ClassNotFoundException cnfe) {
throw new IOException();
} catch (NoSuchMethodException nsme) {
throw new IOException();
} catch (SecurityException se) {
throw new IOException();
}
}
/**
* Attempts to launch the given file.
* NOTE: WE COULD DO THIS ONE BETTER!!
*
* @throws IOException if the call to Runtime.exec throws an IOException
* or if the Process created by the Runtime.exec call
* throws an InterruptedException
*/
private static void launchFileOther(String path) throws IOException {
String handler;
if (MediaType.getAudioMediaType().matches(path)) {
handler = URLHandlerSettings.AUDIO_PLAYER.getValue();
} else if (MediaType.getVideoMediaType().matches(path)) {
handler = URLHandlerSettings.VIDEO_PLAYER.getValue();
} else if (MediaType.getImageMediaType().matches(path)) {
handler = URLHandlerSettings.IMAGE_VIEWER.getValue();
} else {
handler = URLHandlerSettings.BROWSER.getValue();
}
if (handler.indexOf("$URL$") != -1) {
System.out.println("starting " + handler);
StringTokenizer tok = new StringTokenizer (handler);
String[] strs = new String[tok.countTokens()];
for (int i = 0; tok.hasMoreTokens(); i++) {
strs[i] = StringUtils.replace(tok.nextToken(), "$URL$", path);
System.out.print(" "+strs[i]);
}
try {
Runtime.getRuntime().exec(strs);
} catch(IOException e) {
e.printStackTrace();
}
} else {
System.out.println("starting " + handler);
String[] strs = {handler, path};
Runtime.getRuntime().exec(strs);
}
}
}