net.roydesign.io.DocumentFile Maven / Gradle / Ivy
Show all versions of mrjadapter Show documentation
/*******************************************************************************
File: DocumentFile.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/12/03 Created this file - Steve
03/31/03 Modified openWith() to call into ApplicationFile instead - Steve
06/17/03 Use net.roydesign.mac.MRJAdapter - Steve
*******************************************************************************/
package net.roydesign.io;
import com.apple.eio.FileManager;
import com.apple.mrj.MRJFileUtils;
import com.apple.mrj.MRJOSType;
import net.roydesign.mac.MRJAdapter;
import java.io.File;
import java.io.IOException;
/**
* A document file is a file meant to be opened in an application. Most
* platforms have their own way of binding a document to an application,
* and this class attempts to incorporate the smarts for those varied
* mechanisms. Of interest is the fact that all methods in this class
* are cross-platform, even the methods specific to a particular OS. Those
* methods will simply not do anything, or will act on an internal member
* variable. In any case, this allows your code to be free of ugly platform
* checks.
*
* In particular, this class supports the Mac OS type/creator binding
* mechanism. Even though the methods are specific to the Mac OS, they can
* be called on any platform. For example, calling {@code setMacCreator("ABCD")}
* won't put this information in the file system as it would on Mac OS, but
* it will still be tracked internally, so that a subsequent call to
* {@code getMacCreator()} will return the set value. This ensures that
* your code runs unchanged on all platforms.
*
* This class also exposes methods to easily change the filename extension
* of the document on disk. This is meant as an equivalent to the Mac specific
* type/creator facility so that the type and binding of a document can be
* set on platforms other that Mac OS.
*
* The document can be renamed, and this translates into the file
* on disk to be renamed as well. This is a convenience over having to use
* the {@code File.renameTo(File)} method and its associated pitfalls.
*
* Finally this class also makes it easy to open a document, either as
* though the user had double-clicked it in the file system, using its default
* binding, or with a given application of your choice. This functionality is
* typically highly platform dependent and error prone. Using this class makes
* it simple. All you have to do is {@code new DocumentFile(myFile).open()}.
*
* @version MRJ Adapter 1.2
*/
public class DocumentFile
{
/**
* The name of the OS, from the os.name system property.
*/
private static final String osName = System.getProperty("os.name");
/**
* The file represented by this document.
*/
private File file;
/**
* The Mac OS creator code of the file.
*/
private String macCreator = "";
/**
* The Mac OS type code of the file.
*/
private String macType = "";
/**
* Construct a document file with a path. This is identical
* to the {@code java.io.File} constructor.
* @param path the document path
*/
public DocumentFile(String path)
{
this.file = new File(path);
}
/**
* Construct a document file with a parent directory and the
* name or subpath of a child document 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 DocumentFile(String parent, String child)
{
this.file = new File(parent, child);
}
/**
* Construct a document file with a parent directory and the
* name or subpath of a child document 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 DocumentFile(File parent, String child)
{
this.file = new File(parent, child);
}
/**
* Construct a document file with a {@code java.io.File}
* object. Note that the given file object is not used internally.
* @param file the file object
*/
public DocumentFile(File file)
{
// We don't use the passed File object, because
// we want to have full control over it
this(file.getPath());
}
/**
* Set the Mac OS type code of the given file. This method does nothing
* on other platforms. The type code is a case-sensitive four character
* string. Any exceeding character will be ignored and the string will be
* padded with spaces if shorter, with the exception of the case where
* the string length is zero, in which case the file type will be set
* to nothing.
* @param file the file to set the type of
* @param type the type code
* @exception IOException when an I/O error occurs
*/
public static void setFileType(File file, String type) throws IOException
{
FileManager.setFileType(file.getAbsolutePath(), MRJAdapter.fourCharCodeToInt(type));
}
/**
* Set the Mac OS creator code of the given file. This method does
* nothing on other platforms. The creator code is a case-sensitive
* four character string. Any exceeding character will be ignored
* and the string will be padded with spaces if shorter, with the
* exception of the case where the string length is zero, in which
* case the file creator will be set to nothing.
* @param file the file to set the creator of
* @param creator the creator code
* @exception IOException when an I/O error occurs
*/
public static void setFileCreator(File file, String creator) throws IOException
{
FileManager.setFileCreator(file.getAbsolutePath(), MRJAdapter.fourCharCodeToInt(creator));
}
/**
* Set the Mac OS creator and type codes of the given file. This method
* does nothing on other platforms. The creator and type codes are
* case-sensitive four character strings. Any exceeding character will
* be ignored and the string will be padded with spaces if shorter, with
* the exception of the case where the string length is zero, in which
* case the file creator will be set to nothing.
* @param file the file to set the creator and type of
* @param creator the creator code
* @param type the type code
* @exception IOException when an I/O error occurs
*/
public static void setFileCreatorAndType(File file, String creator, String type)
throws IOException
{
FileManager.setFileTypeAndCreator(file.getAbsolutePath(),
MRJAdapter.fourCharCodeToInt(type), MRJAdapter.fourCharCodeToInt(creator));
}
/**
* Get the Mac OS type code of the given file. On other platforms,
* this method returns an empty string, which is the same as if
* the file type was not set on the Mac platform. A Mac type is
* a four character string, unless the file has no type, in which
* case an empty string is returned. Note that this method supports
* Mac OS style bundles on all platforms, which is useful in the
* case where such a bundle is temporarily located on a non-Mac
* file system. When this is detected, the method will extract the
* type code from the bundle just like Mac OS does natively.
* @param file the file to get to type from
* @return the type code
* @exception IOException when an I/O error occurs
*/
public static String getFileType(File file) throws IOException
{
int t = FileManager.getFileType(file.getAbsolutePath());
return MRJAdapter.intToFourCharCode(t);
}
/**
* Open the document in its native application. If the application
* can't be identified or found, this method will return false.
* You should handle this by bringing up a file dialog for the
* user to locate an application to use for opening the file. You
* can use the class {@code ApplicationDialog} for this.
* Currently, this method supports all flavors of Mac OS and Windows.
* On all other platforms, the value {@code false} is returned
* so you can take appropriate action, which should actually be the
* same action that you would take if you were on Mac OS/Windows and
* the method returned {@code false}.
* @return whether the file was opened successfully or not
* @exception IOException when an I/O error occurs
*/
public boolean open() throws IOException
{
try
{
// On Mac OS X, use 'open' on the command line
Process p = Runtime.getRuntime().exec(new String[] {"open", file.getAbsolutePath()});
if (p.waitFor() != 0)
return false;
}
catch (InterruptedException e)
{
return false;
}
// We get here when the document was opened successfully,
// so be happy and let the user know
return true;
}
/**
* Open the document with the given application file. If the operation
* fails or an error occurs, this method will return false.
* @param application the application file to use to open the document
* @return whether the file was opened successfully or not
* @exception IOException when an I/O error occurs
*/
public boolean openWith(ApplicationFile application) throws IOException
{
return application.openDocument(this);
}
/**
* Open the document with the given application. If the operation
* fails or an error occurs, this method will return false.
* @param application the application to use to open the document
* @return whether the file was opened successfully or not
* @exception IOException when an I/O error occurs
*/
public boolean openWith(File application) throws IOException
{
return openWith(new ApplicationFile(application));
}
/**
* Set the type code of the file as used on Mac OS and Mac OS X.
* On all other platforms, this method does nothing to the file
* on disk but still keeps track of the assigned type internally
* so that {@code getMacType()} returns the assigned value.
* @param type the Mac type code to assign
* @exception IOException when an I/O error occurs
* @see DocumentFile#setFileType
*/
public void setMacType(String type) throws IOException
{
this.macType = type;
setFileType(file, type);
}
/**
* Get the type code of a file as used on Mac OS and Mac OS X.
* On all other platforms, this method will returned the value set
* in a previous call to {@code setMacType()}. Otherwise an
* empty string is returned.
* @return the Mac type code of the file
* @exception IOException when an I/O error occurs
* @see DocumentFile#getFileType
*/
public String getMacType() throws IOException
{
String t = getFileType(file);
if (t.isEmpty() && macType != null)
return macType;
return t;
}
/**
* Set the creator code of the file as used on Mac OS and Mac OS X.
* On all other platforms, this method does nothing to the file
* on disk but still keeps track of the assigned creator internally
* so that {@code getMacCreator()} returns the assigned value.
* @param creator the Mac creator code to assign
* @exception IOException when an I/O error occurs
* @see DocumentFile#setFileCreator
*/
public void setMacCreator(String creator) throws IOException
{
this.macCreator = creator;
setFileCreator(file, creator);
}
/**
* Get the creator code of a file as used on Mac OS and Mac OS X.
* On all other platforms, this method will returned the value set
* in a previous call to {@code setMacCreator()}. Otherwise an
* empty string is returned.
* @return the Mac creator code of the file
* @exception IOException when an I/O error occurs
* @see MRJAdapter#getFileCreator
*/
public String getMacCreator() throws IOException
{
String c = MRJAdapter.getFileCreator(file);
if (c.isEmpty() && macCreator != null)
return macCreator;
return c;
}
/**
* Set the creator code of the file as used on Mac OS and Mac OS X.
* On all other platforms, this method does nothing to the file
* on disk but still keeps track of the assigned creator internally
* so that {@code getMacCreator()} returns the assigned value.
* @param creator the Mac creator code to assign
* @param type the Mac type code to assign
* @exception IOException when an I/O error occurs
* @see DocumentFile#setFileCreatorAndType
*/
public void setMacCreatorAndType(String creator, String type) throws IOException
{
this.macCreator = creator;
this.macType = type;
setFileCreatorAndType(file, creator, type);
}
/**
* Set the name extension of the file as used on many platforms to
* designate the file type and the application binding. The
* extension must not start with a period. Since the file will
* be renamed on disk, it's up to you to verify if a file
* with the new title and extension already exists prior to calling
* this method. An I/O exception will be thrown if the document
* can't be renamed for whatever reason.
* @param extension the new extension of the document
* @exception IOException if the method fails to rename the document
*/
public void setExtension(String extension) throws IOException
{
StringBuilder b = new StringBuilder();
b.append(getTitle());
if (extension != null && !extension.isEmpty())
{
b.append('.');
b.append(extension);
}
File f = new File(file.getParent(), b.toString());
if (!file.renameTo(f))
throw new IOException("failed to rename file");
this.file = f;
}
/**
* Get the name extension of a file as used on many platforms to
* designate the file type and the application binding. The extension
* is the name of the document on disk, minus the title and not
* including the period.
* @return the name extension of the document
* @exception IOException when an I/O error occurs
*/
public String getExtension() {
String n = file.getName();
int pos = n.lastIndexOf('.');
if (pos != -1 && pos + 1 != n.length())
return n.substring(pos + 1);
return "";
}
/**
* Set the title of the document file. This method sets the title
* of the document, which corresponds to the name of the document
* file minus its filename extension. Since the file will be
* renamed on disk, it's up to you to verify if a file with the
* new title (and extension) already exists prior to calling this
* method. An I/O exception will be thrown if the document
* can't be renamed for whatever reason.
* @param title the new title of the document
* @exception IOException if the method fails to rename the document
*/
public void setTitle(String title) throws IOException
{
if (title == null || title.isEmpty())
throw new IllegalArgumentException("title can't be null or zero length");
StringBuilder b = new StringBuilder();
b.append(title);
String ext = getExtension();
if (ext != null && !ext.isEmpty())
{
b.append('.');
b.append(ext);
}
File f = new File(file.getParent(), b.toString());
if (!file.renameTo(f))
throw new IOException("failed to rename file");
this.file = f;
}
/**
* Get the title of the document file. The title of the document
* is the name of the file on disk minus the filename extension.
* @return the title of the document
* @exception IOException when an I/O error occurs
*/
public String getTitle() {
String n = file.getName();
int pos = n.lastIndexOf('.');
if (pos != -1 && pos != 0 && pos + 1 != n.length())
return n.substring(0, pos);
return n;
}
/**
* Set the title and extension of the document file. Since the
* file will be renamed on disk, it's up to you to verify if a file
* with the new title and extension already exists prior to calling
* this method. An I/O exception will be thrown if the document
* can't be renamed for whatever reason.
* @param title the new title of the document
* @param extension the new extension of the document
* @exception IOException if the method fails to rename the document
*/
public void setTitleAndExtension(String title, String extension) throws IOException
{
if (title == null || title.isEmpty())
throw new IllegalArgumentException("title can't be null or zero length");
StringBuilder b = new StringBuilder();
b.append(title);
if (extension != null && !extension.isEmpty())
{
b.append('.');
b.append(extension);
}
File f = new File(file.getParent(), b.toString());
if (!file.renameTo(f))
throw new IOException("failed to rename file");
this.file = f;
}
/**
* Get the file object representing this document on disk.
* @return the file object for this document
*/
public File getFile()
{
return file;
}
/**
* Get the path to the document file on disk.
* @return the path to the document
* @see File#getPath
*/
public String getPath()
{
return file.getPath();
}
/**
* Get the absolute path to the document file on disk.
* @return the absolute path to the document
* @see File#getAbsolutePath
*/
public String getAbsolutePath()
{
return file.getAbsolutePath();
}
/**
* Get the canonical path to the document file on disk.
* @return the canonical path to the document
* @see File#getCanonicalPath
*/
public String getCanonicalPath() throws IOException
{
return file.getCanonicalPath();
}
}