Java tutorial
/* * Copyright (C) 2010 The Android Open Source Project * * Licensed 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 com.android.tradefed.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; 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.util.Enumeration; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import com.android.ddmlib.Log; import com.android.tradefed.command.FatalHostError; import com.android.tradefed.log.LogUtil.CLog; /** * A helper class for file related operations */ public class FileUtil { private static final String LOG_TAG = "FileUtil"; /** * The minimum allowed disk space in megabytes. File creation methods will * throw {@link LowDiskSpaceException} if the usable disk space in desired * partition is less than this amount. */ private static final long MIN_DISK_SPACE_MB = 100; /** The min disk space in bytes */ private static final long MIN_DISK_SPACE = MIN_DISK_SPACE_MB * 1024 * 1024; private static final char[] SIZE_SPECIFIERS = { ' ', 'K', 'M', 'G', 'T' }; /** * Thrown if usable disk space is below minimum threshold. */ @SuppressWarnings("serial") public static class LowDiskSpaceException extends FatalHostError { LowDiskSpaceException(String msg, Throwable cause) { super(msg, cause); } LowDiskSpaceException(String msg) { super(msg); } } /** * Method to create a chain of directories, and set them all group * execute/read/writable as they are created, by calling * {@link #chmodGroupRWX(File)}. Essentially a version of * {@link File#mkdirs()} that also runs {@link #chmod(File, String)}. * * @param file * the name of the directory to create, possibly with containing * directories that don't yet exist. * @return {@code true} if {@code file} exists and is a directory, * {@code false} otherwise. */ public static boolean mkdirsRWX(File file) { File parent = file.getParentFile(); if (parent != null && !parent.isDirectory()) { // parent doesn't exist. recurse upward, which should both mkdir and // chmod if (!mkdirsRWX(parent)) { // Couldn't mkdir parent, fail Log.w(LOG_TAG, String.format("Failed to mkdir parent dir %s.", parent)); return false; } } // by this point the parent exists. Try to mkdir file if (file.isDirectory() || file.mkdir()) { // file should exist. Try chmod and complain if that fails, but keep // going boolean setPerms = chmodGroupRWX(file); if (!setPerms) { Log.w(LOG_TAG, String.format("Failed to set dir %s to be group accessible.", file)); } } return file.isDirectory(); } public static boolean chmodRWXRecursively(File file) { boolean success = true; if (!file.setExecutable(true, false)) { CLog.w("Failed to set %s executable.", file.getAbsolutePath()); success = false; } if (!file.setWritable(true, false)) { CLog.w("Failed to set %s writable.", file.getAbsolutePath()); success = false; } if (!file.setReadable(true, false)) { CLog.w("Failed to set %s readable", file.getAbsolutePath()); success = false; } if (file.isDirectory()) { File[] childs = file.listFiles(); for (File child : childs) { if (!chmodRWXRecursively(child)) { success = false; } } } return success; } public static boolean chmod(File file, String perms) { Log.d(LOG_TAG, String.format("Attempting to chmod %s to %s", file.getAbsolutePath(), perms)); CommandResult result = RunUtil.getDefault().runTimedCmd(10 * 1000, "chmod", perms, file.getAbsolutePath()); return result.getStatus().equals(CommandStatus.SUCCESS); } /** * Performs a best effort attempt to make given file group readable and * writable. * <p /> * Note that the execute permission is required to make directories * accessible. See {@link #chmodGroupRWX(File)}. * <p/ > * If 'chmod' system command is not supported by underlying OS, will set * file to writable by all. * * @param file * the {@link File} to make owner and group writable * @return <code>true</code> if file was successfully made group writable, * <code>false</code> otherwise */ public static boolean chmodGroupRW(File file) { if (chmod(file, "ug+rw")) { return true; } else { Log.d(LOG_TAG, String.format("Failed chmod; attempting to set %s globally RW", file.getAbsolutePath())); return file.setWritable(true, false /* false == writable for all */) && file.setReadable(true, false /* false == readable for all */); } } /** * Performs a best effort attempt to make given file group executable, * readable, and writable. * <p/ > * If 'chmod' system command is not supported by underlying OS, will attempt * to set permissions for all users. * * @param file * the {@link File} to make owner and group writable * @return <code>true</code> if permissions were set successfully, * <code>false</code> otherwise */ public static boolean chmodGroupRWX(File file) { if (chmod(file, "ug+rwx")) { return true; } else { Log.d(LOG_TAG, String.format("Failed chmod; attempting to set %s globally RWX", file.getAbsolutePath())); return file.setExecutable(true, false /* false == executable for all */) && file.setWritable(true, false /* false == writable for all */) && file.setReadable(true, false /* false == readable for all */); } } /** * Helper function to create a temp directory in the system default * temporary file directory. * * @param prefix * The prefix string to be used in generating the file's name; * must be at least three characters long * @return the created directory * @throws IOException * if file could not be created */ public static File createTempDir(String prefix) throws IOException { return createTempDir(prefix, null); } /** * Helper function to create a temp directory. * * @param prefix * The prefix string to be used in generating the file's name; * must be at least three characters long * @param parentDir * The parent directory in which the directory is to be created. * If <code>null</code> the system default temp directory will be * used. * @return the created directory * @throws IOException * if file could not be created */ public static File createTempDir(String prefix, File parentDir) throws IOException { // create a temp file with unique name, then make it a directory File tmpDir = File.createTempFile(prefix, "", parentDir); tmpDir.delete(); if (!tmpDir.mkdirs()) { throw new IOException("unable to create directory"); } return tmpDir; } /** * Helper wrapper function around * {@link File#createTempFile(String, String)} that audits for potential out * of disk space scenario. * * @see {@link File#createTempFile(String, String)} * @throws LowDiskSpaceException * if disk space on temporary partition is lower than minimum * allowed */ public static File createTempFile(String prefix, String suffix) throws IOException { File returnFile = File.createTempFile(prefix, suffix); verifyDiskSpace(returnFile); return returnFile; } /** * Helper wrapper function around {@link File#createTempFile(String, String, * File parentDir)} that audits for potential out of disk space scenario. * * @see {@link File#createTempFile(String, String, File)} * @throws LowDiskSpaceException * if disk space on partition is lower than minimum allowed */ public static File createTempFile(String prefix, String suffix, File parentDir) throws IOException { File returnFile = File.createTempFile(prefix, suffix, parentDir); verifyDiskSpace(returnFile); return returnFile; } /** * A helper method that hardlinks a file to another file * * @param origFile * the original file * @param destFile * the destination file * @throws IOException * if failed to hardlink file */ public static void hardlinkFile(File origFile, File destFile) throws IOException { if (!origFile.exists()) { throw new IOException( String.format("Cannot hardlink %s. File does not exist", origFile.getAbsolutePath())); } // `ln src dest` will create a hardlink (note: not `ln -s src dest`, // which creates symlink) // note that this will fail across filesystem boundaries // FIXME: should probably just fall back to normal copy if this fails CommandResult result = RunUtil.getDefault().runTimedCmd(10 * 1000, "ln", origFile.getAbsolutePath(), destFile.getAbsolutePath()); if (!result.getStatus().equals(CommandStatus.SUCCESS)) { throw new IOException(String.format("Failed to hardlink %s to %s. Across filesystem boundary?", origFile.getAbsolutePath(), destFile.getAbsolutePath())); } } /** * Recursively hardlink folder contents. * <p/> * Only supports copying of files and directories - symlinks are not copied. * * @param sourceDir * the folder that contains the files to copy * @param destDir * the destination folder * @throws IOException */ public static void recursiveHardlink(File sourceDir, File destDir) throws IOException { for (File childFile : sourceDir.listFiles()) { File destChild = new File(destDir, childFile.getName()); if (childFile.isDirectory()) { if (!destChild.mkdir()) { throw new IOException( String.format("Could not create directory %s", destChild.getAbsolutePath())); } recursiveHardlink(childFile, destChild); } else if (childFile.isFile()) { hardlinkFile(childFile, destChild); } } } /** * A helper method that copies a file's contents to a local file * * @param origFile * the original file to be copied * @param destFile * the destination file * @throws IOException * if failed to copy file */ public static void copyFile(File origFile, File destFile) throws IOException { writeToFile(new FileInputStream(origFile), destFile); } public static void copyFileToDir(File origFile, File destDir) throws IOException { FileUtil.copyFile(origFile, new File(destDir, origFile.getName())); } /** * Recursively copy folder contents. * <p/> * Only supports copying of files and directories - symlinks are not copied. * * @param sourceDir * the folder that contains the files to copy * @param destDir * the destination folder * @throws IOException */ public static void recursiveCopy(File sourceDir, File destDir) throws IOException { File[] childFiles = sourceDir.listFiles(); if (childFiles == null) { throw new IOException( String.format("Failed to recursively copy. Could not determine contents for directory '%s'", sourceDir.getAbsolutePath())); } for (File childFile : childFiles) { File destChild = new File(destDir, childFile.getName()); if (childFile.isDirectory()) { if (!destChild.mkdir()) { throw new IOException( String.format("Could not create directory %s", destChild.getAbsolutePath())); } recursiveCopy(childFile, destChild); } else if (childFile.isFile()) { copyFile(childFile, destChild); } } } /** * A helper method for reading string data from a file * * @param sourceFile * the file to read from * @throws IOException * @throws FileNotFoundException */ public static String readStringFromFile(File sourceFile, String charset) throws IOException { FileInputStream is = null; try { // no need to buffer since StreamUtil does is = new FileInputStream(sourceFile); return StreamUtil.getStringFromStream(is, charset); } finally { StreamUtil.close(is); } } public static String readStringFromFile(File sourceFile) throws IOException { FileInputStream is = null; try { // no need to buffer since StreamUtil does is = new FileInputStream(sourceFile); return StreamUtil.getStringFromStream(is); } finally { StreamUtil.close(is); } } /** * A helper method for writing string data to file * * @param inputString * the input {@link String} * @param destFile * the dest file to write to */ public static void writeToFile(String inputString, File destFile) throws IOException { writeToFile(new ByteArrayInputStream(inputString.getBytes()), destFile); } /** * A helper method for writing stream data to file * * @param input * the unbuffered input stream * @param destFile * the dest file to write to */ public static void writeToFile(InputStream input, File destFile) throws IOException { InputStream origStream = null; OutputStream destStream = null; try { origStream = new BufferedInputStream(input); destStream = new BufferedOutputStream(new FileOutputStream(destFile, true)); StreamUtil.copyStreams(origStream, destStream); } finally { StreamUtil.close(origStream); StreamUtil.close(destStream); } } private static void verifyDiskSpace(File file) { // Based on empirical testing File.getUsableSpace is a low cost // operation (~ 100 us for // local disk, ~ 100 ms for network disk). Therefore call it every time // tmp file is // created if (file.getUsableSpace() < MIN_DISK_SPACE) { throw new LowDiskSpaceException(String.format("Available space on %s is less than %s MB", file.getAbsolutePath(), MIN_DISK_SPACE_MB)); } } /** * Recursively delete given file and all its contents */ public static void recursiveDelete(File rootDir) { if (rootDir.isDirectory()) { File[] childFiles = rootDir.listFiles(); if (childFiles != null) { for (File child : childFiles) { recursiveDelete(child); } } } rootDir.delete(); } /** * Utility method to extract entire contents of zip file into given * directory * * @param zipFile * the {@link ZipFile} to extract * @param destDir * the local dir to extract file to * @throws IOException * if failed to extract file */ public static void extractZip(ZipFile zipFile, File destDir) throws IOException { Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); File childFile = new File(destDir, entry.getName()); childFile.getParentFile().mkdirs(); if (entry.isDirectory()) { continue; } else { FileUtil.writeToFile(zipFile.getInputStream(entry), childFile); } } } public static void extractTarGzip(File tarGzipFile, File destDir) throws FileNotFoundException, IOException, ArchiveException { GZIPInputStream gzipIn = null; ArchiveInputStream archivIn = null; BufferedInputStream buffIn = null; BufferedOutputStream buffOut = null; try { gzipIn = new GZIPInputStream(new BufferedInputStream(new FileInputStream(tarGzipFile))); archivIn = new ArchiveStreamFactory().createArchiveInputStream("tar", gzipIn); buffIn = new BufferedInputStream(archivIn); TarArchiveEntry entry = null; while ((entry = (TarArchiveEntry) archivIn.getNextEntry()) != null) { String entryName = entry.getName(); String[] engtryPart = entryName.split("/"); StringBuilder fullPath = new StringBuilder(); fullPath.append(destDir.getAbsolutePath()); for (String e : engtryPart) { fullPath.append(File.separator); fullPath.append(e); } File destFile = new File(fullPath.toString()); if (entryName.endsWith("/")) { if (!destFile.exists()) destFile.mkdirs(); } else { if (!destFile.exists()) destFile.createNewFile(); buffOut = new BufferedOutputStream(new FileOutputStream(destFile)); byte[] buf = new byte[8192]; int len = 0; while ((len = buffIn.read(buf)) != -1) { buffOut.write(buf, 0, len); } buffOut.flush(); } } } finally { if (buffOut != null) { buffOut.close(); } if (buffIn != null) { buffIn.close(); } if (archivIn != null) { archivIn.close(); } if (gzipIn != null) { gzipIn.close(); } } } public static void extractGzip(File tarGzipFile, File destDir) throws FileNotFoundException, IOException, ArchiveException { String srcGzipName = tarGzipFile.getName(); String srcUnzipName = srcGzipName.substring(0, srcGzipName.length() - 3); extractGzip(tarGzipFile, destDir, srcUnzipName); } public static void extractGzip(File tarGzipFile, File destDir, String destName) throws FileNotFoundException, IOException, ArchiveException { GZIPInputStream zipIn = null; BufferedOutputStream buffOut = null; try { File destUnzipFile = new File(destDir.getAbsolutePath(), destName); zipIn = new GZIPInputStream(new FileInputStream(tarGzipFile)); buffOut = new BufferedOutputStream(new FileOutputStream(destUnzipFile)); int b = 0; byte[] buf = new byte[8192]; while ((b = zipIn.read(buf)) != -1) { buffOut.write(buf, 0, b); } } finally { if (zipIn != null) zipIn.close(); if (buffOut != null) buffOut.close(); } } /** * Utility method to extract one specific file from zip file into a tmp file * * @param zipFile * the {@link ZipFile} to extract * @param filePath * the filePath of to extract * @throws IOException * if failed to extract file * @return the {@link File} or null if not found */ public static File extractFileFromZip(ZipFile zipFile, String filePath) throws IOException { ZipEntry entry = zipFile.getEntry(filePath); if (entry == null) { return null; } File createdFile = FileUtil.createTempFile("extracted", FileUtil.getExtension(filePath)); FileUtil.writeToFile(zipFile.getInputStream(entry), createdFile); return createdFile; } /** * Utility method to create a temporary zip file containing the given * directory and all its contents. * * @param dir * the directory to zip * @return a temporary zip {@link File} containing directory contents * @throws IOException * if failed to create zip file */ public static File createZip(File dir) throws IOException { File zipFile = FileUtil.createTempFile("dir", ".zip"); createZip(dir, zipFile); return zipFile; } /** * Utility method to create a zip file containing the given directory and * all its contents. * * @param dir * the directory to zip * @param zipFile * the zip file to create - it should not already exist * @throws IOException * if failed to create zip file */ public static void createZip(File dir, File zipFile) throws IOException { ZipOutputStream out = null; try { FileOutputStream fileStream = new FileOutputStream(zipFile); out = new ZipOutputStream(new BufferedOutputStream(fileStream)); addToZip(out, dir, new LinkedList<String>()); } catch (IOException e) { zipFile.delete(); throw e; } catch (RuntimeException e) { zipFile.delete(); throw e; } finally { StreamUtil.close(out); } } /** * Recursively adds given file and its contents to ZipOutputStream * * @param out * the {@link ZipOutputStream} * @param file * the {@link File} to add to the stream * @param relativePathSegs * the relative path of file, including separators * @throws IOException * if failed to add file to zip */ private static void addToZip(ZipOutputStream out, File file, List<String> relativePathSegs) throws IOException { relativePathSegs.add(file.getName()); if (file.isDirectory()) { // note: it appears even on windows, ZipEntry expects '/' as a path // separator relativePathSegs.add("/"); } ZipEntry zipEntry = new ZipEntry(buildPath(relativePathSegs)); out.putNextEntry(zipEntry); if (file.isFile()) { writeToStream(file, out); } out.closeEntry(); if (file.isDirectory()) { // recursively add contents File[] subFiles = file.listFiles(); if (subFiles == null) { throw new IOException(String.format("Could not read directory %s", file.getAbsolutePath())); } for (File subFile : subFiles) { addToZip(out, subFile, relativePathSegs); } // remove the path separator relativePathSegs.remove(relativePathSegs.size() - 1); } // remove the last segment, added at beginning of method relativePathSegs.remove(relativePathSegs.size() - 1); } /** * Close an open {@link ZipFile}, ignoring any exceptions. * * @param otaZip * the file to close */ public static void closeZip(ZipFile otaZip) { if (otaZip != null) { try { otaZip.close(); } catch (IOException e) { // ignore } } } /** * Helper method to create a gzipped version of a single file. * * @param file * the original file * @param gzipFile * the file to place compressed contents in * @throws IOException */ public static void gzipFile(File file, File gzipFile) throws IOException { GZIPOutputStream out = null; try { FileOutputStream fileStream = new FileOutputStream(gzipFile); out = new GZIPOutputStream(new BufferedOutputStream(fileStream, 64 * 1024)); writeToStream(file, out); } catch (IOException e) { gzipFile.delete(); throw e; } catch (RuntimeException e) { gzipFile.delete(); throw e; } finally { StreamUtil.close(out); } } /** * Helper method to write input file contents to output stream. * * @param file * the input {@link File} * @param out * the {@link OutputStream} * * @throws IOException */ private static void writeToStream(File file, OutputStream out) throws IOException { InputStream inputStream = null; try { inputStream = new BufferedInputStream(new FileInputStream(file)); StreamUtil.copyStreams(inputStream, out); } finally { StreamUtil.close(inputStream); } } /** * Builds a file system path from a stack of relative path segments * * @param relativePathSegs * the list of relative paths * @return a {@link String} containing all relativePathSegs */ private static String buildPath(List<String> relativePathSegs) { StringBuilder pathBuilder = new StringBuilder(); for (String segment : relativePathSegs) { pathBuilder.append(segment); } return pathBuilder.toString(); } /** * Gets the extension for given file name. * * @param fileName * @return the extension or empty String if file has no extension */ public static String getExtension(String fileName) { int index = fileName.lastIndexOf('.'); if (index == -1) { return ""; } else { return fileName.substring(index); } } /** * Gets the base name, without extension, of given file name. * <p/> * e.g. getBaseName("file.txt") will return "file" * * @param fileName * @return the base name */ public static String getBaseName(String fileName) { int index = fileName.lastIndexOf('.'); if (index == -1) { return fileName; } else { return fileName.substring(0, index); } } /** * Utility method to do byte-wise content comparison of two files. * * @return <code>true</code> if file contents are identical */ public static boolean compareFileContents(File file1, File file2) throws IOException { BufferedInputStream stream1 = null; BufferedInputStream stream2 = null; boolean result = true; try { stream1 = new BufferedInputStream(new FileInputStream(file1)); stream2 = new BufferedInputStream(new FileInputStream(file2)); boolean eof = false; while (!eof) { int byte1 = stream1.read(); int byte2 = stream2.read(); if (byte1 != byte2) { result = false; break; } eof = byte1 == -1; } } finally { StreamUtil.close(stream1); StreamUtil.close(stream2); } return result; } /** * Helper method which constructs a unique file on temporary disk, whose * name corresponds as closely as possible to the file name given by the * remote file path * * @param remoteFilePath * the '/' separated remote path to construct the name from * @param parentDir * the parent directory to create the file in. <code>null</code> * to use the default temporary directory */ public static File createTempFileForRemote(String remoteFilePath, File parentDir) throws IOException { String[] segments = remoteFilePath.split("/"); // take last segment as base name String remoteFileName = segments[segments.length - 1]; String prefix = getBaseName(remoteFileName); if (prefix.length() < 3) { // prefix must be at least 3 characters long prefix = prefix + "XXX"; } String fileExt = getExtension(remoteFileName); // create a unique file name. Add a underscore to prefix so file name is // more readable // e.g. myfile_57588758.img rather than myfile57588758.img File tmpFile = FileUtil.createTempFile(prefix + "_", fileExt, parentDir); return tmpFile; } /** * Try to delete a file and silently ignore IOExceptions. Intended for use * when cleaning up in {@code finally} stanzas. * * @param file * may be null. */ public static void deleteFile(File file) { if (file != null) { file.delete(); } } /** * Helper method to build a system-dependent File * * @param parentDir * the parent directory to use. * @param pathSegments * the relative path segments to use * @return the {@link File} representing given path, with each * <var>pathSegment</var> separated by {@link File#separatorChar} */ public static File getFileForPath(File parentDir, String... pathSegments) { return new File(parentDir, getPath(pathSegments)); } /** * Helper method to build a system-dependent relative path * * @param pathSegments * the relative path segments to use * @return the {@link String} representing given path, with each * <var>pathSegment</var> separated by {@link File#separatorChar} */ public static String getPath(String... pathSegments) { StringBuilder pathBuilder = new StringBuilder(); boolean isFirst = true; for (String path : pathSegments) { if (!isFirst) { pathBuilder.append(File.separatorChar); } else { isFirst = false; } pathBuilder.append(path); } return pathBuilder.toString(); } /** * Recursively search given directory for first file with given name * * @param dir * the directory to search * @param fileName * the name of the file to search for * @return the {@link File} or <code>null</code> if it could not be found */ public static File findFile(File dir, String fileName) { if (dir.listFiles() != null) { for (File file : dir.listFiles()) { if (file.getName().equals(fileName)) { return file; } else if (file.isDirectory()) { File result = findFile(file, fileName); if (result != null) { return result; } } } } return null; } /** * Recursively find all directories under the given {@code rootDir} * * @param rootDir * the root directory to search in * @param relativeParent * An optional parent for all {@link File}s returned. If not * specified, all {@link File}s will be relative to * {@code rootDir}. * @return An set of {@link File}s, representing all directories under * {@code rootDir}, including {@code rootDir} itself. If * {@code rootDir} is null, an empty set is returned. */ public static Set<File> findDirsUnder(File rootDir, File relativeParent) { Set<File> dirs = new HashSet<File>(); if (rootDir != null) { if (!rootDir.isDirectory()) { throw new IllegalArgumentException( "Can't find dirs under '" + rootDir + "'. It's not a directory."); } File thisDir = new File(relativeParent, rootDir.getName()); dirs.add(thisDir); for (File file : rootDir.listFiles()) { if (file.isDirectory()) { dirs.addAll(findDirsUnder(file, thisDir)); } } } return dirs; } /** * Convert the given file size in bytes to a more readable format in * X.Y[KMGT] format. * * @param sizeLong * file size in bytes * @return descriptive string of file size */ public static String convertToReadableSize(long sizeLong) { double size = sizeLong; for (int i = 0; i < SIZE_SPECIFIERS.length; i++) { if (size < 1024) { return String.format("%.1f%c", size, SIZE_SPECIFIERS[i]); } size /= 1024f; } throw new IllegalArgumentException( String.format("Passed a file size of %d, I cannot count that high", size)); } /** * The inverse of {@link #convertToReadableSize(long)}. Converts the * readable format described in {@link #convertToReadableSize(long)} to a * byte value. * * @param sizeString * the string description of the size. * @return the size in bytes * @throws IllegalArgumentException * if cannot recognize size */ public static long convertSizeToBytes(String sizeString) throws IllegalArgumentException { if (sizeString.isEmpty()) { throw new IllegalArgumentException("invalid empty string"); } char sizeSpecifier = sizeString.charAt(sizeString.length() - 1); long multiplier = findMultiplier(sizeSpecifier); try { String numberString = sizeString; if (multiplier != 1) { // strip off last char numberString = sizeString.substring(0, sizeString.length() - 1); } return multiplier * Long.parseLong(numberString); } catch (NumberFormatException e) { throw new IllegalArgumentException(String.format("Unrecognized size %s", sizeString)); } } private static long findMultiplier(char sizeSpecifier) { long multiplier = 1; for (int i = 1; i < SIZE_SPECIFIERS.length; i++) { multiplier *= 1024; if (sizeSpecifier == SIZE_SPECIFIERS[i]) { return multiplier; } } // not found return 1; } }