/*
blizzy's Backup - Easy to use personal file backup application
Copyright (C) 2011-2012 Maik Schreiber
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, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.blizzy.backup.restore;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.jooq.Condition;
import org.jooq.Cursor;
import org.jooq.Record;
import org.jooq.exception.DataAccessException;
import de.blizzy.backup.BackupApplication;
import de.blizzy.backup.BackupPlugin;
import de.blizzy.backup.Compression;
import de.blizzy.backup.FileAttributes;
import de.blizzy.backup.IStorageInterceptor;
import de.blizzy.backup.Messages;
import de.blizzy.backup.StorageInterceptorDescriptor;
import de.blizzy.backup.Utils;
import de.blizzy.backup.Utils.IFileOrFolderEntry;
import de.blizzy.backup.database.Database;
import de.blizzy.backup.database.EntryType;
import de.blizzy.backup.database.schema.Tables;
import de.blizzy.backup.database.schema.tables.records.BackupsRecord;
import de.blizzy.backup.settings.Settings;
import de.blizzy.backup.vfs.filesystem.FileSystemFileOrFolder;
public class RestoreDialog extends Dialog {
private Settings settings;
private List<Backup> backups = new ArrayList<>();
private Database database;
private ComboViewer backupsViewer;
private Text searchText;
private boolean listenForSearchText = true;
private TableViewer entriesViewer;
private Button moveUpButton;
private Link currentFolderLink;
private Timer timer = new Timer();
private TimerTask searchTimerTask;
private List<IStorageInterceptor> storageInterceptors = new ArrayList<>();
private Boolean alwaysRestoreFromOlderBackups;
public RestoreDialog(Shell parentShell) {
super(parentShell);
settings = BackupApplication.getSettingsManager().getSettings();
database = new Database(settings, false);
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setImages(BackupApplication.getWindowImages());
newShell.setText(Messages.Title_Restore);
}
@Override
protected boolean isResizable() {
return true;
}
@Override
public int open() {
final ProgressMonitorDialog dlg = new ProgressMonitorDialog(getParentShell());
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask(Messages.Title_OpenBackupDatabase, IProgressMonitor.UNKNOWN);
final boolean[] ok = { true };
List<StorageInterceptorDescriptor> descs = BackupPlugin.getDefault().getStorageInterceptors();
for (final StorageInterceptorDescriptor desc : descs) {
final IStorageInterceptor interceptor = desc.getStorageInterceptor();
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() {
IDialogSettings settings = Utils.getChildSection(
Utils.getSection("storageInterceptors"), desc.getId()); //$NON-NLS-1$
if (!interceptor.initialize(dlg.getShell(), settings)) {
ok[0] = false;
}
}
@Override
public void handleException(Throwable t) {
ok[0] = false;
interceptor.showErrorMessage(t, dlg.getShell());
BackupPlugin.getDefault().logError(
"error while initializing storage interceptor '" + desc.getName() + "'", t); //$NON-NLS-1$ //$NON-NLS-2$
}
});
storageInterceptors.add(interceptor);
}
if (!ok[0]) {
monitor.done();
throw new InterruptedException();
}
Cursor<BackupsRecord> cursor = null;
try {
database.open(storageInterceptors);
database.initialize();
cursor = database.factory()
.selectFrom(Tables.BACKUPS).where(Tables.BACKUPS.NUM_ENTRIES.isNotNull()).orderBy(Tables.BACKUPS.RUN_TIME.desc())
.fetchLazy();
while (cursor.hasNext()) {
BackupsRecord record = cursor.fetchOne();
Backup backup = new Backup(record.getId().intValue(), new Date(record.getRunTime().getTime()), record.getNumEntries().intValue());
backups.add(backup);
}
} catch (SQLException | IOException e) {
boolean handled = false;
for (IStorageInterceptor interceptor : storageInterceptors) {
if (interceptor.showErrorMessage(e, dlg.getShell())) {
handled = true;
}
}
if (handled) {
throw new InterruptedException();
}
throw new InvocationTargetException(e);
} finally {
database.closeQuietly(cursor);
monitor.done();
}
}
};
try {
dlg.run(true, false, runnable);
} catch (InvocationTargetException e) {
// TODO
BackupPlugin.getDefault().logError("Error while opening backup database", e); //$NON-NLS-1$
} catch (InterruptedException e) {
return Window.CANCEL;
}
return super.open();
}
@Override
public boolean close() {
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) {
monitor.beginTask(Messages.Title_CloseBackupDatabase, IProgressMonitor.UNKNOWN);
try {
database.close();
for (final IStorageInterceptor interceptor : storageInterceptors) {
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() {
interceptor.destroy();
}
@Override
public void handleException(Throwable t) {
BackupPlugin.getDefault().logError("error while destroying storage interceptor", t); //$NON-NLS-1$
}
});
}
} finally {
monitor.done();
}
}
};
ProgressMonitorDialog dlg = new ProgressMonitorDialog(getShell());
try {
dlg.run(true, false, runnable);
} catch (InvocationTargetException e) {
// TODO
BackupPlugin.getDefault().logError("Error while closing backup database", e); //$NON-NLS-1$
} catch (InterruptedException e) {
// not cancelable
}
System.gc();
return super.close();
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CLOSE_LABEL, false);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
((GridLayout) composite.getLayout()).numColumns = 2;
((GridLayout) composite.getLayout()).makeColumnsEqualWidth = false;
Composite backupsAndSearchComposite = new Composite(composite, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
layout.marginWidth = 0;
layout.marginHeight = 0;
backupsAndSearchComposite.setLayout(layout);
backupsAndSearchComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
Label label = new Label(backupsAndSearchComposite, SWT.NONE);
label.setText(Messages.Label_ShowBackupContentsAt + ":"); //$NON-NLS-1$
label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
backupsViewer = new ComboViewer(backupsAndSearchComposite);
backupsViewer.getCombo().setVisibleItemCount(10);
backupsViewer.getControl().setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
label = new Label(backupsAndSearchComposite, SWT.NONE);
label.setText(Messages.Label_SearchFileFolder + ":"); //$NON-NLS-1$
label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
searchText = new Text(backupsAndSearchComposite, SWT.BORDER);
searchText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
new Label(composite, SWT.NONE);
backupsViewer.setContentProvider(new ArrayContentProvider());
backupsViewer.setLabelProvider(new BackupLabelProvider());
backupsViewer.setSorter(new ViewerSorter() {
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
return ((Backup) e2).runTime.compareTo(((Backup) e1).runTime);
}
});
backupsViewer.setInput(backups);
entriesViewer = new TableViewer(composite, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION |
SWT.V_SCROLL | SWT.H_SCROLL);
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
gd.widthHint = convertWidthInCharsToPixels(100);
gd.heightHint = convertHeightInCharsToPixels(20);
entriesViewer.getControl().setLayoutData(gd);
entriesViewer.setContentProvider(new ArrayContentProvider());
entriesViewer.setLabelProvider(new EntryLabelProvider(parent.getDisplay()));
entriesViewer.setSorter(new EntrySorter());
Table table = entriesViewer.getTable();
TableLayout tableLayout = new TableLayout();
table.setLayout(tableLayout);
table.setHeaderVisible(true);
table.setLinesVisible(false);
TableColumn col = new TableColumn(table, SWT.LEFT);
col.setText(Messages.Label_Name);
tableLayout.addColumnData(new ColumnWeightData(50, true));
col = new TableColumn(table, SWT.LEFT);
col.setText(Messages.Label_Size);
tableLayout.addColumnData(new ColumnWeightData(20, true));
col = new TableColumn(table, SWT.LEFT);
col.setText(Messages.Label_ModificationDate);
tableLayout.addColumnData(new ColumnWeightData(30, true));
Composite entriesButtonsComposite = new Composite(composite, SWT.NONE);
layout = new GridLayout(1, false);
layout.marginWidth = 0;
layout.marginHeight = 0;
entriesButtonsComposite.setLayout(layout);
entriesButtonsComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, true));
moveUpButton = new Button(entriesButtonsComposite, SWT.PUSH);
moveUpButton.setText(Messages.Button_MoveUp);
moveUpButton.setEnabled(false);
moveUpButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
final Button restoreButton = new Button(entriesButtonsComposite, SWT.PUSH);
restoreButton.setText(Messages.Button_Restore + "..."); //$NON-NLS-1$
restoreButton.setEnabled(false);
restoreButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
currentFolderLink = new Link(composite, SWT.NONE);
gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
gd.horizontalSpan = 2;
currentFolderLink.setLayoutData(gd);
backupsViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent e) {
try {
Backup backup = (Backup) ((IStructuredSelection) e.getSelection()).getFirstElement();
showBackup(backup);
} catch (DataAccessException ex) {
BackupPlugin.getDefault().logError("error while showing backup", ex); //$NON-NLS-1$
}
}
});
searchText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
if (listenForSearchText) {
startSearchTimer(searchText.getText());
}
}
});
entriesViewer.addOpenListener(new IOpenListener() {
@Override
public void open(OpenEvent e) {
IStructuredSelection selection = (IStructuredSelection) e.getSelection();
if (selection.size() == 1) {
Entry entry = (Entry) selection.getFirstElement();
if (entry.type == EntryType.FOLDER) {
listenForSearchText = false;
try {
searchText.setText(StringUtils.EMPTY);
} finally {
listenForSearchText = true;
}
try {
Backup backup = (Backup) ((IStructuredSelection) backupsViewer.getSelection()).getFirstElement();
showFolder(backup, entry);
} catch (DataAccessException ex) {
BackupPlugin.getDefault().logError("error while showing folder", ex); //$NON-NLS-1$
}
}
}
}
});
entriesViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent e) {
restoreButton.setEnabled(!e.getSelection().isEmpty());
}
});
moveUpButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
moveUp();
} catch (DataAccessException ex) {
BackupPlugin.getDefault().logError("error while moving up", ex); //$NON-NLS-1$
}
}
});
restoreButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
@SuppressWarnings("unchecked")
List<Entry> entries = ((IStructuredSelection) entriesViewer.getSelection()).toList();
restore(entries);
}
});
currentFolderLink.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
Backup backup = (Backup) ((IStructuredSelection) backupsViewer.getSelection()).getFirstElement();
int pos = e.text.indexOf('_');
int folderId = Integer.parseInt(e.text.substring(0, pos));
int parentFolderId = Integer.parseInt(e.text.substring(pos + 1));
showFolder(backup, folderId, parentFolderId);
} catch (DataAccessException ex) {
BackupPlugin.getDefault().logError("error while showing folder", ex); //$NON-NLS-1$
}
}
});
if (!backups.isEmpty()) {
backupsViewer.setSelection(new StructuredSelection(backups.get(0)), true);
}
return composite;
}
private void showBackup(Backup backup) {
showEntries(backup, -1);
moveUpButton.setEnabled(false);
}
private void showFolder(Backup backup, Entry entry) {
showFolder(backup, entry.id, entry.parentId);
}
private void showFolder(Backup backup, int entryId, int parentFolderId) {
showEntries(backup, entryId);
moveUpButton.setEnabled(true);
moveUpButton.setData((parentFolderId > 0) ? Integer.valueOf(parentFolderId) : backup);
}
private void moveUp() {
Object data = moveUpButton.getData();
if (data instanceof Backup) {
showBackup((Backup) data);
} else {
int folderId = ((Integer) moveUpButton.getData()).intValue();
Backup backup = (Backup) ((IStructuredSelection) backupsViewer.getSelection()).getFirstElement();
try {
Integer parentIdInt = database.factory()
.select(Tables.ENTRIES.PARENT_ID).from(Tables.ENTRIES).where(Tables.ENTRIES.ID.equal(Integer.valueOf(folderId)))
.fetchOne(Tables.ENTRIES.PARENT_ID);
showEntries(backup, folderId);
moveUpButton.setEnabled(true);
moveUpButton.setData((parentIdInt != null) ? parentIdInt : backup);
} catch (DataAccessException e) {
BackupPlugin.getDefault().logError("error while getting parent ID", e); //$NON-NLS-1$
}
}
}
private void showEntries(Backup backup, int parentFolderId) {
List<Entry> entries = Collections.emptyList();
Cursor<Record> cursor = null;
try {
cursor = getEntriesCursor(backup.id, parentFolderId, null, -1);
entries = getEntries(cursor, false);
} catch (DataAccessException e) {
BackupPlugin.getDefault().logError("error while loading entries", e); //$NON-NLS-1$
} finally {
database.closeQuietly(cursor);
}
((EntryLabelProvider) entriesViewer.getLabelProvider()).setShowFullPath(false);
((EntrySorter) entriesViewer.getSorter()).setSortFullPath(false);
entriesViewer.setInput(entries);
entriesViewer.getControl().setData(Integer.valueOf(parentFolderId));
listenForSearchText = false;
try {
searchText.setText(StringUtils.EMPTY);
} finally {
listenForSearchText = true;
}
String folder = getFolderLink(parentFolderId);
currentFolderLink.setText(StringUtils.isNotBlank(folder) ?
Messages.Label_CurrentFolder + ": " + getFolderLink(parentFolderId) : //$NON-NLS-1$
StringUtils.EMPTY);
}
private String getFolderLink(int folderId) {
if (folderId <= 0) {
return null;
}
Record record = database.factory()
.select(Tables.ENTRIES.NAME, Tables.ENTRIES.PARENT_ID)
.from(Tables.ENTRIES)
.where(Tables.ENTRIES.ID.equal(Integer.valueOf(folderId)))
.fetchOne();
String name = record.getValue(Tables.ENTRIES.NAME);
Integer parentFolderId = record.getValue(Tables.ENTRIES.PARENT_ID);
String parentFolder = getFolderLink((parentFolderId != null) ? parentFolderId.intValue() : -1);
String part = "<a href=\"" + folderId + "_" + ((parentFolderId != null) ? parentFolderId.intValue() : -1) + //$NON-NLS-1$ //$NON-NLS-2$
"\">" + name + "</a>"; //$NON-NLS-1$ //$NON-NLS-2$
return (parentFolder != null) ? parentFolder + File.separator + part : part;
}
private String getFolderPath(int folderId) {
if (folderId <= 0) {
return null;
}
Record record = database.factory()
.select(Tables.ENTRIES.NAME, Tables.ENTRIES.PARENT_ID)
.from(Tables.ENTRIES)
.where(Tables.ENTRIES.ID.equal(Integer.valueOf(folderId)))
.fetchOne();
String name = record.getValue(Tables.ENTRIES.NAME);
Integer parentFolderId = record.getValue(Tables.ENTRIES.PARENT_ID);
String parentPath = getFolderPath((parentFolderId != null) ? parentFolderId.intValue() : -1);
return (parentPath != null) ? parentPath + File.separator + name : name;
}
private Cursor<Record> getEntriesCursor(int backupId, int parentFolderId, String searchText, int entryId) {
Condition searchCondition;
if (entryId > 0) {
searchCondition = Tables.ENTRIES.ID.equal(Integer.valueOf(entryId));
} else if (StringUtils.isNotBlank(searchText)) {
searchCondition = Tables.ENTRIES.NAME_LOWER.like("%" + searchText.toLowerCase() + "%"); //$NON-NLS-1$ //$NON-NLS-2$
} else if (parentFolderId > 0) {
searchCondition = Tables.ENTRIES.PARENT_ID.equal(Integer.valueOf(parentFolderId));
} else {
searchCondition = Tables.ENTRIES.PARENT_ID.isNull();
}
return database.factory()
.select(Tables.ENTRIES.ID, Tables.ENTRIES.PARENT_ID, Tables.ENTRIES.NAME, Tables.ENTRIES.TYPE,
Tables.ENTRIES.CREATION_TIME, Tables.ENTRIES.MODIFICATION_TIME, Tables.ENTRIES.HIDDEN,
Tables.FILES.LENGTH, Tables.FILES.BACKUP_PATH, Tables.FILES.COMPRESSION)
.from(Tables.ENTRIES)
.leftOuterJoin(Tables.FILES)
.on(Tables.FILES.ID.equal(Tables.ENTRIES.FILE_ID))
.where(Tables.ENTRIES.BACKUP_ID.equal(Integer.valueOf(backupId)),
searchCondition)
.orderBy(Tables.ENTRIES.NAME.asc())
.fetchLazy();
}
private List<Entry> getEntries(Cursor<Record> cursor, boolean fullPaths) {
List<Entry> entries = new ArrayList<>();
while (cursor.hasNext()) {
Record record = cursor.fetchOne();
Entry entry = toEntry(record, fullPaths);
entries.add(entry);
}
return entries;
}
private Entry toEntry(Record record, boolean fullPaths) {
int id = record.getValue(Tables.ENTRIES.ID).intValue();
Integer parentIdInt = record.getValue(Tables.ENTRIES.PARENT_ID);
int parentId = (parentIdInt != null) ? parentIdInt.intValue() : -1;
String name = record.getValue(Tables.ENTRIES.NAME);
EntryType type = EntryType.fromValue(record.getValue(Tables.ENTRIES.TYPE).intValue());
Timestamp createTime = record.getValue(Tables.ENTRIES.CREATION_TIME);
Date creationTime = (createTime != null) ? new Date(createTime.getTime()) : null;
Timestamp modTime = record.getValue(Tables.ENTRIES.MODIFICATION_TIME);
Date modificationTime = (modTime != null) ? new Date(modTime.getTime()) : null;
boolean hidden = record.getValue(Tables.ENTRIES.HIDDEN).booleanValue();
Long lengthLong = record.getValue(Tables.FILES.LENGTH);
long length = (lengthLong != null) ? lengthLong.longValue() : -1;
String backupPath = record.getValue(Tables.FILES.BACKUP_PATH);
Byte compressionByte = record.getValue(Tables.FILES.COMPRESSION);
Compression compression = (compressionByte != null) ? Compression.fromValue(compressionByte.intValue()) : null;
Entry entry = new Entry(id, parentId, name, type, creationTime, modificationTime, hidden, length, backupPath,
compression);
if (fullPaths) {
entry.fullPath = getFolderPath(parentId);
}
return entry;
}
private Entry findInOlderBackups(Entry entry) throws IOException {
Date runTime = new Date(database.factory()
.select(Tables.BACKUPS.RUN_TIME)
.from(Tables.ENTRIES)
.join(Tables.BACKUPS)
.on(Tables.BACKUPS.ID.equal(Tables.ENTRIES.BACKUP_ID))
.where(Tables.ENTRIES.ID.equal(Integer.valueOf(entry.id)))
.fetchOne()
.getValue(Tables.BACKUPS.RUN_TIME).getTime());
Cursor<Record> cursor = database.factory()
.select(Tables.BACKUPS.ID)
.from(Tables.BACKUPS)
.where(Tables.BACKUPS.RUN_TIME.lessThan(new Timestamp(runTime.getTime())))
.orderBy(Tables.BACKUPS.RUN_TIME.desc())
.fetchLazy();
try {
while (cursor.hasNext()) {
Record record = cursor.fetchOne();
int backupId = record.getValue(Tables.BACKUPS.ID).intValue();
int oldEntryId = findInBackup(entry.id, backupId);
if (oldEntryId > 0) {
Cursor<Record> entriesCursor = getEntriesCursor(backupId, -1, null, oldEntryId);
try {
List<Entry> entries = getEntries(entriesCursor, false);
if (!entries.isEmpty()) {
Entry oldEntry = entries.get(0);
if (oldEntry.type == EntryType.FILE) {
return oldEntry;
}
}
} finally {
database.closeQuietly(entriesCursor);
}
}
}
} finally {
database.closeQuietly(cursor);
}
return null;
}
private int findInBackup(int entryId, int backupId) throws IOException {
IFileOrFolderEntry entry = toFileOrFolderEntry(entryId);
return Utils.findFileOrFolderEntryInBackup(entry, backupId, database);
}
private Utils.IFileOrFolderEntry toFileOrFolderEntry(final int entryId) {
Record record = database.factory()
.select(Tables.ENTRIES.NAME, Tables.ENTRIES.PARENT_ID, Tables.ENTRIES.TYPE)
.from(Tables.ENTRIES)
.where(Tables.ENTRIES.ID.equal(Integer.valueOf(entryId)))
.fetchOne();
final String name = record.getValue(Tables.ENTRIES.NAME);
final Integer parentId = record.getValue(Tables.ENTRIES.PARENT_ID);
final EntryType type = EntryType.fromValue(record.getValue(Tables.ENTRIES.TYPE).intValue());
return new Utils.IFileOrFolderEntry() {
@Override
public boolean isFolder() {
return type == EntryType.FOLDER;
}
@Override
public IFileOrFolderEntry getParentFolder() {
return (parentId != null) ? toFileOrFolderEntry(parentId.intValue()) : null;
}
@Override
public String getName() {
return name;
}
@Override
public String getAbsolutePath() {
return RestoreDialog.this.getAbsolutePath(entryId);
}
};
}
private String getAbsolutePath(int entryId) {
Record record = database.factory()
.select(Tables.ENTRIES.NAME, Tables.ENTRIES.PARENT_ID)
.from(Tables.ENTRIES)
.where(Tables.ENTRIES.ID.equal(Integer.valueOf(entryId)))
.fetchOne();
String name = record.getValue(Tables.ENTRIES.NAME);
Integer parentId = record.getValue(Tables.ENTRIES.PARENT_ID);
return (parentId != null) ? getAbsolutePath(parentId.intValue()) + File.separator + name : name;
}
private void restore(final Collection<Entry> entries) {
String folder = null;
for (;;) {
DirectoryDialog dlg = new DirectoryDialog(getShell(), SWT.SAVE);
dlg.setText(Messages.Title_SelectOutputFolder);
dlg.setFilterPath(folder);
folder = dlg.open();
if (folder == null) {
break;
}
if (new File(folder).list().length > 0) {
MessageDialog.openError(getShell(), Messages.Title_FolderNotEmpty,
NLS.bind(Messages.FolderNotEmpty,
Utils.getSimpleName(new FileSystemFileOrFolder(new File(folder)))));
continue;
}
break;
}
if (folder != null) {
alwaysRestoreFromOlderBackups = null;
final String myFolder = folder;
Backup backup = (Backup) ((IStructuredSelection) backupsViewer.getSelection()).getFirstElement();
final int backupId = backup.id;
final int numEntries = backup.numEntries;
final ProgressMonitorDialog dlg = new ProgressMonitorDialog(getShell());
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
try {
monitor.beginTask(Messages.Title_RestoreFromBackup, numEntries);
for (Entry entry : entries) {
restoreEntry(entry, new File(myFolder), settings.getOutputFolder(), backupId,
monitor, dlg.getShell());
}
} catch (IOException e) {
throw new InvocationTargetException(e);
} finally {
monitor.done();
}
}
};
try {
dlg.run(true, true, runnable);
} catch (InvocationTargetException e) {
// TODO
BackupPlugin.getDefault().logError("error while restoring from backup", e.getCause()); //$NON-NLS-1$
} catch (InterruptedException e) {
// okay
}
}
}
private void restoreEntry(Entry entry, File parentFolder, String outputFolder, int backupId,
IProgressMonitor monitor, Shell shell) throws IOException, InterruptedException {
if (monitor.isCanceled()) {
throw new InterruptedException();
}
boolean isFolder = entry.type == EntryType.FOLDER;
if (entry.type == EntryType.FAILED_FILE) {
if (alwaysRestoreFromOlderBackups == null) {
alwaysRestoreFromOlderBackups = Boolean.valueOf(promptRestoreFromOlderBackups(shell));
}
if (alwaysRestoreFromOlderBackups.booleanValue()) {
entry = findInOlderBackups(entry);
}
}
if ((entry != null) && (entry.type != EntryType.FAILED_FILE)) {
Path outputPath;
if (entry.type == EntryType.FOLDER) {
File newFolder = new File(parentFolder, escapeFileName(entry.name));
FileUtils.forceMkdir(newFolder);
Cursor<Record> cursor = getEntriesCursor(backupId, entry.id, null, -1);
try {
for (Entry e : getEntries(cursor, false)) {
restoreEntry(e, newFolder, outputFolder, backupId, monitor, shell);
}
} finally {
database.closeQuietly(cursor);
}
outputPath = newFolder.toPath();
} else {
File inputFile = Utils.toBackupFile(entry.backupPath, outputFolder);
File outputFile = new File(parentFolder, escapeFileName(entry.name));
outputPath = outputFile.toPath();
InputStream in = null;
try {
InputStream fileIn = new BufferedInputStream(new FileInputStream(inputFile));
InputStream interceptIn = fileIn;
for (IStorageInterceptor interceptor : storageInterceptors) {
interceptIn = interceptor.interceptInputStream(interceptIn, entry.length);
}
InputStream compressIn = entry.compression.getInputStream(interceptIn);
in = compressIn;
Files.copy(in, outputPath);
} finally {
IOUtils.closeQuietly(in);
}
}
FileAttributes fileAttributes = FileAttributes.get(outputPath);
if (entry.hidden) {
fileAttributes.setHidden(entry.hidden);
}
FileTime createTime = (entry.creationTime != null) ? FileTime.fromMillis(entry.creationTime.getTime()) : null;
FileTime modTime = (entry.modificationTime != null) ? FileTime.fromMillis(entry.modificationTime.getTime()) : null;
fileAttributes.setTimes(createTime, modTime);
}
if (!isFolder) {
monitor.worked(1);
}
}
private boolean promptRestoreFromOlderBackups(final Shell shell) {
Display display = shell.getDisplay();
final boolean[] result = new boolean[1];
display.syncExec(new Runnable() {
@Override
public void run() {
if (!shell.isDisposed()) {
result[0] = MessageDialog.openQuestion(shell, Messages.Title_FailedFiles, Messages.RestoreFailedFilesFromOlderBackups);
}
}
});
return result[0];
}
private String escapeFileName(String name) {
return name
.replace('/', '_')
.replace('\\', '_')
.replace(':', '_')
.replace(';', '_')
.replaceAll("__", "_"); //$NON-NLS-1$ //$NON-NLS-2$
}
private void startSearchTimer(final String text) {
synchronized (this) {
if (searchTimerTask != null) {
searchTimerTask.cancel();
}
searchTimerTask = new TimerTask() {
@Override
public void run() {
if (StringUtils.isNotBlank(text)) {
runSearchAsync(text);
} else {
final Shell shell = getShell();
final Display display = shell.getDisplay();
if (!display.isDisposed()) {
display.asyncExec(new Runnable() {
@Override
public void run() {
if (!display.isDisposed() && !shell.isDisposed()) {
Backup backup = (Backup) ((IStructuredSelection) backupsViewer.getSelection()).getFirstElement();
try {
showBackup(backup);
} catch (DataAccessException e) {
BackupPlugin.getDefault().logError("error while showing backup", e); //$NON-NLS-1$
}
}
}
});
}
}
}
};
timer.schedule(searchTimerTask, 1000L);
}
}
private void runSearchAsync(String text) {
final Shell shell = getShell();
final Display display = shell.getDisplay();
final org.eclipse.swt.graphics.Cursor[] oldCursor = new org.eclipse.swt.graphics.Cursor[1];
final int[] backupId = { -1 };
if (!display.isDisposed()) {
display.syncExec(new Runnable() {
@Override
public void run() {
if (!display.isDisposed() && !shell.isDisposed()) {
oldCursor[0] = shell.getCursor();
shell.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
shell.setEnabled(false);
Backup backup = (Backup) ((IStructuredSelection) backupsViewer.getSelection()).getFirstElement();
if (backup != null) {
backupId[0] = backup.id;
}
}
}
});
}
Cursor<Record> cursor = null;
try {
if (backupId[0] > 0) {
cursor = getEntriesCursor(backupId[0], -1, text, -1);
final List<Entry> entries = getEntries(cursor, true);
if (!display.isDisposed()) {
display.syncExec(new Runnable() {
@Override
public void run() {
if (!display.isDisposed() && !shell.isDisposed()) {
((EntryLabelProvider) entriesViewer.getLabelProvider()).setShowFullPath(true);
((EntrySorter) entriesViewer.getSorter()).setSortFullPath(true);
entriesViewer.setInput(entries);
currentFolderLink.setText(StringUtils.EMPTY);
moveUpButton.setEnabled(false);
}
}
});
}
}
} catch (DataAccessException e) {
BackupPlugin.getDefault().logError("error while searching for entries", e); //$NON-NLS-1$
} finally {
database.closeQuietly(cursor);
if (!display.isDisposed()) {
display.syncExec(new Runnable() {
@Override
public void run() {
if (!display.isDisposed() && !shell.isDisposed()) {
shell.setEnabled(true);
shell.setCursor(oldCursor[0]);
}
}
});
}
}
}
}