/*
* FileUtil.java
*
* Created on April 1, 2005, 11:31 AM
*/
package edu.oregonstate.cartography.app;
import edu.oregonstate.cartography.gui.ErrorDialog;
import java.io.*;
import java.awt.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
/**
* FileUtils - file related utility methods.
*
* @author Bernhard Jenny, Institute of Cartography, ETH Zurich.
*/
public class FileUtils {
private static final boolean IS_MAC_OSX;
static {
String osname = System.getProperty("os.name");
IS_MAC_OSX = osname.toLowerCase().startsWith("mac os x");
}
/**
* Makes sure that a given name of a file has a certain file extension.<br>
* Existing file extension that are different from the required one are not
* removed. The fileName is trimmed in any case (i.e. leading and trailing
* non-printable characters are remmoved).
*
* @param fileName The name of the file.
* @param ext The extension of the file that will be appended if necessary.
* @return The trimmed file name with the required extension.
*/
public static String forceFileNameExtension(String fileName, String ext) {
if (fileName == null) {
return null;
}
if (ext == null) {
return fileName;
}
fileName = fileName.trim();
ext = ext.trim();
String fileNameLower = fileName.toLowerCase();
String extLower = ext.toLowerCase();
// test if the fileName has the required extension
if (!fileNameLower.endsWith("." + extLower)) {
// fileName has wrong extension: add an extension
if (!fileNameLower.endsWith(".")) {
fileName = fileName.concat(".");
}
fileName = fileName.concat(ext); // add extension
}
return fileName;
}
/**
* Test whether a file name ends with a specific extension.
*
* @param fileName The path to the file.
* @param ext The required extension.
* @return True if the file ends with the required extension, false
* otherwise.
*/
public static boolean hasExtension(String fileName, String ext) {
if (fileName == null || ext == null) {
return false;
}
String fileNameLower = fileName.trim().toLowerCase();
String extLower = ext.trim().toLowerCase();
return fileNameLower.endsWith("." + extLower);
}
/**
* If the file at filePath exists, a warning message is displayed to the
* user and true is returned. This method is intended to be used after a
* dialog for specifying the name and path of a new file. If an extension is
* required, but the user does not enter an extension, the extension can be
* added programmatically. If this new concatenated file path conflicts with
* an existing file, a warning should be displayed and the operation should
* be aborted.
*
* @param filePath The path to the file.
* @param ext The user is informed to append this extension to the file name
* when entering a name for a new file.
* @return True if the file exists, false otherwise.
*/
public static boolean warningIfFileExists(String filePath, String ext) {
String newline = System.getProperty("line.separator");
if (filePath == null) {
throw new IllegalArgumentException();
}
filePath = filePath.trim();
String fileName = FileUtils.getFileName(filePath);
String parentDirectoryPath = FileUtils.getParentDirectoryPath(filePath);
if (ext != null) {
ext = ext.trim();
}
boolean fileExists = new File(filePath).exists();
if (fileExists) {
StringBuilder sb = new StringBuilder();
sb.append("The file \"");
sb.append(fileName);
sb.append("\" already exists at").append(newline);
sb.append(parentDirectoryPath);
sb.append(".").append(newline);
sb.append("Please try again");
if (ext != null) {
sb.append(" and add the extension \".");
sb.append(ext);
sb.append("\" to the file name");
}
sb.append(".");
String title = "File Already Exists";
ErrorDialog.showErrorDialog(sb.toString(), title);
}
return fileExists;
}
/**
* Returns the file extension from a passed file path.
*/
public static String getFileExtension(String fileName) {
final int dotIndex = fileName.lastIndexOf('.');
if (dotIndex == -1) {
return new String();
}
return fileName.substring(dotIndex + 1);
}
/**
* Change the extension of a file path. The extension is what follows the
* last dot '.' in the path. If no dot exists in the path, the passed
* extension is simply appended without replacing anything.
*
* @param filePath The path of the file with the extension to replace.
* @param newExtension The new extension for the file, e.g. "tif".
* @return A new path to a file. The file may not actually exist on the hard
* disk.
*/
public static String replaceExtension(String filePath, String newExtension) {
final int dotIndex = filePath.lastIndexOf('.');
if (dotIndex == -1) {
return filePath + "." + newExtension;
}
return filePath.substring(0, dotIndex + 1) + newExtension;
}
/**
* Change the extension of a file path. The extension is what follows the
* last dot '.' in the path. If no dot exists in the path, the passed
* extension is simply appended without replacing anything.
*
* @param filePath The path of the file with the extension to replace.
* @param newExtension The new extension for the file, e.g. "tif".
* @param maxExtensionLength The maximum length of the extension to remove.
* @return A new path to a file. The file may not actually exist on the hard
* disk!
*/
public static String replaceExtension(String filePath, String newExtension,
int maxExtensionLength) {
filePath = FileUtils.cutFileExtension(filePath, maxExtensionLength);
return FileUtils.replaceExtension(filePath, newExtension);
}
/**
* Removes the path to the parent folder and also the extension of a file
* path.
*
* @return The name of the file without the path to its parent folder and
* without the file extension.
*/
public static String getFileNameWithoutExtension(String filePath) {
return getFileName(cutFileExtension(filePath));
}
/**
* Removes the path to the parent folder.
*
* @return The name of the file without the path to its parent folder. Null
* if the passed fileName is null.
*/
public static String getFileName(String filePath) {
if (filePath == null) {
return null;
}
// cut the path to the parent folder
String pathSeparator = System.getProperty("file.separator");
final int pathSeparatorIndex = filePath.lastIndexOf(pathSeparator);
if (pathSeparatorIndex != -1) {
filePath = filePath.substring(pathSeparatorIndex + 1, filePath.length());
}
return filePath;
}
/**
* Returns the parent directory for a file.
*
* @param filePath Path to a file.
* @return For "/Volumes/toto/gaga.txt" returns "/Volumes/toto/". Returns
* filePath if is is a path to a directory.
*/
public static String getParentDirectoryPath(String filePath) {
if (filePath == null) {
return null;
}
// cut the path to the parent folder
String pathSeparator = System.getProperty("file.separator");
final int pathSeparatorIndex = filePath.lastIndexOf(pathSeparator);
if (pathSeparatorIndex != -1) {
filePath = filePath.substring(0, pathSeparatorIndex + 1);
}
return filePath;
}
/**
* Removes the extension of a file path. Does not remove extensions longer
* than 3 characters.
*
* @return The name of the file without the file extension.
*/
public static String cutFileExtension(String fileName) {
return cutFileExtension(fileName, 3);
}
/**
* Removes the extension of a file path.
*
* @param fileName The path to the file.
* @param maxExtensionLength The maximum length of the extension to remove.
* If the extension is shorter, it is not removed.
* @return The name of the file without the file extension.
*/
public static String cutFileExtension(String fileName, int maxExtensionLength) {
final int dotIndex = fileName.lastIndexOf('.');
if (dotIndex == -1) {
return fileName;
}
final int extensionLength = fileName.length() - dotIndex - 1;
if (extensionLength <= maxExtensionLength) {
return fileName.substring(0, dotIndex);
}
return fileName;
}
/**
* Ask the user for a file using the AWT FileDialog.
*/
private static String askAWTFile(java.awt.Frame frame, String message,
String defaultFile, boolean load) {
// use AWT FileDialog on mac
final int flag = load ? FileDialog.LOAD : FileDialog.SAVE;
// build dummy Frame if none is passed as parameter.
if (frame == null) {
frame = new Frame();
}
FileDialog fd = new FileDialog(frame, message, flag);
fd.setFile(defaultFile);
fd.setVisible(true);
String fileName = fd.getFile();
String directory = fd.getDirectory();
if (fileName == null || directory == null) {
return null;
}
return directory + fileName;
}
/**
* Returns true if the passed file can be written.
*
* @param file
* @param parent
* @return
*/
private static boolean askOverwrite(File file, Component parent) {
if (file.exists()) {
StringBuilder sb = new StringBuilder("<html>\"");
sb.append(file.getName());
sb.append("\" already exists. Do you want<br>to replace it?</html>");
String msg = sb.toString();
String title = "";
String[] options = new String[]{"Cancel", "Replace"};
int res = JOptionPane.showOptionDialog(parent,
msg,
title,
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE,
null,
options,
options[0]);
return res == 1;
}
return true;
}
/**
* Ask the user for a file using the Swing JFileChooser.
*/
private static String askSwingFile(java.awt.Frame frame, String message,
String defaultFile, boolean load) {
// use Swing FileChooser on other systems
JFileChooser fc = new JFileChooser();
fc.setDialogTitle(message);
File selFile;
// set default file
try {
File f = new File(new File(defaultFile).getCanonicalPath());
fc.setSelectedFile(f);
} catch (Exception e) {
}
int result = JFileChooser.ERROR_OPTION;
do {
if (load) {
// Show open dialog
result = fc.showOpenDialog(frame);
} else {
// Show save dialog
result = fc.showSaveDialog(frame);
}
if (result != JFileChooser.APPROVE_OPTION) {
return null;
}
selFile = fc.getSelectedFile();
if (selFile == null) {
return null;
}
} while (!load && !askOverwrite(selFile, fc));
return selFile.getPath();
}
/**
* A private utility class that wraps calls to FileUtils.askAWTFile and
* FileUtils.askSwingFile into a Runnable object.
*/
private static class GUI implements Runnable {
public String filePath;
public Frame frame;
public String message;
public String defaultFile;
public boolean load;
public String ext;
public void run() {
if (FileUtils.IS_MAC_OSX) {
filePath = FileUtils.askAWTFile(frame, message, defaultFile, load);
} else {
filePath = FileUtils.askSwingFile(frame, message, defaultFile, load);
}
// append the required file extension if necessary
if (!load && filePath != null && ext != null) {
//
if (new File(filePath).exists() && FileUtils.hasExtension(filePath, ext)) {
return;
}
// append the extension if necessary
String extendedFilePath = forceFileNameExtension(filePath, ext);
if (!extendedFilePath.equals(filePath)) {
// make sure there is not a conflict with an existing file
if (FileUtils.warningIfFileExists(extendedFilePath, ext)) {
filePath = null;
} else {
filePath = extendedFilePath;
}
}
}
}
};
/**
* Ask the user for a file to load or to write to. Uses the awt FileDialog
* on Mac and the JFileChooser on other platforms. Makes sure the dialog is
* displayed in the event dispatch thread.
*
* @param frame A Frame for which to display the dialog. Cannot be null.
* @param message A message that will be displayed in the dialog.
* @param defaultFile The default file name.
* @param load Pass true if an existing file for reading should be selected.
* Pass false if a new file for writing should be specified.
* @param ext A file extension that is required when selecting a file name
* for saving a file. Can be null.
* @return A path to the file, including the file name.
*/
public static String askFile(final java.awt.Frame frame, final String message,
final String defaultFile, final boolean load, final String ext) {
GUI gui = new GUI();
gui.frame = frame;
gui.message = message;
gui.defaultFile = defaultFile;
gui.load = load;
gui.ext = ext;
// make sure we run in the event dispatch thread.
SwingThreadUtils.invokeAndWait(gui);
return gui.filePath;
}
/**
* Ask the user for a file to load or to write to. Makes sure the dialog is
* displayed in the event dispatch thread.
*
* @param frame A Frame for which to display the dialog. Cannot be null.
* @param message A message that will be displayed in the dialog.
* @param load Pass true if an existing file for reading should be selected.
* Pass false if a new file for writing should be specified.
* @return A path to the file, including the file name.
*/
public static String askFile(java.awt.Frame frame, String message, boolean load) {
return FileUtils.askFile(frame, message, null, load, null);
}
public static String askDirectory(java.awt.Frame frame,
String message,
boolean load,
String defaultDirectory) throws IOException {
if (FileUtils.IS_MAC_OSX) {
try {
System.setProperty("apple.awt.fileDialogForDirectories", "true");
FileDialog fd = new FileDialog(frame, message, load ? FileDialog.LOAD : FileDialog.SAVE);
fd.setFile(defaultDirectory);
fd.setVisible(true);
String fileName = fd.getFile();
String directory = fd.getDirectory();
return (fileName == null || directory == null) ? null : directory + fileName;
} finally {
System.setProperty("apple.awt.fileDialogForDirectories", "false");
}
} else {
JFileChooser fc = new JFileChooser(defaultDirectory);
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fc.setDialogTitle(message);
fc.showOpenDialog(null);
File selFile = fc.getSelectedFile();
return selFile.getCanonicalPath();
}
}
/**
* Converts the contents of a file into a CharSequence suitable for use by
* the regex package. The matching routines in java.util.regex require that
* the input be a CharSequence object. This method efficiently returns the
* contents of a file in a CharSequence object. Based on
* http://javaalmanac.com/egs/java.util.regex/FromFile.html?l=rel
*
* @param filename The file path.
* @maxNbrBytes The maximum number of bytes that should be read. Pass 0 if
* all bytes should be read.
*/
public static CharSequence charSequenceFromFile(String filename, long maxNbrBytes)
throws IOException {
FileInputStream fis = new FileInputStream(filename);
FileChannel fc = fis.getChannel();
// Create a read-only CharBuffer on the file
int nbrBytesToRead = (int) Math.max(maxNbrBytes, fc.size());
ByteBuffer bbuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, nbrBytesToRead);
CharBuffer cbuf = Charset.forName("8859_1").newDecoder().decode(bbuf);
return cbuf;
}
/**
* Converts the contents of a file into a CharSequence suitable for use by
* the regex package.
*/
public static CharSequence charSequenceFromFile(String filename)
throws IOException {
return FileUtils.charSequenceFromFile(filename, 0);
}
public static byte[] getBytesFromFile(File file) throws IOException {
InputStream is = new FileInputStream(file);
// Get the size of the file
long length = file.length();
if (length > Integer.MAX_VALUE) {
throw new IOException("The file is too large.");
}
// Create the byte array to hold the data
byte[] bytes = new byte[(int) length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file " + file.getName() + ".");
}
// Close the input stream and return bytes
is.close();
return bytes;
}
public static File createTempDirectory() throws IOException {
final File temp;
temp = File.createTempFile("temp", Long.toString(System.nanoTime()));
if (!(temp.delete())) {
throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
}
if (!(temp.mkdir())) {
throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
}
return (temp);
}
}