Merge change 3407 into donut

* changes:
  SDK updater: refactor install into Archive.
This commit is contained in:
Android (Google) Code Review
2009-06-08 13:24:37 -07:00
2 changed files with 458 additions and 439 deletions

View File

@@ -16,8 +16,16 @@
package com.android.sdklib.internal.repository;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
@@ -32,6 +40,8 @@ import java.security.NoSuchAlgorithmException;
*/
public class Archive implements IDescription {
public static final int NUM_MONITOR_INC = 100;
/** The checksum type. */
public enum ChecksumType {
/** A SHA1 checksum, represented as a 40-hex string. */
@@ -269,5 +279,450 @@ public class Archive implements IDescription {
return true;
}
/**
* Install this {@link Archive}s.
* The archive will be skipped if it is incompatible.
*
* @return True if the archive was installed, false otherwise.
*/
public boolean install(String osSdkRoot, ITaskMonitor monitor) {
File archiveFile = null;
try {
String name = getParentPackage().getShortDescription();
// TODO: we should not see this test fail if we had the filter UI above.
if (!isCompatible()) {
monitor.setResult("Skipping incompatible archive: %1$s", name);
return false;
}
archiveFile = downloadFile(monitor);
if (archiveFile != null) {
if (unarchive(osSdkRoot, archiveFile, monitor)) {
monitor.setResult("Installed: %1$s", name);
return true;
}
}
} finally {
// Delete the temp archive if it exists
deleteFileOrFolder(archiveFile);
}
return false;
}
/**
* Downloads an archive and returns the temp file with it.
* Caller is responsible with deleting the temp file when done.
*/
private File downloadFile(ITaskMonitor monitor) {
File tmpFileToDelete = null;
try {
File tmpFile = File.createTempFile("sdkupload", ".bin"); //$NON-NLS-1$ //$NON-NLS-2$
tmpFileToDelete = tmpFile;
String name = getParentPackage().getShortDescription();
String desc = String.format("Downloading %1$s", name);
monitor.setDescription(desc);
String link = getUrl();
if (!link.startsWith("http://") //$NON-NLS-1$
&& !link.startsWith("https://") //$NON-NLS-1$
&& !link.startsWith("ftp://")) { //$NON-NLS-1$
// Make the URL absolute by prepending the source
Package pkg = getParentPackage();
RepoSource src = pkg.getParentSource();
if (src == null) {
monitor.setResult("Internal error: no source for archive %1$s", name);
return null;
}
// take the URL to the repository.xml and remove the last component
// to get the base
String repoXml = src.getUrl();
int pos = repoXml.lastIndexOf('/');
String base = repoXml.substring(0, pos + 1);
link = base + link;
}
if (fetchUrl(tmpFile, link, desc, monitor)) {
// Fetching was successful, don't delete the temp file here!
tmpFileToDelete = null;
return tmpFile;
}
} catch (IOException e) {
monitor.setResult(e.getMessage());
} finally {
deleteFileOrFolder(tmpFileToDelete);
}
return null;
}
/**
* Actually performs the download.
* Also computes the SHA1 of the file on the fly.
* <p/>
* Success is defined as downloading as many bytes as was expected and having the same
* SHA1 as expected. Returns true on success or false if any of those checks fail.
* <p/>
* Increments the monitor by {@link #NUM_MONITOR_INC}.
*/
private boolean fetchUrl(File tmpFile,
String urlString,
String description,
ITaskMonitor monitor) {
URL url;
description += " (%1$d%%, %2$.0f KiB/s, %3$d %4$s left)";
FileOutputStream os = null;
InputStream is = null;
try {
url = new URL(urlString);
is = url.openStream();
os = new FileOutputStream(tmpFile);
MessageDigest digester = getChecksumType().getMessageDigest();
byte[] buf = new byte[65536];
int n;
long total = 0;
long size = getSize();
long inc = size / NUM_MONITOR_INC;
long next_inc = inc;
long startMs = System.currentTimeMillis();
long nextMs = startMs + 2000; // start update after 2 seconds
while ((n = is.read(buf)) >= 0) {
if (n > 0) {
os.write(buf, 0, n);
digester.update(buf, 0, n);
}
long timeMs = System.currentTimeMillis();
total += n;
if (total >= next_inc) {
monitor.incProgress(1);
next_inc += inc;
}
if (timeMs > nextMs) {
long delta = timeMs - startMs;
if (total > 0 && delta > 0) {
// percent left to download
int percent = (int) (100 * total / size);
// speed in KiB/s
float speed = (float)total / (float)delta * (1000.f / 1024.f);
// time left to download the rest at the current KiB/s rate
int timeLeft = (speed > 1e-3) ?
(int)(((size - total) / 1024.0f) / speed) :
0;
String timeUnit = "seconds";
if (timeLeft > 120) {
timeUnit = "minutes";
timeLeft /= 60;
}
monitor.setDescription(description, percent, speed, timeLeft, timeUnit);
}
nextMs = timeMs + 1000; // update every second
}
if (monitor.isCancelRequested()) {
monitor.setResult("Download aborted by user at %1$d bytes.", total);
return false;
}
}
if (total != size) {
monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
size, total);
return false;
}
// Create an hex string from the digest
byte[] digest = digester.digest();
n = digest.length;
String hex = "0123456789abcdef"; //$NON-NLS-1$
char[] hexDigest = new char[n * 2];
for (int i = 0; i < n; i++) {
int b = digest[i] & 0x0FF;
hexDigest[i*2 + 0] = hex.charAt(b >>> 4);
hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);
}
String expected = getChecksum();
String actual = new String(hexDigest);
if (!actual.equalsIgnoreCase(expected)) {
monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
expected, actual);
return false;
}
return true;
} catch (Exception e) {
monitor.setResult(e.getMessage());
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// pass
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
// pass
}
}
}
return false;
}
/**
* Install the given archive in the given folder.
*/
private boolean unarchive(String osSdkRoot, File archiveFile, ITaskMonitor monitor) {
String name = getParentPackage().getShortDescription();
String desc = String.format("Installing %1$s", name);
monitor.setDescription(desc);
File destFolder = getParentPackage().getInstallFolder(osSdkRoot);
File unzipDestFolder = destFolder;
File renamedDestFolder = null;
try {
// If this folder already exists, unzip in a temporary folder and then move/unlink.
if (destFolder.exists()) {
// Find a new temp folder that doesn't exist yet
unzipDestFolder = findTempFolder(destFolder, "new"); //$NON-NLS-1$
if (unzipDestFolder == null) {
// this should not seriously happen.
monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",
destFolder.getPath());
return false;
}
if (!unzipDestFolder.mkdirs()) {
monitor.setResult("Failed to create temp directory %1$s",
unzipDestFolder.getPath());
return false;
}
}
if (!unzipFolder(archiveFile, getSize(), unzipDestFolder, desc, monitor)) {
return false;
}
if (unzipDestFolder != destFolder) {
// Swap the old folder by the new one.
// Both original folders will be deleted in the finally clause below.
renamedDestFolder = findTempFolder(destFolder, "old"); //$NON-NLS-1$
if (renamedDestFolder == null) {
// this should not seriously happen.
monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",
destFolder.getPath());
return false;
}
if (!destFolder.renameTo(renamedDestFolder)) {
monitor.setResult("Failed to rename directory %1$s to %2$s",
destFolder.getPath(), renamedDestFolder.getPath());
return false;
}
if (!unzipDestFolder.renameTo(destFolder)) {
monitor.setResult("Failed to rename directory %1$s to %2$s",
unzipDestFolder.getPath(), destFolder.getPath());
return false;
}
}
return true;
} finally {
// Cleanup if the unzip folder is still set.
deleteFileOrFolder(renamedDestFolder);
if (unzipDestFolder != destFolder) {
deleteFileOrFolder(unzipDestFolder);
}
}
}
private boolean unzipFolder(File archiveFile,
long compressedSize,
File unzipDestFolder,
String description,
ITaskMonitor monitor) {
description += " (%1$d%%)";
FileInputStream fis = null;
ZipInputStream zis = null;
try {
fis = new FileInputStream(archiveFile);
zis = new ZipInputStream(fis);
// To advance the percent and the progress bar, we don't know the number of
// items left to unzip. However we know the size of the archive and the size of
// each uncompressed item. The zip file format overhead is negligible so that's
// a good approximation.
long incStep = compressedSize / NUM_MONITOR_INC;
long incTotal = 0;
long incCurr = 0;
int lastPercent = 0;
byte[] buf = new byte[65536];
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String name = entry.getName();
// ZipFile entries should have forward slashes, but not all Zip
// implementations can be expected to do that.
name = name.replace('\\', '/');
File destFile = new File(unzipDestFolder, name);
if (name.endsWith("/")) { //$NON-NLS-1$
// Create directory if it doesn't exist yet. This allows us to create
// empty directories.
if (!destFile.isDirectory() && !destFile.mkdirs()) {
monitor.setResult("Failed to create temp directory %1$s",
destFile.getPath());
return false;
}
continue;
} else if (name.indexOf('/') != -1) {
// Otherwise it's a file in a sub-directory.
// Make sure the parent directory has been created.
File parentDir = destFile.getParentFile();
if (!parentDir.isDirectory()) {
if (!parentDir.mkdirs()) {
monitor.setResult("Failed to create temp directory %1$s",
parentDir.getPath());
return false;
}
}
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(destFile);
int n;
while ((n = zis.read(buf)) != -1) {
if (n > 0) {
fos.write(buf, 0, n);
}
}
} finally {
if (fos != null) {
fos.close();
}
}
// Increment progress bar to match. We update only between files.
for(incTotal += entry.getCompressedSize(); incCurr < incTotal; incCurr += incStep) {
monitor.incProgress(1);
}
int percent = (int) (100 * incTotal / compressedSize);
if (percent != lastPercent) {
monitor.setDescription(description, percent);
lastPercent = percent;
}
if (monitor.isCancelRequested()) {
return false;
}
}
return true;
} catch (IOException e) {
monitor.setResult("Unzip failed: %1$s", e.getMessage());
} finally {
if (zis != null) {
try {
zis.close();
} catch (IOException e) {
// pass
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
// pass
}
}
}
return false;
}
/**
* Finds a temp folder which name is similar to the one of the ideal folder
* and with a ".tmpN" appended.
* <p/>
* This operation is not atomic so there's no guarantee the folder can't get
* created in between. This is however unlikely and the caller can assume the
* returned folder does not exist yet.
* <p/>
* Returns null if no such folder can be found (e.g. if all candidates exist),
* which is rather unlikely.
*/
private File findTempFolder(File idealFolder, String suffix) {
String basePath = idealFolder.getPath();
for (int i = 1; i < 100; i++) {
File folder = new File(String.format("%1$s.%2$s%3$02d", basePath, suffix, i)); //$NON-NLS-1$
if (!folder.exists()) {
return folder;
}
}
return null;
}
/**
* Deletes a file or a directory.
* Directories are deleted recursively.
* The argument can be null.
*/
private void deleteFileOrFolder(File fileOrFolder) {
if (fileOrFolder != null) {
if (fileOrFolder.isDirectory()) {
// Must delete content recursively first
for (File item : fileOrFolder.listFiles()) {
deleteFileOrFolder(item);
}
}
if (!fileOrFolder.delete()) {
fileOrFolder.deleteOnExit();
}
}
}
}

View File

@@ -21,7 +21,6 @@ import com.android.sdklib.ISdkLog;
import com.android.sdklib.internal.repository.Archive;
import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor;
import com.android.sdklib.internal.repository.Package;
import com.android.sdklib.internal.repository.RepoSource;
import com.android.sdklib.repository.SdkRepository;
@@ -42,26 +41,16 @@ import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* This is the private implementation of the UpdateWindow.
*/
public class UpdaterWindowImpl {
private static final int NUM_MONITOR_INC = 100;
/** Internal data shared between the window and its pages. */
private final UpdaterData mUpdaterData = new UpdaterData();
/** The array of pages instances. Only one is visible at a time. */
@@ -351,7 +340,7 @@ public class UpdaterWindowImpl {
mTaskFactory.start("Installing Archives", new ITask() {
public void run(ITaskMonitor monitor) {
final int progressPerArchive = 2 * NUM_MONITOR_INC + 10;
final int progressPerArchive = 2 * Archive.NUM_MONITOR_INC;
monitor.setProgressMax(archives.size() * progressPerArchive);
monitor.setDescription("Preparing to install archives");
@@ -359,34 +348,20 @@ public class UpdaterWindowImpl {
for (Archive archive : archives) {
int nextProgress = monitor.getProgress() + progressPerArchive;
File archiveFile = null;
try {
if (monitor.isCancelRequested()) {
break;
}
String name = archive.getParentPackage().getShortDescription();
// TODO: we should not see this test fail if we had the filter UI above.
if (!archive.isCompatible()) {
monitor.setResult("Skipping incompatible archive: %1$s", name);
continue;
if (archive.install(mUpdaterData.getOsSdkRoot(), monitor)) {
numInstalled++;
}
archiveFile = downloadArchive(archive, monitor);
if (archiveFile != null) {
if (installArchive(archive, archiveFile, monitor)) {
monitor.setResult("Installed: %1$s", name);
numInstalled++;
}
}
} catch (Throwable t) {
// Display anything unexpected in the monitor.
monitor.setResult("Unexpected Error: %1$s", t.getMessage());
} finally {
// Delete the temp archive if it exists
deleteFileOrFolder(archiveFile);
// Always move the progress bar to the desired position.
// This allows internal methods to not have to care in case
@@ -406,417 +381,6 @@ public class UpdaterWindowImpl {
});
}
/**
* Downloads an archive and returns the temp file with it.
* Caller is responsible with deleting the temp file when done.
*/
private File downloadArchive(Archive archive, ITaskMonitor monitor) {
File tmpFileToDelete = null;
try {
File tmpFile = File.createTempFile("sdkupload", ".bin"); //$NON-NLS-1$ //$NON-NLS-2$
tmpFileToDelete = tmpFile;
String name = archive.getParentPackage().getShortDescription();
String desc = String.format("Downloading %1$s", name);
monitor.setDescription(desc);
String link = archive.getUrl();
if (!link.startsWith("http://") //$NON-NLS-1$
&& !link.startsWith("https://") //$NON-NLS-1$
&& !link.startsWith("ftp://")) { //$NON-NLS-1$
// Make the URL absolute by prepending the source
Package pkg = archive.getParentPackage();
RepoSource src = pkg.getParentSource();
if (src == null) {
monitor.setResult("Internal error: no source for archive %1$s", name);
return null;
}
// take the URL to the repository.xml and remove the last component
// to get the base
String repoXml = src.getUrl();
int pos = repoXml.lastIndexOf('/');
String base = repoXml.substring(0, pos + 1);
link = base + link;
}
if (fetchUrl(tmpFile, archive, link, desc, monitor)) {
// Fetching was successful, don't delete the temp file here!
tmpFileToDelete = null;
return tmpFile;
}
} catch (IOException e) {
monitor.setResult(e.getMessage());
} finally {
deleteFileOrFolder(tmpFileToDelete);
}
return null;
}
/**
* Actually performs the download.
* Also computes the SHA1 of the file on the fly.
* <p/>
* Success is defined as downloading as many bytes as was expected and having the same
* SHA1 as expected. Returns true on success or false if any of those checks fail.
* <p/>
* Increments the monitor by {@link #NUM_MONITOR_INC}.
*/
private boolean fetchUrl(File tmpFile,
Archive archive,
String urlString,
String description,
ITaskMonitor monitor) {
URL url;
description += " (%1$d%%, %2$.0f KiB/s, %3$d %4$s left)";
FileOutputStream os = null;
InputStream is = null;
try {
url = new URL(urlString);
is = url.openStream();
os = new FileOutputStream(tmpFile);
MessageDigest digester = archive.getChecksumType().getMessageDigest();
byte[] buf = new byte[65536];
int n;
long total = 0;
long size = archive.getSize();
long inc = size / NUM_MONITOR_INC;
long next_inc = inc;
long startMs = System.currentTimeMillis();
long nextMs = startMs + 2000; // start update after 2 seconds
while ((n = is.read(buf)) >= 0) {
if (n > 0) {
os.write(buf, 0, n);
digester.update(buf, 0, n);
}
long timeMs = System.currentTimeMillis();
total += n;
if (total >= next_inc) {
monitor.incProgress(1);
next_inc += inc;
}
if (timeMs > nextMs) {
long delta = timeMs - startMs;
if (total > 0 && delta > 0) {
// percent left to download
int percent = (int) (100 * total / size);
// speed in KiB/s
float speed = (float)total / (float)delta * (1000.f / 1024.f);
// time left to download the rest at the current KiB/s rate
int timeLeft = (speed > 1e-3) ?
(int)(((size - total) / 1024.0f) / speed) :
0;
String timeUnit = "seconds";
if (timeLeft > 120) {
timeUnit = "minutes";
timeLeft /= 60;
}
monitor.setDescription(description, percent, speed, timeLeft, timeUnit);
}
nextMs = timeMs + 1000; // update every second
}
if (monitor.isCancelRequested()) {
monitor.setResult("Download aborted by user at %1$d bytes.", total);
return false;
}
}
if (total != size) {
monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
size, total);
return false;
}
// Create an hex string from the digest
byte[] digest = digester.digest();
n = digest.length;
String hex = "0123456789abcdef"; //$NON-NLS-1$
char[] hexDigest = new char[n * 2];
for (int i = 0; i < n; i++) {
int b = digest[i] & 0x0FF;
hexDigest[i*2 + 0] = hex.charAt(b >>> 4);
hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);
}
String expected = archive.getChecksum();
String actual = new String(hexDigest);
if (!actual.equalsIgnoreCase(expected)) {
monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
expected, actual);
return false;
}
return true;
} catch (Exception e) {
monitor.setResult(e.getMessage());
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// pass
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
// pass
}
}
}
return false;
}
/**
* Install the given archive in the given folder.
*/
private boolean installArchive(Archive archive, File archiveFile, ITaskMonitor monitor) {
String name = archive.getParentPackage().getShortDescription();
String desc = String.format("Installing %1$s", name);
monitor.setDescription(desc);
File destFolder = archive.getParentPackage().getInstallFolder(mUpdaterData.getOsSdkRoot());
File unzipDestFolder = destFolder;
File renamedDestFolder = null;
try {
// If this folder already exists, unzip in a temporary folder and then move/unlink.
if (destFolder.exists()) {
// Find a new temp folder that doesn't exist yet
unzipDestFolder = findTempFolder(destFolder, "new"); //$NON-NLS-1$
if (unzipDestFolder == null) {
// this should not seriously happen.
monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",
destFolder.getPath());
return false;
}
if (!unzipDestFolder.mkdirs()) {
monitor.setResult("Failed to create temp directory %1$s",
unzipDestFolder.getPath());
return false;
}
}
if (!unzipFolder(archiveFile, archive.getSize(), unzipDestFolder, desc, monitor)) {
return false;
}
if (unzipDestFolder != destFolder) {
// Swap the old folder by the new one.
// Both original folders will be deleted in the finally clause below.
renamedDestFolder = findTempFolder(destFolder, "old"); //$NON-NLS-1$
if (renamedDestFolder == null) {
// this should not seriously happen.
monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",
destFolder.getPath());
return false;
}
if (!destFolder.renameTo(renamedDestFolder)) {
monitor.setResult("Failed to rename directory %1$s to %2$s",
destFolder.getPath(), renamedDestFolder.getPath());
return false;
}
if (!unzipDestFolder.renameTo(destFolder)) {
monitor.setResult("Failed to rename directory %1$s to %2$s",
unzipDestFolder.getPath(), destFolder.getPath());
return false;
}
}
return true;
} finally {
// Cleanup if the unzip folder is still set.
deleteFileOrFolder(renamedDestFolder);
if (unzipDestFolder != destFolder) {
deleteFileOrFolder(unzipDestFolder);
}
}
}
private boolean unzipFolder(File archiveFile,
long compressedSize,
File unzipDestFolder,
String description,
ITaskMonitor monitor) {
description += " (%1$d%%)";
FileInputStream fis = null;
ZipInputStream zis = null;
try {
fis = new FileInputStream(archiveFile);
zis = new ZipInputStream(fis);
// To advance the percent and the progress bar, we don't know the number of
// items left to unzip. However we know the size of the archive and the size of
// each uncompressed item. The zip file format overhead is negligible so that's
// a good approximation.
long incStep = compressedSize / NUM_MONITOR_INC;
long incTotal = 0;
long incCurr = 0;
int lastPercent = 0;
byte[] buf = new byte[65536];
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String name = entry.getName();
// ZipFile entries should have forward slashes, but not all Zip
// implementations can be expected to do that.
name = name.replace('\\', '/');
File destFile = new File(unzipDestFolder, name);
if (name.endsWith("/")) { //$NON-NLS-1$
// Create directory if it doesn't exist yet. This allows us to create
// empty directories.
if (!destFile.isDirectory() && !destFile.mkdirs()) {
monitor.setResult("Failed to create temp directory %1$s",
destFile.getPath());
return false;
}
continue;
} else if (name.indexOf('/') != -1) {
// Otherwise it's a file in a sub-directory.
// Make sure the parent directory has been created.
File parentDir = destFile.getParentFile();
if (!parentDir.isDirectory()) {
if (!parentDir.mkdirs()) {
monitor.setResult("Failed to create temp directory %1$s",
parentDir.getPath());
return false;
}
}
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(destFile);
int n;
while ((n = zis.read(buf)) != -1) {
if (n > 0) {
fos.write(buf, 0, n);
}
}
} finally {
if (fos != null) {
fos.close();
}
}
// Increment progress bar to match. We update only between files.
for(incTotal += entry.getCompressedSize(); incCurr < incTotal; incCurr += incStep) {
monitor.incProgress(1);
}
int percent = (int) (100 * incTotal / compressedSize);
if (percent != lastPercent) {
monitor.setDescription(description, percent);
lastPercent = percent;
}
if (monitor.isCancelRequested()) {
return false;
}
}
return true;
} catch (IOException e) {
monitor.setResult("Unzip failed: %1$s", e.getMessage());
} finally {
if (zis != null) {
try {
zis.close();
} catch (IOException e) {
// pass
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
// pass
}
}
}
return false;
}
/**
* Finds a temp folder which name is similar to the one of the ideal folder
* and with a ".tmpN" appended.
* <p/>
* This operation is not atomic so there's no guarantee the folder can't get
* created in between. This is however unlikely and the caller can assume the
* returned folder does not exist yet.
* <p/>
* Returns null if no such folder can be found (e.g. if all candidates exist),
* which is rather unlikely.
*/
private File findTempFolder(File idealFolder, String suffix) {
String basePath = idealFolder.getPath();
for (int i = 1; i < 100; i++) {
File folder = new File(String.format("%1$s.%2$s%3$02d", basePath, suffix, i)); //$NON-NLS-1$
if (!folder.exists()) {
return folder;
}
}
return null;
}
/**
* Deletes a file or a directory.
* Directories are deleted recursively.
* The argument can be null.
*/
private void deleteFileOrFolder(File fileOrFolder) {
if (fileOrFolder != null) {
if (fileOrFolder.isDirectory()) {
// Must delete content recursively first
for (File item : fileOrFolder.listFiles()) {
deleteFileOrFolder(item);
}
}
if (!fileOrFolder.delete()) {
fileOrFolder.deleteOnExit();
}
}
}
// End of hiding from SWT Designer
//$hide<<$
}