blob: ed926953f16da5ef81f460b8dc68be7a02572b32 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.wizards;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.compare.CompareUI;
import org.eclipse.compare.CompareViewerPane;
import org.eclipse.compare.CompareViewerSwitchingPane;
import org.eclipse.compare.IModificationDate;
import org.eclipse.compare.IStreamContentAccessor;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.Splitter;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSTag;
import org.eclipse.team.internal.ccvs.core.ICVSFile;
import org.eclipse.team.internal.ccvs.core.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFile;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.ILogEntry;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.ui.AdaptableHierarchicalResourceList;
import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
import org.eclipse.team.internal.ccvs.ui.HistoryTableProvider;
import org.eclipse.team.internal.ccvs.ui.IHelpContextIds;
import org.eclipse.team.internal.ccvs.ui.Policy;
import org.eclipse.ui.help.WorkbenchHelp;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.views.navigator.ResourceSorter;
/**
* Select the files to restore
*/
public class RestoreFromRepositoryFileSelectionPage extends CVSWizardPage {
private TreeViewer fileTree;
private CompareViewerPane fileSelectionPane;
private CompareViewerPane revisionSelectionPane;
private CheckboxTableViewer revisionsTable;
private CompareViewerSwitchingPane fileContentPane;
private HistoryTableProvider historyTableProvider;
private AdaptableHierarchicalResourceList treeInput = new AdaptableHierarchicalResourceList(ResourcesPlugin.getWorkspace().getRoot(), new IResource[0]);
private IContainer folder;
private IFile selectedFile;
private ILogEntry selectedRevision;
private Map entriesCache = new HashMap();
private Map filesToRestore = new HashMap();
private static final int WIZARD_WIDTH = 550;
class HistoryInput implements ITypedElement, IStreamContentAccessor, IModificationDate {
IFile file;
ILogEntry logEntry;
HistoryInput(IFile file, ILogEntry logEntry) {
this.file= file;
this.logEntry = logEntry;
}
public InputStream getContents() throws CoreException {
return getContentsFromLogEntry(logEntry);
}
public String getName() {
return file.getName();
}
public String getType() {
return file.getFileExtension();
}
public Image getImage() {
return CompareUI.getImage(file);
}
public long getModificationDate() {
return logEntry.getDate().getTime();
}
}
/**
* Constructor for RestoreFromRepositoryFileSelectionPage.
* @param pageName
* @param title
* @param titleImage
* @param description
*/
public RestoreFromRepositoryFileSelectionPage(
String pageName,
String title,
ImageDescriptor titleImage,
String description) {
super(pageName, title, titleImage, description);
}
/**
* @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
*/
public void createControl(Composite parent) {
Composite composite= createComposite(parent, 1);
setControl(composite);
WorkbenchHelp.setHelp(composite, IHelpContextIds.RESTORE_FROM_REPOSITORY_FILE_SELECTION_PAGE);
// Top and bottom panes: top is the two selection panes, bottom is the file content viewer
Splitter vsplitter= new Splitter(composite, SWT.VERTICAL);
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL
| GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL);
// Set the width to be extra wide to accomodate the two selection lists
data.widthHint = WIZARD_WIDTH;
vsplitter.setLayoutData(data);
// Top left and top right panes: the left for the files, the right for the log entries
Splitter hsplitter= new Splitter(vsplitter, SWT.HORIZONTAL);
// Top left: file selection pane
fileSelectionPane = new CompareViewerPane(hsplitter, SWT.BORDER | SWT.FLAT);
data = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
fileSelectionPane.setLayoutData(data);
fileTree = createFileSelectionTree(fileSelectionPane);
// Top right: Revision selection pane
revisionSelectionPane = new CompareViewerPane(hsplitter, SWT.BORDER | SWT.FLAT);
data = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);;
revisionSelectionPane.setLayoutData(data);
historyTableProvider = new HistoryTableProvider();
revisionsTable = createRevisionSelectionTable(revisionSelectionPane, historyTableProvider);
revisionSelectionPane.setText(Policy.bind("RestoreFromRepositoryFileSelectionPage.emptyRevisionPane")); //$NON-NLS-1$
// Bottom: File content viewer
fileContentPane = new CompareViewerSwitchingPane(vsplitter, SWT.BORDER | SWT.FLAT) {
protected Viewer getViewer(Viewer oldViewer, Object input) {
return CompareUI.findContentViewer(oldViewer, input, this, null);
}
};
initializeValues();
updateWidgetEnablements();
}
protected CheckboxTableViewer createRevisionSelectionTable(CompareViewerPane composite, HistoryTableProvider tableProvider) {
CheckboxTableViewer table = tableProvider.createCheckBoxTable(composite);
table.setContentProvider(new IStructuredContentProvider() {
public Object[] getElements(Object inputElement) {
ILogEntry[] entries = getSelectedEntries();
if (entries != null) return entries;
return new Object[0];
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
});
table.setInput(this);
table.getTable().addSelectionListener(
new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
// Handle check selection in the check state listener
if (e.detail == SWT.CHECK) return;
handleRevisionSelection(e.item);
}
}
);
table.addCheckStateListener(new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent event) {
handleRevisionChecked(event);
}
});
composite.setContent(table.getControl());
return table;
}
protected TreeViewer createFileSelectionTree(CompareViewerPane composite) {
TreeViewer tree = new TreeViewer(composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
tree.setUseHashlookup(true);
tree.setContentProvider(treeInput.getTreeContentProvider());
tree.setLabelProvider(
new DecoratingLabelProvider(
new WorkbenchLabelProvider() {
protected String decorateText(String input, Object element) {
String text;
if (element instanceof IFolder && element.equals(folder)) {
text = super.decorateText(folder.getProjectRelativePath().toString(), element);
} else {
ILogEntry entry = (ILogEntry)filesToRestore.get(element);
text = super.decorateText(input, element);
if (entry != null) {
text = Policy.bind("RestoreFromRepositoryFileSelectionPage.fileToRestore", text, entry.getRevision()); //$NON-NLS-1$
}
}
return text;
}
},
CVSUIPlugin.getPlugin().getWorkbench().getDecoratorManager().getLabelDecorator()));
tree.setSorter(new ResourceSorter(ResourceSorter.NAME));
tree.setInput(treeInput);
GridData data = new GridData(GridData.FILL_BOTH | GridData.GRAB_VERTICAL);
tree.getTree().setLayoutData(data);
tree.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
handleFileSelection(event);
}
});
composite.setContent(tree.getControl());
return tree;
}
/**
* Method updateWidgetEnablements.
*/
private void updateWidgetEnablements() {
if (filesToRestore.isEmpty()) {
setPageComplete(false);
setErrorMessage(null);
return;
}
for (Iterator iter = filesToRestore.keySet().iterator(); iter.hasNext();) {
IFile file = (IFile) iter.next();
if (file.exists()) {
setPageComplete(false);
setErrorMessage(Policy.bind("RestoreFromRepositoryFileSelectionPage.fileExists", file.getName())); //$NON-NLS-1$
return;
}
ILogEntry entry = (ILogEntry) filesToRestore.get(file);
if (entry.isDeletion()) {
setPageComplete(false);
setErrorMessage(Policy.bind("RestoreFromRepositoryFileSelectionPage.revisionIsDeletion", entry.getRevision(), file.getName())); //$NON-NLS-1$
return;
}
}
setPageComplete(true);
setErrorMessage(null);
}
/**
* Method initializeValues.
*/
private void initializeValues() {
refresh();
}
/**
* Sets the folder.
* @param folder The folder to set
*/
public void setInput(IContainer folder, ICVSFile[] files) {
if (folder.equals(this.folder)) return;
this.folder = folder;
setTreeInput(folder, files);
initializeValues();
updateWidgetEnablements();
}
/*
* Set the resource tree input to the files that were deleted
*/
private void setTreeInput(IContainer folder, ICVSFile[] cvsFiles) {
reset();
IResource[] files = new IResource[cvsFiles.length];
for (int i = 0; i < cvsFiles.length; i++) {
try {
files[i] = cvsFiles[i].getIResource();
} catch (CVSException e) {
// In practive, this error shold not occur.
// It may if there is an existing folder with a name that matches the file
// but this is bad in general when using CVS
CVSUIPlugin.log(e);
}
}
treeInput.setResources(files);
// kludge to avoid auto-selection of first element
// set the root to the folder's parent so the folder appears in the tree
treeInput.setRoot(folder.getParent());
refresh();
}
private void reset() {
this.selectedFile = null;
this.selectedRevision = null;
treeInput.setResources(null);
filesToRestore = new HashMap();
if (fileContentPane != null && !fileContentPane.isDisposed()) {
fileContentPane.setInput(null);
}
updateWidgetEnablements();
}
/**
* Method refresh.
*/
private void refresh() {
if (folder == null) return;
if (fileSelectionPane != null && !fileSelectionPane.isDisposed()) {
fileSelectionPane.setText(Policy.bind("RestoreFromRepositoryFileSelectionPage.fileSelectionPaneTitle", folder.getProject().getName())); //$NON-NLS-1$
fileSelectionPane.setImage(CompareUI.getImage(folder.getProject()));
}
if (revisionSelectionPane != null && !revisionSelectionPane.isDisposed()) {
if (selectedFile == null) {
revisionSelectionPane.setText(Policy.bind("RestoreFromRepositoryFileSelectionPage.emptyRevisionPane")); //$NON-NLS-1$
revisionSelectionPane.setImage(null);
}
}
// Empty the file content viewer
if (fileContentPane != null && !fileContentPane.isDisposed()) {
fileContentPane.setInput(null);
}
// refresh the tree
if (fileTree != null) {
// If the parent folder is in the tree, make sure it is expanded
fileTree.setExpandedState(folder, true);
fileTree.refresh();
}
if (revisionsTable != null)
revisionsTable.refresh();
}
/*
* Set the log entry table input to the fetched entries in response to a file selection
*/
private void setLogEntryTableInput(ILogEntry[] entries) {
this.selectedRevision = null;
// Refresh the table so it picks up the selected entries through its content provider
revisionsTable.refresh();
// Check the previously checked entry if one exists
ILogEntry selectedEntry = (ILogEntry)filesToRestore.get(selectedFile);
if (selectedEntry != null) {
revisionsTable.setChecked(selectedEntry, true);
}
// Disable entries that represent deletions since they can't be loaded
for (int i = 0; i < entries.length; i++) {
ILogEntry entry = entries[i];
if (entry.isDeletion()) {
revisionsTable.setGrayed(entry, true);
}
}
// Set the titlebar text for the revisions table
revisionSelectionPane.setText(Policy.bind("RestoreFromRepositoryFileSelectionPage.revisionSelectionPaneTitle", selectedFile.getName())); //$NON-NLS-1$
revisionSelectionPane.setImage(CompareUI.getImage(selectedFile));
// Clear the file content pane
fileContentPane.setInput(null);
}
private void handleFileSelection(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
if (selection == null || selection.isEmpty()) {
clearSelection();
} else {
if (selection instanceof StructuredSelection) {
StructuredSelection structuredSelection = (StructuredSelection) selection;
IResource resource = (IResource)structuredSelection.getFirstElement();
if (resource instanceof IFile) {
handleFileSelection((IFile) resource);
} else {
clearSelection();
}
}
}
}
/**
* Method handleFileSelection.
* @param file
*/
private void handleFileSelection(IFile file) {
if (this.selectedFile == file) return;
this.selectedFile = file;
if (entriesCache.get(file) == null) {
try {
// First, we need to create a remote file handle so we can get the log entries
ICVSFolder parent = CVSWorkspaceRoot.getCVSFolderFor(file.getParent());
FolderSyncInfo info = parent.getFolderSyncInfo();
ICVSRepositoryLocation location = CVSProviderPlugin.getPlugin().getRepository(info.getRoot());
final ICVSRemoteFile remoteFile = location.getRemoteFile(new Path(info.getRepository()).append(file.getName()).toString(), CVSTag.DEFAULT);
// Then we need to fetch the log entries
getContainer().run(true, true, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
// fetch the entries
ILogEntry[] entries = remoteFile.getLogEntries(monitor);
// cache the entries with the selected file
entriesCache.put(selectedFile, entries);
} catch (TeamException e) {
throw new InvocationTargetException(e);
}
}
});
} catch (CVSException e) {
setErrorMessage(
CVSUIPlugin.openError(getShell(), null, null, e, CVSUIPlugin.PERFORM_SYNC_EXEC)
.getMessage());
return;
} catch (InvocationTargetException e) {
setErrorMessage(
CVSUIPlugin.openError(getShell(), null, null, e, CVSUIPlugin.PERFORM_SYNC_EXEC)
.getMessage());
return;
} catch (InterruptedException e) {
return;
}
}
// Set the log table to display the entries for the selected file
setLogEntryTableInput(getSelectedEntries());
}
private ILogEntry[] getSelectedEntries() {
return (ILogEntry[])entriesCache.get(selectedFile);
}
/**
* Method getContents.
* @param logEntry
* @return InputStream
*/
private InputStream getContentsFromLogEntry(final ILogEntry logEntry) {
final InputStream[] is = new InputStream[] { null };
try {
getContainer().run(true, true, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
ICVSRemoteFile remoteFile = logEntry.getRemoteFile();
is[0] = remoteFile.getContents(monitor);
} catch (TeamException e) {
throw new InvocationTargetException(e);
}
}
});
} catch (InvocationTargetException e) {
setErrorMessage(
CVSUIPlugin.openError(getShell(), null, null, e, CVSUIPlugin.PERFORM_SYNC_EXEC)
.getMessage());
return null;
} catch (InterruptedException e) {
return null;
}
return new BufferedInputStream(is[0]);
}
private void handleRevisionChecked(CheckStateChangedEvent event) {
if (event.getChecked()) {
revisionsTable.setCheckedElements(new Object[] {event.getElement()});
filesToRestore.put(selectedFile, event.getElement());
}
if (revisionsTable.getCheckedElements().length == 0) {
filesToRestore.remove(selectedFile);
}
fileTree.refresh();
updateWidgetEnablements();
}
/*
* A revision in the revision table has been selected.
* Populate the file contents pane with the selected log entry.
*/
private void handleRevisionSelection(Widget w) {
if (fileContentPane != null && !fileContentPane.isDisposed()) {
Object o= w.getData();
if (o instanceof ILogEntry) {
ILogEntry selected = (ILogEntry) o;
if (this.selectedRevision == selected) return;
this.selectedRevision = selected;
if (selected.isDeletion()) {
fileContentPane.setInput(null);
} else {
fileContentPane.setInput(new HistoryInput(selectedFile, selected));
fileContentPane.setText(getEditionLabel(selectedFile, selected));
fileContentPane.setImage(CompareUI.getImage(selectedFile));
}
} else {
fileContentPane.setInput(null);
}
}
}
/**
* Method getEditionLabel.
* @param selectedFile
* @param selected
* @return String
*/
private String getEditionLabel(IFile selectedFile, ILogEntry selected) {
return Policy.bind("RestoreFromRepositoryFileSelectionPage.fileContentPaneTitle", //$NON-NLS-1$
new Object[] { selectedFile.getName(), selected.getRevision(), selectedFile.getFullPath().makeRelative().removeLastSegments(1).toString() });
}
public boolean restoreSelectedFiles() {
try {
getContainer().run(true, true, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
monitor.beginTask(null, 100 * filesToRestore.size());
for (Iterator iter = filesToRestore.keySet().iterator();iter.hasNext();) {
IFile file = (IFile) iter.next();
ILogEntry entry = (ILogEntry)filesToRestore.get(file);
ensureParentExists(file);
file.create(entry.getRemoteFile().getContents(Policy.subMonitorFor(monitor, 50)), false, Policy.subMonitorFor(monitor, 50));
}
} catch (TeamException e) {
throw new InvocationTargetException(e);
} catch (CoreException e) {
throw new InvocationTargetException(e);
} finally {
monitor.done();
}
}
});
} catch (InvocationTargetException e) {
setErrorMessage(
CVSUIPlugin.openError(getShell(), null, null, e, CVSUIPlugin.PERFORM_SYNC_EXEC)
.getMessage());
return false;
} catch (InterruptedException e) {
return false;
}
return true;
}
/**
* Method ensureParentExists.
* @param file
*/
private void ensureParentExists(IResource resource) throws CoreException {
IContainer parent = resource.getParent();
if (!parent.exists() && parent.getType() == IResource.FOLDER) {
ensureParentExists(parent);
((IFolder)parent).create(false, true, null);
}
}
private void clearSelection() {
this.selectedFile = null;
this.selectedRevision = null;
refresh();
}
}