All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.roydesign.io.ApplicationFile Maven / Gradle / Ivy

Go to download

MRJ Adapter is a wrapper around built in Java Virtual Machine APIs provided by Apple.

The newest version!
/*******************************************************************************

	File:		ApplicationFile.java
	Author:		Steve Roy 
				
	Part of MRJ Adapter, a unified API for easy integration of Mac OS specific
	functionality within your cross-platform Java application.
	
	This library is open source and can be modified and/or distributed under
	the terms of the Artistic License.
	

	Change History:
	02/20/03	Created this file - Steve
	03/31/03	Added instantiation of osName, which was otherwise undefined,
				implemented the two getMacBundleResource() methods, provided
				a better implementation of open() and openDocuments() - Steve
	06/17/03	Use net.roydesign.mac.MRJAdapter - Steve

*******************************************************************************/

package net.roydesign.io;

import net.roydesign.mac.MRJAdapter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

/**
 * An application file is the executable file on disk for an application. This
 * class defines methods to locate, inspect and launch such applications and
 * integrates a few methods to handle traits specific to the Mac OS platform.
 * Due to the intrinsic platform-dependant nature of some of this functionality,
 * this is always work in progress. Support for classic Mac OS, Mac OS X, and
 * Windows is currently implemented.
 * 
 * @version MRJ Adapter 1.2
 */
public class ApplicationFile
{
	/**
	 * The name of the OS, from the os.name system property.
	 */
	private static final String osName = System.getProperty("os.name");

	/**
	 * The executable file on disk.
	 */
	private final File executable;

	/**
	 * Construct an application file with a path. This is identical
	 * to the {@code java.io.File} constructor.
	 * @param path the executable path
	 */
	public ApplicationFile(String path)
	{
		this.executable = new File(path);
	}

	/**
	 * Construct an application file with a parent directory and the
	 * name or subpath of a child executable file.  This is identical
	 * to the {@code java.io.File} constructor.
	 * @param parent the parent directory
	 * @param child the child directory path or file name
	 */
	public ApplicationFile(String parent, String child)
	{
		this.executable = new File(parent, child);
	}

	/**
	 * Construct an application file with a parent directory and the
	 * name or subpath of a child executable file.  This is identical
	 * to the {@code java.io.File} constructor.
	 * @param parent the parent directory
	 * @param child the child directory path or file name
	 */
	public ApplicationFile(File parent, String child)
	{
		this.executable = new File(parent, child);
	}

	/**
	 * Construct an application file with a {@code java.io.File}
	 * object. Note that the given file object is not used internally.
	 * @param executable the executable file object
	 */
	public ApplicationFile(File executable)
	{
		// We don't use the passed File object, because
		// we want to have full control over it
		this(executable.getPath());
	}

	/**
	 * Utility method to read and parse the content of the given Mac OS
	 * MRJApp.properties file, trying to extract the value of the given key.
	 * The keys that can be found in the Info.plist are defined by Apple.
	 * If the given key is not found, the value {@code null} is returned.
	 * @param file the MRJApp.properties file
	 * @param key the key to look for
	 * @return the value of the key, or null
	 * @exception IOException when an I/O error occurs
	 */
	public static String parseMRJAppProperties(File file, String key) throws IOException
	{
		FileInputStream in = new FileInputStream(file);
		Properties props = new Properties();
		props.load(in);
		in.close();
		return props.getProperty(key);
	}

	/**
	 * Launch the application.
	 * @return whether the file was opened successfully or not
	 * @exception IOException when any error occurs
	 */
	public boolean open() throws IOException
	{
		try
		{
			Process p = Runtime.getRuntime().exec(new String[]
				{"open", "-a", executable.getAbsolutePath()});
			if (p.waitFor() != 0)
				return false;
		}
		catch (InterruptedException e)
		{
			return false;
		}

		// We get here when the application was opened successfully,
		// so be happy and let the user know
		return true;
	}

	/**
	 * Launch the application with the given arguments.
	 * @param args the arguments to pass to the application
	 * @return the application process
	 * @exception IOException when any error occurs
	 */
	public Process open(String... args) throws IOException
	{
		/** @todo Need to provide a better way to start processes */
		String[] nargs = new String[args.length + 1];
		nargs[0] = executable.getAbsolutePath();
		System.arraycopy(args, 0, nargs, 1, args.length);
		return Runtime.getRuntime().exec(nargs);
	}

	/**
	 * Open the given document. Note that the behavior of this method
	 * is platform specific. Some platforms allow multiple instances of
	 * an application to execute simultaneously, some others don't, and
	 * some others have mixed behaviors. For example, on Mac OS X,
	 * double-clickable applications are not allowed to have multiple
	 * instances but command line tools can.
	 * @param documentFile the document to be opened
	 * @return whether the document was opened successfully or not
	 * @exception IOException when any error occurs
	 */
	public boolean openDocument(DocumentFile documentFile) throws IOException
	{
		return openDocument(documentFile.getFile());
	}

	/**
	 * Open the given file. Note that the behavior of this method
	 * is platform specific. Some platforms allow multiple instances of
	 * an application to execute simultaneously, some others don't, and
	 * some others have mixed behaviors. For example, on Mac OS X,
	 * double-clickable applications are not allowed to have multiple
	 * instances but command line tools can.
	 * @param file the file to be opened
	 * @return whether the file was opened successfully or not
	 * @exception IOException when any error occurs
	 */
	public boolean openDocument(File file) throws IOException
	{
		return openDocuments(file);
	}

	/**
	 * Open the given documents. Note that the behavior of this method
	 * is platform specific. Some platforms allow multiple instances of
	 * an application to execute simultaneously, some others don't, and
	 * some others have mixed behaviors. For example, on Mac OS X,
	 * double-clickable applications are not allowed to have multiple
	 * instances but command line tools can.
	 * @param documentFiles the documents to be opened
	 * @return whether the documents were opened successfully or not
	 * @exception IOException when any error occurs
	 */
	public boolean openDocuments(DocumentFile... documentFiles) throws IOException
	{
		int numFiles = documentFiles.length;
		File[] files = new File[numFiles];
		for (int i = 0; i < numFiles; i++)
			files[i] = documentFiles[i].getFile();
		return openDocuments(files);
	}

	/**
	 * Open the given files. Note that the behavior of this method
	 * is platform specific. Some platforms allow multiple instances of
	 * an application to execute simultaneously, some others don't, and
	 * some others have mixed behaviors. For example, on Mac OS X,
	 * double-clickable applications are not allowed to have multiple
	 * instances but command line tools can.
	 * @param files the files to be opened
	 * @return whether the files were opened successfully or not
	 * @exception IOException when any error occurs
	 */
	public boolean openDocuments(File... files) throws IOException
	{
		int numFiles = files.length;
		try
		{
			// On Mac OS X, use 'open' on the command line
			String[] strs = new String[3 + numFiles];
			strs[0] = "open";
			strs[1] = "-a";
			strs[2] = executable.getAbsolutePath();
			for (int i = 0; i < numFiles; i++)
				strs[3 + i] = files[i].getAbsolutePath();
			Process p = Runtime.getRuntime().exec(strs);
			if (p.waitFor() != 0)
				return false;
		}
		catch (InterruptedException e)
		{
			return false;
		}

		// We get here when the documents were opened successfully,
		// so be happy and let the user know
		return true;
	}

	/**
	 * Get the path to the application file on disk.
	 * @return the path to the application
	 * @see File#getPath
	 */
	public String getPath()
	{
		return executable.getPath();
	}

	/**
	 * Get the absolute path to the application file on disk.
	 * @return the absolute path to the application
	 * @see File#getAbsolutePath
	 */
	public String getAbsolutePath()
	{
		return executable.getAbsolutePath();
	}

	/**
	 * Get the canonical path to the application file on disk.
	 * @return the canonical path to the application
	 * @see File#getCanonicalPath
	 */
	public String getCanonicalPath() throws IOException
	{
		return executable.getCanonicalPath();
	}

	// exists() ?
	// canExecute() ?
	// getLocalizedName() ?
	// getParent() ?
	// getParentFile() ?
	// isMacBundle() ?
	// isCFM() ?
	// isMachO() ?
	// isBoth() ?

	/**
	 * Get the name of the executable file on disk.
	 * @return the name of the executable
	 */
	public String getExecutableName()
	{
		return executable.getName();
	}

	/**
	 * Get the name displayed by the application to the user.
	 * @return the name displayed to the user
	 * @exception IOException when an I/O error occurs
	 */
	public String getDisplayedName() throws IOException
	{
		if (executable.isDirectory())
		{
			// Try to extract it out of the bundle
			File mrjAppProperties = new File(executable, "Contents/MRJApp.properties");
			if (mrjAppProperties.exists())
			{
				String name =
					parseMRJAppProperties(mrjAppProperties, "com.apple.mrj.application.apple.menu.about.name");
				if (name != null)
					return name;
			}
			/** @todo Add support for InfoPlist.strings */
			File infoPlist = new File(executable, "Contents/Info.plist");
			if (infoPlist.exists())
			{
				String name = MRJAdapter.parseInfoPlist(infoPlist, "com.apple.mrj.application.apple.menu.about.name");
				if (name == null)
				{
					name = MRJAdapter.parseInfoPlist(infoPlist, "CFBundleName");
					if (name == null)
						name = MRJAdapter.parseInfoPlist(infoPlist, "CFBundleExecutable");
				}
				return name;
			}
		}
		return getExecutableName();
	}

	/**
	 * Get the Mac OS creator code of the application. On the Mac OS and
	 * Mac OS X, this method will return the assigned case-sensitive four
	 * character type string. On all other platforms, the behavior depends
	 * whether the application is a Mac OS bundled application that is
	 * temporarily sitting on another platform, or not. When this is the
	 * case, this method will recognize the bundled app and will extract
	 * the Mac OS creator code from it. This will only work for correctly
	 * bundled applications. Otherwise, an empty string will be returned
	 * to provide a consistent cross-platform behavior.
	 * @return the Mac OS creator code, or an empty string
	 * @exception IOException when an I/O error occurs
	 * @see MRJAdapter#getFileCreator
	 */
	public String getMacCreator() throws IOException
	{
		return MRJAdapter.getFileCreator(executable);
	}

	/**
	 * Get a Mac OS resource file from the application bundle. Resource files
	 * are stored in the folder Contents/Resources inside the bundle. This
	 * method will locate the requested resource only if it's located at the
	 * top level of the Resources folder. It returns a {@code File}
	 * object that can be used to read the resource.
	 * @param resource the name of the resource file
	 * @return the resource file
	 * @exception FileNotFoundException when the resource can't be found
	 * @see MRJAdapter#getBundleResource
	 */
	public File getMacBundleResource(String resource) throws FileNotFoundException
	{
		return MRJAdapter.getBundleResource(resource);
	}

	/**
	 * Get a Mac OS resource file from the application bundle. Resource files
	 * are stored in the folder Contents/Resources inside the bundle. This
	 * method will locate the requested resource only if it's located in the
	 * specified subfolder of the Resources folder. It returns a {@code File}
	 * object that can be used to read the resource.
	 * @param resource the name of the resource file
	 * @param subFolder the name of the subfolder of Resources
	 * @return the resource file
	 * @exception FileNotFoundException when the resource can't be found
	 * @see MRJAdapter#getBundleResource
	 */
	public File getMacBundleResource(String resource, String subFolder)
		throws FileNotFoundException
	{
		return MRJAdapter.getBundleResource(resource, subFolder);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy