Fix enabling team operations on project import
[egit/spearce.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / clone / GitProjectsImportPage.java
blob6ff3c5799ef04fa59e61d91d8a0d090f5ff7295e
1 package org.eclipse.egit.ui.internal.clone;
3 /*******************************************************************************
4 * Copyright (c) 2004, 2008 IBM Corporation and others.
5 * Copyright (C) 2007, Martin Oberhuber (martin.oberhuber@windriver.com)
6 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
7 * Copyright (C) 2009, Mykola Nikishov <mn@mn.com.ua>
9 * All rights reserved. This program and the accompanying materials
10 * are made available under the terms of the Eclipse Public License v1.0
11 * which accompanies this distribution, and is available at
12 * http://www.eclipse.org/legal/epl-v10.html
13 *******************************************************************************/
15 import java.io.File;
16 import java.io.IOException;
17 import java.lang.reflect.InvocationTargetException;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Set;
25 import org.eclipse.core.resources.IProject;
26 import org.eclipse.core.resources.IProjectDescription;
27 import org.eclipse.core.resources.IResource;
28 import org.eclipse.core.resources.IWorkspace;
29 import org.eclipse.core.resources.ResourcesPlugin;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IPath;
32 import org.eclipse.core.runtime.IProgressMonitor;
33 import org.eclipse.core.runtime.IStatus;
34 import org.eclipse.core.runtime.OperationCanceledException;
35 import org.eclipse.core.runtime.Path;
36 import org.eclipse.core.runtime.Platform;
37 import org.eclipse.core.runtime.Status;
38 import org.eclipse.core.runtime.SubProgressMonitor;
39 import org.eclipse.egit.core.op.ConnectProviderOperation;
40 import org.eclipse.egit.ui.Activator;
41 import org.eclipse.egit.ui.UIText;
42 import org.eclipse.jface.dialogs.Dialog;
43 import org.eclipse.jface.dialogs.ErrorDialog;
44 import org.eclipse.jface.operation.IRunnableWithProgress;
45 import org.eclipse.jface.viewers.CheckStateChangedEvent;
46 import org.eclipse.jface.viewers.CheckboxTreeViewer;
47 import org.eclipse.jface.viewers.ICheckStateListener;
48 import org.eclipse.jface.viewers.ITreeContentProvider;
49 import org.eclipse.jface.viewers.LabelProvider;
50 import org.eclipse.jface.viewers.Viewer;
51 import org.eclipse.jface.viewers.ViewerComparator;
52 import org.eclipse.jface.wizard.WizardPage;
53 import org.eclipse.osgi.util.NLS;
54 import org.eclipse.swt.SWT;
55 import org.eclipse.swt.events.SelectionAdapter;
56 import org.eclipse.swt.events.SelectionEvent;
57 import org.eclipse.swt.layout.GridData;
58 import org.eclipse.swt.layout.GridLayout;
59 import org.eclipse.swt.widgets.Button;
60 import org.eclipse.swt.widgets.Composite;
61 import org.eclipse.swt.widgets.Label;
62 import org.eclipse.ui.actions.WorkspaceModifyOperation;
63 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
64 import org.eclipse.ui.internal.ide.StatusUtil;
65 import org.eclipse.ui.internal.wizards.datatransfer.WizardProjectsImportPage;
66 import org.eclipse.ui.statushandlers.StatusManager;
67 import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
69 /**
70 * The GitWizardProjectsImportPage is the page that allows the user to import
71 * projects from a particular location. This is a modified copy of
72 * {@link WizardProjectsImportPage}
74 public class GitProjectsImportPage extends WizardPage {
76 /**
77 * The name of the folder containing metadata information for the workspace.
79 public static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$
81 /**
82 * The import structure provider.
84 private IImportStructureProvider structureProvider;
86 /**
87 * The git directory which will contain the repository
89 private File gitDir;
91 class ProjectRecord {
92 File projectSystemFile;
94 String projectName;
96 Object parent;
98 int level;
100 IProjectDescription description;
103 * Create a record for a project based on the info in the file.
105 * @param file
107 ProjectRecord(File file) {
108 projectSystemFile = file;
109 setProjectName();
113 * @param parent
114 * The parent folder of the .project file
115 * @param level
116 * The number of levels deep in the provider the file is
118 ProjectRecord(Object parent, int level) {
119 this.parent = parent;
120 this.level = level;
121 setProjectName();
125 * Set the name of the project based on the projectFile.
127 private void setProjectName() {
128 try {
129 // If we don't have the project name try again
130 if (projectName == null) {
131 IPath path = new Path(projectSystemFile.getPath());
132 // if the file is in the default location, use the directory
133 // name as the project name
134 if (isDefaultLocation(path)) {
135 projectName = path.segment(path.segmentCount() - 2);
136 description = ResourcesPlugin.getWorkspace()
137 .newProjectDescription(projectName);
138 } else {
139 description = ResourcesPlugin.getWorkspace()
140 .loadProjectDescription(path);
141 projectName = description.getName();
145 } catch (CoreException e) {
146 // no good couldn't get the name
151 * Returns whether the given project description file path is in the
152 * default location for a project
154 * @param path
155 * The path to examine
156 * @return Whether the given path is the default location for a project
158 private boolean isDefaultLocation(IPath path) {
159 // The project description file must at least be within the project,
160 // which is within the workspace location
161 if (path.segmentCount() < 2)
162 return false;
163 return path.removeLastSegments(2).toFile().equals(
164 Platform.getLocation().toFile());
168 * Get the name of the project
170 * @return String
172 public String getProjectName() {
173 return projectName;
177 * Gets the label to be used when rendering this project record in the
178 * UI.
180 * @return String the label
181 * @since 3.4
183 public String getProjectLabel() {
184 if (description == null)
185 return projectName;
187 String path = projectSystemFile == null ? structureProvider
188 .getLabel(parent) : projectSystemFile.getParent();
190 return NLS.bind(UIText.WizardProjectsImportPage_projectLabel,
191 projectName, path);
195 private CheckboxTreeViewer projectsList;
197 private ProjectRecord[] selectedProjects = new ProjectRecord[0];
199 private IProject[] wsProjects;
201 // The last selected path to minimize searches
202 private String lastPath;
204 // The last time that the file or folder at the selected path was modified
205 // to minimize searches
206 private long lastModified;
208 private Button shareCheckBox;
210 private boolean share;
213 * Creates a new project creation wizard page.
215 public GitProjectsImportPage() {
216 this("gitWizardExternalProjectsPage"); //$NON-NLS-1$
220 * Create a new instance of the receiver.
222 * @param pageName
224 public GitProjectsImportPage(String pageName) {
225 super(pageName);
226 setPageComplete(false);
227 setTitle(UIText.WizardProjectsImportPage_ImportProjectsTitle);
228 setDescription(UIText.WizardProjectsImportPage_ImportProjectsDescription);
232 * (non-Javadoc)
234 * @see
235 * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
236 * .Composite)
238 public void createControl(Composite parent) {
240 initializeDialogUnits(parent);
242 Composite workArea = new Composite(parent, SWT.NONE);
243 setControl(workArea);
245 workArea.setLayout(new GridLayout());
246 workArea.setLayoutData(new GridData(GridData.FILL_BOTH
247 | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
249 createProjectsRoot(workArea);
250 createProjectsList(workArea);
251 createOptionsArea(workArea);
252 Dialog.applyDialogFont(workArea);
257 * Create the area with the extra options.
259 * @param workArea
261 private void createOptionsArea(Composite workArea) {
262 Composite optionsGroup = new Composite(workArea, SWT.NONE);
263 optionsGroup.setLayout(new GridLayout());
264 optionsGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
266 shareCheckBox = new Button(optionsGroup, SWT.CHECK);
267 shareCheckBox.setText(UIText.WizardProjectsImportPage_enableGit);
268 shareCheckBox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
269 shareCheckBox.setSelection(share = true);
270 shareCheckBox.addSelectionListener(new SelectionAdapter() {
271 public void widgetSelected(SelectionEvent e) {
272 share = shareCheckBox.getSelection();
278 * Create the checkbox list for the found projects.
280 * @param workArea
282 private void createProjectsList(Composite workArea) {
284 Label title = new Label(workArea, SWT.NONE);
285 title.setText(UIText.WizardProjectsImportPage_ProjectsListTitle);
287 Composite listComposite = new Composite(workArea, SWT.NONE);
288 GridLayout layout = new GridLayout();
289 layout.numColumns = 2;
290 layout.marginWidth = 0;
291 layout.makeColumnsEqualWidth = false;
292 listComposite.setLayout(layout);
294 listComposite.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
295 | GridData.GRAB_VERTICAL | GridData.FILL_BOTH));
297 projectsList = new CheckboxTreeViewer(listComposite, SWT.BORDER);
298 GridData listData = new GridData(GridData.GRAB_HORIZONTAL
299 | GridData.GRAB_VERTICAL | GridData.FILL_BOTH);
300 projectsList.getControl().setLayoutData(listData);
302 projectsList.setContentProvider(new ITreeContentProvider() {
305 * (non-Javadoc)
307 * @see
308 * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java
309 * .lang.Object)
311 public Object[] getChildren(Object parentElement) {
312 return null;
316 * (non-Javadoc)
318 * @see
319 * org.eclipse.jface.viewers.IStructuredContentProvider#getElements
320 * (java.lang.Object)
322 public Object[] getElements(Object inputElement) {
323 return getValidProjects();
327 * (non-Javadoc)
329 * @see
330 * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java
331 * .lang.Object)
333 public boolean hasChildren(Object element) {
334 return false;
338 * (non-Javadoc)
340 * @see
341 * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java
342 * .lang.Object)
344 public Object getParent(Object element) {
345 return null;
349 * (non-Javadoc)
351 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
353 public void dispose() {
358 * (non-Javadoc)
360 * @see
361 * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse
362 * .jface.viewers.Viewer, java.lang.Object, java.lang.Object)
364 public void inputChanged(Viewer viewer, Object oldInput,
365 Object newInput) {
370 projectsList.setLabelProvider(new LabelProvider() {
372 * (non-Javadoc)
374 * @see
375 * org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
377 public String getText(Object element) {
378 return ((ProjectRecord) element).getProjectLabel();
382 projectsList.addCheckStateListener(new ICheckStateListener() {
384 * (non-Javadoc)
386 * @see
387 * org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged
388 * (org.eclipse.jface.viewers.CheckStateChangedEvent)
390 public void checkStateChanged(CheckStateChangedEvent event) {
391 setPageComplete(projectsList.getCheckedElements().length > 0);
395 projectsList.setInput(this);
396 projectsList.setComparator(new ViewerComparator());
397 createSelectionButtons(listComposite);
401 * Create the selection buttons in the listComposite.
403 * @param listComposite
405 private void createSelectionButtons(Composite listComposite) {
406 Composite buttonsComposite = new Composite(listComposite, SWT.NONE);
407 GridLayout layout = new GridLayout();
408 layout.marginWidth = 0;
409 layout.marginHeight = 0;
410 buttonsComposite.setLayout(layout);
412 buttonsComposite.setLayoutData(new GridData(
413 GridData.VERTICAL_ALIGN_BEGINNING));
415 Button selectAll = new Button(buttonsComposite, SWT.PUSH);
416 selectAll.setText(UIText.WizardProjectsImportPage_selectAll);
417 selectAll.addSelectionListener(new SelectionAdapter() {
418 public void widgetSelected(SelectionEvent e) {
419 projectsList.setCheckedElements(selectedProjects);
420 setPageComplete(projectsList.getCheckedElements().length > 0);
423 Dialog.applyDialogFont(selectAll);
424 setButtonLayoutData(selectAll);
426 Button deselectAll = new Button(buttonsComposite, SWT.PUSH);
427 deselectAll.setText(UIText.WizardProjectsImportPage_deselectAll);
428 deselectAll.addSelectionListener(new SelectionAdapter() {
430 * (non-Javadoc)
432 * @see
433 * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
434 * .swt.events.SelectionEvent)
436 public void widgetSelected(SelectionEvent e) {
438 projectsList.setCheckedElements(new Object[0]);
439 setPageComplete(false);
442 Dialog.applyDialogFont(deselectAll);
443 setButtonLayoutData(deselectAll);
448 * Create the area where you select the root directory for the projects.
450 * @param workArea
451 * Composite
453 private void createProjectsRoot(Composite workArea) {
455 // project specification group
456 Composite projectGroup = new Composite(workArea, SWT.NONE);
457 GridLayout layout = new GridLayout();
458 layout.numColumns = 3;
459 layout.makeColumnsEqualWidth = false;
460 layout.marginWidth = 0;
461 projectGroup.setLayout(layout);
462 projectGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
467 * (non-Javadoc) Method declared on IDialogPage. Set the focus on path
468 * fields when page becomes visible.
470 public void setVisible(boolean visible) {
471 super.setVisible(visible);
475 * Set the git directory which will contain the repository
477 * @param gitDir
479 public void setGitDir(File gitDir) {
480 this.gitDir = gitDir;
484 * Update the list of projects based on path.
486 * @param path
488 void setProjectsList(final String path) {
489 // on an empty path empty selectedProjects
490 if (path == null || path.length() == 0) {
491 setMessage(UIText.WizardProjectsImportPage_ImportProjectsDescription);
492 selectedProjects = new ProjectRecord[0];
493 projectsList.refresh(true);
494 projectsList.setCheckedElements(selectedProjects);
495 setPageComplete(projectsList.getCheckedElements().length > 0);
496 lastPath = path;
497 return;
500 final File directory = new File(path);
501 long modified = directory.lastModified();
502 if (path.equals(lastPath) && lastModified == modified) {
503 // since the file/folder was not modified and the path did not
504 // change, no refreshing is required
505 return;
508 lastPath = path;
509 lastModified = modified;
511 try {
512 getContainer().run(true, true, new IRunnableWithProgress() {
515 * (non-Javadoc)
517 * @see
518 * org.eclipse.jface.operation.IRunnableWithProgress#run(org
519 * .eclipse.core.runtime.IProgressMonitor)
521 public void run(IProgressMonitor monitor) {
523 monitor.beginTask(
524 UIText.WizardProjectsImportPage_SearchingMessage,
525 100);
526 selectedProjects = new ProjectRecord[0];
527 Collection<File> files = new ArrayList<File>();
528 monitor.worked(10);
529 if (directory.isDirectory()) {
531 if (!collectProjectFilesFromDirectory(files, directory,
532 null, monitor)) {
533 return;
535 Iterator<File> filesIterator = files.iterator();
536 selectedProjects = new ProjectRecord[files.size()];
537 int index = 0;
538 monitor.worked(50);
539 monitor
540 .subTask(UIText.WizardProjectsImportPage_ProcessingMessage);
541 while (filesIterator.hasNext()) {
542 File file = filesIterator.next();
543 selectedProjects[index] = new ProjectRecord(file);
544 index++;
546 } else {
547 monitor.worked(60);
549 monitor.done();
553 } catch (InvocationTargetException e) {
554 IDEWorkbenchPlugin.log(e.getMessage(), e);
555 } catch (InterruptedException e) {
556 // Nothing to do if the user interrupts.
559 projectsList.refresh(true);
560 projectsList.setCheckedElements(getValidProjects());
561 if (getValidProjects().length < selectedProjects.length) {
562 setMessage(UIText.WizardProjectsImportPage_projectsInWorkspace,
563 WARNING);
564 } else {
565 setMessage(UIText.WizardProjectsImportPage_ImportProjectsDescription);
567 setPageComplete(projectsList.getCheckedElements().length > 0);
571 * Collect the list of .project files that are under directory into files.
573 * @param files
574 * @param directory
575 * @param directoriesVisited
576 * Set of canonical paths of directories, used as recursion guard
577 * @param monitor
578 * The monitor to report to
579 * @return boolean <code>true</code> if the operation was completed.
581 private boolean collectProjectFilesFromDirectory(Collection<File> files,
582 File directory, Set<String> directoriesVisited, IProgressMonitor monitor) {
584 if (monitor.isCanceled()) {
585 return false;
587 monitor.subTask(NLS.bind(
588 UIText.WizardProjectsImportPage_CheckingMessage, directory
589 .getPath()));
590 File[] contents = directory.listFiles();
591 if (contents == null)
592 return false;
594 // Initialize recursion guard for recursive symbolic links
595 if (directoriesVisited == null) {
596 directoriesVisited = new HashSet<String>();
597 try {
598 directoriesVisited.add(directory.getCanonicalPath());
599 } catch (IOException exception) {
600 StatusManager.getManager().handle(
601 StatusUtil.newStatus(IStatus.ERROR, exception
602 .getLocalizedMessage(), exception));
606 // first look for project description files
607 final String dotProject = IProjectDescription.DESCRIPTION_FILE_NAME;
608 for (int i = 0; i < contents.length; i++) {
609 File file = contents[i];
610 if (file.isFile() && file.getName().equals(dotProject)) {
611 files.add(file);
612 // don't search sub-directories since we can't have nested
613 // projects
614 return true;
617 // no project description found, so recurse into sub-directories
618 for (int i = 0; i < contents.length; i++) {
619 if (contents[i].isDirectory()) {
620 if (!contents[i].getName().equals(METADATA_FOLDER)) {
621 try {
622 String canonicalPath = contents[i].getCanonicalPath();
623 if (!directoriesVisited.add(canonicalPath)) {
624 // already been here --> do not recurse
625 continue;
627 } catch (IOException exception) {
628 StatusManager.getManager().handle(
629 StatusUtil.newStatus(IStatus.ERROR, exception
630 .getLocalizedMessage(), exception));
633 collectProjectFilesFromDirectory(files, contents[i],
634 directoriesVisited, monitor);
638 return true;
642 * Create the selected projects
644 * @return boolean <code>true</code> if all project creations were
645 * successful.
647 boolean createProjects() {
648 final Object[] selected = projectsList.getCheckedElements();
649 WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
650 protected void execute(IProgressMonitor monitor)
651 throws InvocationTargetException, InterruptedException {
652 try {
653 monitor.beginTask("", selected.length); //$NON-NLS-1$
654 if (monitor.isCanceled()) {
655 throw new OperationCanceledException();
657 for (int i = 0; i < selected.length; i++) {
658 createExistingProject((ProjectRecord) selected[i],
659 new SubProgressMonitor(monitor, 1));
661 } finally {
662 monitor.done();
666 // run the new project creation operation
667 try {
668 getContainer().run(true, true, op);
669 } catch (InterruptedException e) {
670 return false;
671 } catch (InvocationTargetException e) {
672 // one of the steps resulted in a core exception
673 Throwable t = e.getTargetException();
674 String message = UIText.WizardProjectImportPage_errorMessage;
675 IStatus status;
676 if (t instanceof CoreException) {
677 status = ((CoreException) t).getStatus();
678 } else {
679 status = new Status(IStatus.ERROR,
680 IDEWorkbenchPlugin.IDE_WORKBENCH, 1, message, t);
682 Activator.logError(message, t);
683 ErrorDialog.openError(getShell(), message, null, status);
684 return false;
686 return true;
690 * Create the project described in record. If it is successful return true.
692 * @param record
693 * @param monitor
694 * @return boolean <code>true</code> if successful
695 * @throws InvocationTargetException
696 * @throws InterruptedException
698 private boolean createExistingProject(final ProjectRecord record,
699 IProgressMonitor monitor) throws InvocationTargetException,
700 InterruptedException {
701 String projectName = record.getProjectName();
702 final IWorkspace workspace = ResourcesPlugin.getWorkspace();
703 final IProject project = workspace.getRoot().getProject(projectName);
704 if (record.description == null) {
705 // error case
706 record.description = workspace.newProjectDescription(projectName);
707 IPath locationPath = new Path(record.projectSystemFile
708 .getAbsolutePath());
710 // If it is under the root use the default location
711 if (Platform.getLocation().isPrefixOf(locationPath)) {
712 record.description.setLocation(null);
713 } else {
714 record.description.setLocation(locationPath);
716 } else {
717 record.description.setName(projectName);
720 try {
721 monitor.beginTask(
722 UIText.WizardProjectsImportPage_CreateProjectsTask, 100);
723 project.create(record.description, new SubProgressMonitor(monitor,
724 30));
725 int openTicks = share ? 50 : 70;
726 project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(
727 monitor, openTicks));
728 if (share) {
729 ConnectProviderOperation connectProviderOperation = new ConnectProviderOperation(
730 project, gitDir);
731 connectProviderOperation
732 .run(new SubProgressMonitor(monitor, 20));
734 } catch (CoreException e) {
735 throw new InvocationTargetException(e);
736 } finally {
737 monitor.done();
740 return true;
744 * Method used for test suite.
746 * @return CheckboxTreeViewer the viewer containing all the projects found
748 public CheckboxTreeViewer getProjectsList() {
749 return projectsList;
753 * Retrieve all the projects in the current workspace.
755 * @return IProject[] array of IProject in the current workspace
757 private IProject[] getProjectsInWorkspace() {
758 if (wsProjects == null) {
759 wsProjects = IDEWorkbenchPlugin.getPluginWorkspace().getRoot()
760 .getProjects();
762 return wsProjects;
766 * Get the array of valid project records that can be imported from the
767 * source workspace or archive, selected by the user. If a project with the
768 * same name exists in both the source workspace and the current workspace,
769 * it will not appear in the list of projects to import and thus cannot be
770 * selected for import.
772 * Method declared public for test suite.
774 * @return ProjectRecord[] array of projects that can be imported into the
775 * workspace
777 public ProjectRecord[] getValidProjects() {
778 List<ProjectRecord> validProjects = new ArrayList<ProjectRecord>();
779 for (int i = 0; i < selectedProjects.length; i++) {
780 if (!isProjectInWorkspace(selectedProjects[i].getProjectName())) {
781 validProjects.add(selectedProjects[i]);
784 return validProjects
785 .toArray(new ProjectRecord[validProjects.size()]);
789 * Determine if the project with the given name is in the current workspace.
791 * @param projectName
792 * String the project name to check
793 * @return boolean true if the project with the given name is in this
794 * workspace
796 private boolean isProjectInWorkspace(String projectName) {
797 if (projectName == null) {
798 return false;
800 IProject[] workspaceProjects = getProjectsInWorkspace();
801 for (int i = 0; i < workspaceProjects.length; i++) {
802 if (projectName.equals(workspaceProjects[i].getName())) {
803 return true;
806 return false;