/*
* $Id: FileUtils.java,v 1.4 2008-09-04 10:47:43 tom Exp $
*
* Copyright (C) 2002,2003 by Brockmann Consult (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation. This program is distributed in the hope it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package com.bc.util.io;
import sun.awt.shell.ShellFolder;
import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.FileImageOutputStream;
import java.io.File;
import java.io.IOException;
public class FileUtils {
public static final String[] PATH_SEPARATORS = {"\\", "/"};
/**
* Retrieves the filename with (eventual) extension from a complete path
*
* @param path the complete path
* @return the filename
*/
public static String getFileNameFromPath(String path) {
if (path == null) {
throw new IllegalArgumentException("argument path is null");
}
int lastChar = -1;
for (int i = 0; i < PATH_SEPARATORS.length; i++) {
lastChar = path.lastIndexOf(PATH_SEPARATORS[i]);
if (lastChar >= 0) {
break;
}
}
String fileName;
if (lastChar >= 0) {
fileName = path.substring(lastChar + 1, path.length());
} else {
fileName = path;
}
return fileName;
}
public static String getFileNameWithoutExtension(String fileName) {
final int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex >= 0) {
return fileName.substring(0, lastDotIndex);
} else {
return fileName;
}
}
/**
* Retrieves the size of a file object. Three cases can be distinguished:
* <ul>
* <li>The file object denotes a file: returns the result of File.length()</li>
* <li>The file object denotes a directory: returns the iteratively accumulated size of all files contained</li>
* <li>The file object does not exist or is null: returns 0</li>
* </ul>
*
* @param file the file to check
* @return the size
*/
public static long getSizeInBytes(File file) {
if ((file == null) || (!file.exists())) {
return 0;
}
if (file.isFile()) {
return file.length();
}
long size = 0;
final File[] content = file.listFiles();
if (content == null) {
return 0;
}
for (int i = 0; i < content.length; i++) {
size += getSizeInBytes(content[i]);
}
return size;
}
/**
* Returns a system dependent slashified path.
*
* @param path the path to shlashify
* @return a system dependent slashified path.
*/
public static String slashify(final String path) {
final char sep = File.separatorChar;
if ('/' == sep) {
return path.replace('\\', sep);
} else {
return path.replace('/', sep);
}
}
public static void copy(File source, File target) throws IOException {
final byte[] buffer = new byte[ONE_KB * ONE_KB];
int bytesRead;
FileImageInputStream sourceStream = null;
FileImageOutputStream targetStream = null;
try {
final File targetDir = target.getParentFile();
if (!targetDir.isDirectory()) {
if (!targetDir.mkdirs()) {
throw new IOException("failed to create target directory: " + targetDir.getAbsolutePath());
}
}
target.createNewFile();
sourceStream = new FileImageInputStream(source);
targetStream = new FileImageOutputStream(target);
while ((bytesRead = sourceStream.read(buffer)) >= 0) {
targetStream.write(buffer, 0, bytesRead);
}
} finally {
if (sourceStream != null) {
sourceStream.close();
}
if (targetStream != null) {
targetStream.flush();
targetStream.close();
}
}
}
public static void move(File source, File target) throws IOException {
copy(source, target);
if (source.length() == target.length()) {
source.delete();
}
}
synchronized public static File createFileInTempDir(String tempDirName, String fileName) throws IOException {
final File tempDir = new File(tempDirName);
if (!tempDir.isDirectory()) {
throw new IOException("The temp directory '" + tempDirName + "' does not exist");
}
final String name;
final String extension;
if (fileName.contains(".")) {
final int i = fileName.lastIndexOf(".");
name = fileName.substring(0, i);
extension = fileName.substring(i);
} else {
name = fileName;
extension = "";
}
File tempFile;
int number = 0;
do {
tempFile = new File(tempDirName, name + (number > 0 ? number : "") + extension);
number++;
} while (tempFile.exists());
tempFile.createNewFile();
return tempFile;
}
/**
* @param dereferenceSymbolicLinks means that when a link is found and points is a directory, we go in that directory
* and delete the contents there, even if the contents are not inside the treeRoot
*/
public static void deleteFileTree(File treeRoot, boolean dereferenceSymbolicLinks) throws IOException {
if( isSymbolicLink(treeRoot) ) {
treeRoot.delete();
return;//do not continue... if you do, the listFiles() method might return files outside the treeRoot
}
File[] files = treeRoot.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
File file = files[i];
boolean linkFound = isSymbolicLink(file);
if( !dereferenceSymbolicLinks && linkFound) {
// if it is a link, and we are not supposed to dereference symlinks
file.delete(); //delete the symbolic link
}else{
if( file.isDirectory() ) {
// if it is a directory, recursive call
if( linkFound ) {
File canonicalFile = file.getCanonicalFile();
if( !canonicalFile.exists() || directoryContainsFile(treeRoot, canonicalFile) ) {
//if canonicalFile doesn't exist, or is inside treeRoot, skip dereferencing and just delete the link
file.delete();//delete the symbolic link
}else{
//delete linked directory and its contents
deleteFileTree(canonicalFile, dereferenceSymbolicLinks);
file.delete();//delete the symbolic link
}
}else{
deleteFileTree(file, dereferenceSymbolicLinks);
}
}else{
// if it is a normal file, delete it
file.delete();
}
}
}
}
treeRoot.delete();
}
public static boolean directoryContainsFile(File treeRoot, File file) {
File fileToLookForInTreeRoot = file;
while (fileToLookForInTreeRoot != null) {
if (treeRoot.equals(fileToLookForInTreeRoot)) {
return true;
}
fileToLookForInTreeRoot = fileToLookForInTreeRoot.getParentFile();
}
return false;
}
public static boolean isSymbolicLink(File file) throws IOException {
if( file == null ) {
throw new IllegalArgumentException("File cannot be null");
}
//next line dereferences symbolic links along the path.
//eg. if path is /tmp/somedir/linktosomewhere/filename
// and /tmp/somedir/linktosomewhere is a link to /tmp/somedir2
// then this file is now /tmp/somedir2/filename
File parentFile = file.getParentFile();
if( parentFile != null ) {
file = new File(parentFile.getAbsoluteFile().getCanonicalFile(), file.getName());
}
File absoluteFile = file.getAbsoluteFile();
String canonicalPath = absoluteFile.getCanonicalPath();
String absolutePath = file.getAbsolutePath();
//if the absolute canonical path is the same as the absolute path, we don't believe it is a link
return !canonicalPath.equals(absolutePath);
//org.apache.commons.io.FileUtils is supposed to have a method for this, but the versions in maven (1.3, 1.3.1, 1.4) don't seem to.
//return org.apache.commons.io.FileUtils.isSymlink(file);
}
/**
* @param treeRoot The directory to delete along with its contents.
*/
/* @todo 1 pm/* Someone needs to review this to decide how it affects other software.
* This method is faulty. If the directory contains a symlink to a directory outside the tree to be deleted,
* the contents of the linked directory will be gone too. Replace this method with a call to deleteFileTree(treeRoot, false) when it is finished.
*
* http://www.onyxbits.de/content/blog/patrick/how-deal-filesystem-softlinkssymbolic-links-java
*/
public static void deleteFileTree(File treeRoot) {
File[] files = treeRoot.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isDirectory()) {
deleteFileTree(file);
}
file.delete();
}
}
treeRoot.delete();
}
////////////////////////////////////////////////////////////////////////////////
/////// END OF PUBLIC
////////////////////////////////////////////////////////////////////////////////
private static final int ONE_KB = 1024;
}