/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.pathvisio.core.util; import java.io.Closeable; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.List; /** * PathVisio note: this is an extract of the most useful functions from apache.commons.io.FileUtils * and apache.commons.io.IOUtils. This is a rather large library that we don't want to include as a whole * in the wikipathways applet. *
* General file manipulation utilities. *
* Facilities are provided in the following areas: *
* Origin of code: Excalibur, Alexandria, Commons-Utils * * @author Kevin A. Burton * @author Scott Sanders * @author Daniel Rall * @author Christoph.Reck * @author Peter Donald * @author Jeff Turner * @author Matthew Hawthorne * @author Jeremias Maerki * @author Stephen Colebourne * @author Ian Springer * @author Chris Eldredge * @author Jim Harrington * @author Sandy McArthur * @version $Id: FileUtils.java 1163295 2011-08-30 17:22:38Z ggregory $ */ public abstract class CommonsFileUtils { /** * Instances should NOT be constructed in standard programming. */ private CommonsFileUtils() { } /** * The number of bytes in a kilobyte. */ private static final long ONE_KB = 1024; /** * The number of bytes in a megabyte. */ private static final long ONE_MB = ONE_KB * ONE_KB; /** * The file copy buffer size (30 MB) */ private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30; //----------------------------------------------------------------------- /** * Copies a file to a directory preserving the file date. *
* This method copies the contents of the specified source file * to a file of the same name in the specified destination directory. * The destination directory is created if it does not exist. * If the destination file exists, then this method will overwrite it. *
* Note: This method tries to preserve the file's last
* modified date/times using {@link File#setLastModified(long)}, however
* it is not guaranteed that the operation will succeed.
* If the modification operation fails, no indication is provided.
*
* @param srcFile an existing file to copy, must not be null
* @param destDir the directory to place the copy in, must not be null
*
* @throws NullPointerException if source or destination is null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @see #copyFile(File, File, boolean)
*/
public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
copyFileToDirectory(srcFile, destDir, true);
}
/**
* Copies a file to a directory optionally preserving the file date.
*
* This method copies the contents of the specified source file * to a file of the same name in the specified destination directory. * The destination directory is created if it does not exist. * If the destination file exists, then this method will overwrite it. *
* Note: Setting preserveFileDate
to
* true
tries to preserve the file's last modified
* date/times using {@link File#setLastModified(long)}, however it is
* not guaranteed that the operation will succeed.
* If the modification operation fails, no indication is provided.
*
* @param srcFile an existing file to copy, must not be null
* @param destDir the directory to place the copy in, must not be null
* @param preserveFileDate true if the file date of the copy
* should be the same as the original
*
* @throws NullPointerException if source or destination is null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @see #copyFile(File, File, boolean)
* @since Commons IO 1.3
*/
private static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (destDir.exists() && destDir.isDirectory() == false) {
throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
}
File destFile = new File(destDir, srcFile.getName());
copyFile(srcFile, destFile, preserveFileDate);
}
/**
* Copies a file to a new location preserving the file date.
*
* This method copies the contents of the specified source file to the * specified destination file. The directory holding the destination file is * created if it does not exist. If the destination file exists, then this * method will overwrite it. *
* Note: This method tries to preserve the file's last
* modified date/times using {@link File#setLastModified(long)}, however
* it is not guaranteed that the operation will succeed.
* If the modification operation fails, no indication is provided.
*
* @param srcFile an existing file to copy, must not be null
* @param destFile the new file, must not be null
*
* @throws NullPointerException if source or destination is null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @see #copyFileToDirectory(File, File)
*/
public static void copyFile(File srcFile, File destFile) throws IOException {
copyFile(srcFile, destFile, true);
}
/**
* Copies a file to a new location.
*
* This method copies the contents of the specified source file * to the specified destination file. * The directory holding the destination file is created if it does not exist. * If the destination file exists, then this method will overwrite it. *
* Note: Setting preserveFileDate
to
* true
tries to preserve the file's last modified
* date/times using {@link File#setLastModified(long)}, however it is
* not guaranteed that the operation will succeed.
* If the modification operation fails, no indication is provided.
*
* @param srcFile an existing file to copy, must not be null
* @param destFile the new file, must not be null
* @param preserveFileDate true if the file date of the copy
* should be the same as the original
*
* @throws NullPointerException if source or destination is null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @see #copyFileToDirectory(File, File, boolean)
*/
private static void copyFile(File srcFile, File destFile,
boolean preserveFileDate) throws IOException {
if (srcFile == null) {
throw new NullPointerException("Source must not be null");
}
if (destFile == null) {
throw new NullPointerException("Destination must not be null");
}
if (srcFile.exists() == false) {
throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
}
if (srcFile.isDirectory()) {
throw new IOException("Source '" + srcFile + "' exists but is a directory");
}
if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
}
File parentFile = destFile.getParentFile();
if (parentFile != null) {
if (!parentFile.mkdirs() && !parentFile.isDirectory()) {
throw new IOException("Destination '" + parentFile + "' directory cannot be created");
}
}
if (destFile.exists() && destFile.canWrite() == false) {
throw new IOException("Destination '" + destFile + "' exists but is read-only");
}
doCopyFile(srcFile, destFile, preserveFileDate);
}
/**
* Internal copy file method.
*
* @param srcFile the validated source file, must not be null
* @param destFile the validated destination file, must not be null
* @param preserveFileDate whether to preserve the file date
* @throws IOException if an error occurs
*/
private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
if (destFile.exists() && destFile.isDirectory()) {
throw new IOException("Destination '" + destFile + "' exists but is a directory");
}
FileInputStream fis = null;
FileOutputStream fos = null;
FileChannel input = null;
FileChannel output = null;
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
input = fis.getChannel();
output = fos.getChannel();
long size = input.size();
long pos = 0;
long count = 0;
while (pos < size) {
count = (size - pos) > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : (size - pos);
pos += output.transferFrom(input, pos, count);
}
} finally {
closeQuietly(output);
closeQuietly(fos);
closeQuietly(input);
closeQuietly(fis);
}
if (srcFile.length() != destFile.length()) {
throw new IOException("Failed to copy full contents from '" +
srcFile + "' to '" + destFile + "'");
}
if (preserveFileDate) {
destFile.setLastModified(srcFile.lastModified());
}
}
//-----------------------------------------------------------------------
/**
* Copies a directory to within another directory preserving the file dates.
*
* This method copies the source directory and all its contents to a * directory of the same name in the specified destination directory. *
* The destination directory is created if it does not exist. * If the destination directory did exist, then this method merges * the source with the destination, with the source taking precedence. *
* Note: This method tries to preserve the files' last
* modified date/times using {@link File#setLastModified(long)}, however
* it is not guaranteed that those operations will succeed.
* If the modification operation fails, no indication is provided.
*
* @param srcDir an existing directory to copy, must not be null
* @param destDir the directory to place the copy in, must not be null
*
* @throws NullPointerException if source or destination is null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @since Commons IO 1.2
*/
public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
if (srcDir == null) {
throw new NullPointerException("Source must not be null");
}
if (srcDir.exists() && srcDir.isDirectory() == false) {
throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
}
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (destDir.exists() && destDir.isDirectory() == false) {
throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
}
copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
}
/**
* Copies a whole directory to a new location preserving the file dates.
*
* This method copies the specified directory and all its child * directories and files to the specified destination. * The destination is the new location and name of the directory. *
* The destination directory is created if it does not exist. * If the destination directory did exist, then this method merges * the source with the destination, with the source taking precedence. *
* Note: This method tries to preserve the files' last
* modified date/times using {@link File#setLastModified(long)}, however
* it is not guaranteed that those operations will succeed.
* If the modification operation fails, no indication is provided.
*
* @param srcDir an existing directory to copy, must not be null
* @param destDir the new directory, must not be null
*
* @throws NullPointerException if source or destination is null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @since Commons IO 1.1
*/
public static void copyDirectory(File srcDir, File destDir) throws IOException {
copyDirectory(srcDir, destDir, true);
}
/**
* Copies a whole directory to a new location.
*
* This method copies the contents of the specified source directory * to within the specified destination directory. *
* The destination directory is created if it does not exist. * If the destination directory did exist, then this method merges * the source with the destination, with the source taking precedence. *
* Note: Setting preserveFileDate
to
* true
tries to preserve the files' last modified
* date/times using {@link File#setLastModified(long)}, however it is
* not guaranteed that those operations will succeed.
* If the modification operation fails, no indication is provided.
*
* @param srcDir an existing directory to copy, must not be null
* @param destDir the new directory, must not be null
* @param preserveFileDate true if the file date of the copy
* should be the same as the original
*
* @throws NullPointerException if source or destination is null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @since Commons IO 1.1
*/
private static void copyDirectory(File srcDir, File destDir,
boolean preserveFileDate) throws IOException {
copyDirectory(srcDir, destDir, null, preserveFileDate);
}
/**
* Copies a filtered directory to a new location.
*
* This method copies the contents of the specified source directory * to within the specified destination directory. *
* The destination directory is created if it does not exist. * If the destination directory did exist, then this method merges * the source with the destination, with the source taking precedence. *
* Note: Setting preserveFileDate
to
* true
tries to preserve the files' last modified
* date/times using {@link File#setLastModified(long)}, however it is
* not guaranteed that those operations will succeed.
* If the modification operation fails, no indication is provided.
*
*
* // only copy the directory structure * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false); ** *
* // Create a filter for ".txt" files * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); * * // Create a filter for either directories or ".txt" files * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); * * // Copy using the filter * FileUtils.copyDirectory(srcDir, destDir, filter, false); ** * @param srcDir an existing directory to copy, must not be
null
* @param destDir the new directory, must not be null
* @param filter the filter to apply, null means copy all directories and files
* @param preserveFileDate true if the file date of the copy
* should be the same as the original
*
* @throws NullPointerException if source or destination is null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @since Commons IO 1.4
*/
private static void copyDirectory(File srcDir, File destDir,
FileFilter filter, boolean preserveFileDate) throws IOException {
if (srcDir == null) {
throw new NullPointerException("Source must not be null");
}
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (srcDir.exists() == false) {
throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
}
if (srcDir.isDirectory() == false) {
throw new IOException("Source '" + srcDir + "' exists but is not a directory");
}
if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
}
// Cater for destination being directory within the source directory (see IO-141)
Listnull
* @param destDir the validated destination directory, must not be null
* @param filter the filter to apply, null means copy all directories and files
* @param preserveFileDate whether to preserve the file date
* @param exclusionList List of files and directories to exclude from the copy, may be null
* @throws IOException if an error occurs
* @since Commons IO 1.1
*/
private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,
boolean preserveFileDate, List* The difference between File.delete() and this method are: *
null
* @return true
if the file or directory was deleted, otherwise
* false
*
* @since Commons IO 1.4
*/
public static boolean deleteQuietly(File file) {
if (file == null) {
return false;
}
try {
if (file.isDirectory()) {
cleanDirectory(file);
}
} catch (Exception ignored) {
}
try {
return file.delete();
} catch (Exception ignored) {
return false;
}
}
/**
* Cleans a directory without deleting it.
*
* @param directory directory to clean
* @throws IOException in case cleaning is unsuccessful
*/
private static void cleanDirectory(File directory) throws IOException {
if (!directory.exists()) {
String message = directory + " does not exist";
throw new IllegalArgumentException(message);
}
if (!directory.isDirectory()) {
String message = directory + " is not a directory";
throw new IllegalArgumentException(message);
}
File[] files = directory.listFiles();
if (files == null) { // null if security restricted
throw new IOException("Failed to list contents of " + directory);
}
IOException exception = null;
for (File file : files) {
try {
forceDelete(file);
} catch (IOException ioe) {
exception = ioe;
}
}
if (null != exception) {
throw exception;
}
}
//-----------------------------------------------------------------------
/**
* Deletes a file. If file is a directory, delete it and all sub-directories.
* * The difference between File.delete() and this method are: *
null
* @throws NullPointerException if the directory is null
* @throws FileNotFoundException if the file was not found
* @throws IOException in case deletion is unsuccessful
*/
public static void forceDelete(File file) throws IOException {
if (file.isDirectory()) {
deleteDirectory(file);
} else {
boolean filePresent = file.exists();
if (!file.delete()) {
if (!filePresent){
throw new FileNotFoundException("File does not exist: " + file);
}
String message =
"Unable to delete file: " + file;
throw new IOException(message);
}
}
}
/**
* Schedules a file to be deleted when JVM exits.
* If file is directory delete it and all sub-directories.
*
* @param file file or directory to delete, must not be null
* @throws NullPointerException if the file is null
* @throws IOException in case deletion is unsuccessful
*/
public static void forceDeleteOnExit(File file) throws IOException {
if (file.isDirectory()) {
deleteDirectoryOnExit(file);
} else {
file.deleteOnExit();
}
}
/**
* Schedules a directory recursively for deletion on JVM exit.
*
* @param directory directory to delete, must not be null
* @throws NullPointerException if the directory is null
* @throws IOException in case deletion is unsuccessful
*/
private static void deleteDirectoryOnExit(File directory) throws IOException {
if (!directory.exists()) {
return;
}
directory.deleteOnExit();
}
/**
* Makes a directory, including any necessary but nonexistent parent
* directories. If a file already exists with specified name but it is
* not a directory then an IOException is thrown.
* If the directory cannot be created (or does not already exist)
* then an IOException is thrown.
*
* @param directory directory to create, must not be null
* @throws NullPointerException if the directory is null
* @throws IOException if the directory cannot be created or the file already exists but is not a directory
*/
public static void forceMkdir(File directory) throws IOException {
if (directory.exists()) {
if (!directory.isDirectory()) {
String message =
"File "
+ directory
+ " exists and is "
+ "not a directory. Unable to create directory.";
throw new IOException(message);
}
} else {
if (!directory.mkdirs()) {
// Double-check that some other thread or process hasn't made
// the directory in the background
if (!directory.isDirectory())
{
String message =
"Unable to create directory " + directory;
throw new IOException(message);
}
}
}
}
/**
* Moves a directory.
*
* When the destination directory is on another file system, do a "copy and delete".
*
* @param srcDir the directory to be moved
* @param destDir the destination directory
* @throws NullPointerException if source or destination is null
* @throws IOException if the destination directory exists
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs moving the file
* @since Commons IO 1.4
*/
public static void moveDirectory(File srcDir, File destDir) throws IOException {
if (srcDir == null) {
throw new NullPointerException("Source must not be null");
}
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (!srcDir.exists()) {
throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
}
if (!srcDir.isDirectory()) {
throw new IOException("Source '" + srcDir + "' is not a directory");
}
if (destDir.exists()) {
throw new IOException("Destination '" + destDir + "' already exists");
}
boolean rename = srcDir.renameTo(destDir);
if (!rename) {
copyDirectory( srcDir, destDir );
deleteDirectory( srcDir );
if (srcDir.exists()) {
throw new IOException("Failed to delete original directory '" + srcDir +
"' after copy to '" + destDir + "'");
}
}
}
/**
* Moves a directory to another directory.
*
* @param src the file to be moved
* @param destDir the destination file
* @param createDestDir If true
create the destination directory,
* otherwise if false
throw an IOException
* @throws NullPointerException if source or destination is null
* @throws FileExistsException if the directory exists in the destination directory
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs moving the file
* @since Commons IO 1.4
*/
private static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
if (src == null) {
throw new NullPointerException("Source must not be null");
}
if (destDir == null) {
throw new NullPointerException("Destination directory must not be null");
}
if (!destDir.exists() && createDestDir) {
destDir.mkdirs();
}
if (!destDir.exists()) {
throw new FileNotFoundException("Destination directory '" + destDir +
"' does not exist [createDestDir=" + createDestDir +"]");
}
if (!destDir.isDirectory()) {
throw new IOException("Destination '" + destDir + "' is not a directory");
}
moveDirectory(src, new File(destDir, src.getName()));
}
/**
* Moves a file.
*
* When the destination file is on another file system, do a "copy and delete".
*
* @param srcFile the file to be moved
* @param destFile the destination file
* @throws NullPointerException if source or destination is null
* @throws FileExistsException if the destination file exists
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs moving the file
* @since Commons IO 1.4
*/
public static void moveFile(File srcFile, File destFile) throws IOException {
if (srcFile == null) {
throw new NullPointerException("Source must not be null");
}
if (destFile == null) {
throw new NullPointerException("Destination must not be null");
}
if (!srcFile.exists()) {
throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
}
if (srcFile.isDirectory()) {
throw new IOException("Source '" + srcFile + "' is a directory");
}
if (destFile.exists()) {
throw new IOException("Destination '" + destFile + "' already exists");
}
if (destFile.isDirectory()) {
throw new IOException("Destination '" + destFile + "' is a directory");
}
boolean rename = srcFile.renameTo(destFile);
if (!rename) {
copyFile( srcFile, destFile );
if (!srcFile.delete()) {
CommonsFileUtils.deleteQuietly(destFile);
throw new IOException("Failed to delete original file '" + srcFile +
"' after copy to '" + destFile + "'");
}
}
}
/**
* Moves a file to a directory.
*
* @param srcFile the file to be moved
* @param destDir the destination file
* @param createDestDir If true
create the destination directory,
* otherwise if false
throw an IOException
* @throws NullPointerException if source or destination is null
* @throws FileExistsException if the destination file exists
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs moving the file
* @since Commons IO 1.4
*/
private static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException {
if (srcFile == null) {
throw new NullPointerException("Source must not be null");
}
if (destDir == null) {
throw new NullPointerException("Destination directory must not be null");
}
if (!destDir.exists() && createDestDir) {
destDir.mkdirs();
}
if (!destDir.exists()) {
throw new FileNotFoundException("Destination directory '" + destDir +
"' does not exist [createDestDir=" + createDestDir +"]");
}
if (!destDir.isDirectory()) {
throw new IOException("Destination '" + destDir + "' is not a directory");
}
moveFile(srcFile, new File(destDir, srcFile.getName()));
}
/**
* Moves a file or directory to the destination directory.
*
* When the destination is on another file system, do a "copy and delete".
*
* @param src the file or directory to be moved
* @param destDir the destination directory
* @param createDestDir If true
create the destination directory,
* otherwise if false
throw an IOException
* @throws NullPointerException if source or destination is null
* @throws FileExistsException if the directory or file exists in the destination directory
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs moving the file
* @since Commons IO 1.4
*/
public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
if (src == null) {
throw new NullPointerException("Source must not be null");
}
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (!src.exists()) {
throw new FileNotFoundException("Source '" + src + "' does not exist");
}
if (src.isDirectory()) {
moveDirectoryToDirectory(src, destDir, createDestDir);
} else {
moveFileToDirectory(src, destDir, createDestDir);
}
}
/**
* Unconditionally close an InputStream
.
*
* Equivalent to {@link InputStream#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. *
* Example code: *
* byte[] data = new byte[1024]; * InputStream in = null; * try { * in = new FileInputStream("foo.txt"); * in.read(data); * in.close(); //close errors are handled * } catch (Exception e) { * // error handling * } finally { * IOUtils.closeQuietly(in); * } ** * @param input the InputStream to close, may be null or already closed */ public static void closeQuietly(InputStream input) { closeQuietly((Closeable)input); } /** * Unconditionally close an
OutputStream
.
* * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. *
* Example code: *
* byte[] data = "Hello, World".getBytes(); * * OutputStream out = null; * try { * out = new FileOutputStream("foo.txt"); * out.write(data); * out.close(); //close errors are handled * } catch (IOException e) { * // error handling * } finally { * IOUtils.closeQuietly(out); * } ** * @param output the OutputStream to close, may be null or already closed */ public static void closeQuietly(OutputStream output) { closeQuietly((Closeable)output); } /** * Unconditionally close a
Closeable
.
* * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. *
* Example code: *
* Closeable closeable = null; * try { * closeable = new FileReader("foo.txt"); * // process closeable * closeable.close(); * } catch (Exception e) { * // error handling * } finally { * IOUtils.closeQuietly(closeable); * } ** * @param closeable the object to close, may be null or already closed * @since Commons IO 2.0 */ private static void closeQuietly(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException ioe) { // ignore } } }