Use generics for collections in commit dialog and import page
[egit/imyousuf.git] / org.spearce.egit.ui / src / org / spearce / egit / ui / internal / clone / GitProjectsImportPage.java
blob5d82edc8829fceb7102de6ca07a5c4f138bfcc04
1 package org.spearce.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>
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v1.0
10 * See LICENSE for the full license text, also available.
11 *******************************************************************************/
13 import java.io.File;
14 import java.io.IOException;
15 import java.lang.reflect.InvocationTargetException;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Set;
23 import org.eclipse.core.resources.IProject;
24 import org.eclipse.core.resources.IProjectDescription;
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.core.resources.IWorkspace;
27 import org.eclipse.core.resources.ResourcesPlugin;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IPath;
30 import org.eclipse.core.runtime.IProgressMonitor;
31 import org.eclipse.core.runtime.IStatus;
32 import org.eclipse.core.runtime.OperationCanceledException;
33 import org.eclipse.core.runtime.Path;
34 import org.eclipse.core.runtime.Platform;
35 import org.eclipse.core.runtime.Status;
36 import org.eclipse.core.runtime.SubProgressMonitor;
37 import org.eclipse.jface.dialogs.Dialog;
38 import org.eclipse.jface.dialogs.ErrorDialog;
39 import org.eclipse.jface.operation.IRunnableWithProgress;
40 import org.eclipse.jface.viewers.CheckStateChangedEvent;
41 import org.eclipse.jface.viewers.CheckboxTreeViewer;
42 import org.eclipse.jface.viewers.ICheckStateListener;
43 import org.eclipse.jface.viewers.ITreeContentProvider;
44 import org.eclipse.jface.viewers.LabelProvider;
45 import org.eclipse.jface.viewers.Viewer;
46 import org.eclipse.jface.viewers.ViewerComparator;
47 import org.eclipse.jface.wizard.WizardPage;
48 import org.eclipse.osgi.util.NLS;
49 import org.eclipse.swt.SWT;
50 import org.eclipse.swt.events.SelectionAdapter;
51 import org.eclipse.swt.events.SelectionEvent;
52 import org.eclipse.swt.layout.GridData;
53 import org.eclipse.swt.layout.GridLayout;
54 import org.eclipse.swt.widgets.Button;
55 import org.eclipse.swt.widgets.Composite;
56 import org.eclipse.swt.widgets.Label;
57 import org.eclipse.ui.actions.WorkspaceModifyOperation;
58 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
59 import org.eclipse.ui.internal.ide.StatusUtil;
60 import org.eclipse.ui.internal.wizards.datatransfer.WizardProjectsImportPage;
61 import org.eclipse.ui.statushandlers.StatusManager;
62 import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
63 import org.spearce.egit.core.op.ConnectProviderOperation;
64 import org.spearce.egit.ui.Activator;
65 import org.spearce.egit.ui.UIText;
67 /**
68 * The GitWizardProjectsImportPage is the page that allows the user to import
69 * projects from a particular location. This is a modified copy of
70 * {@link WizardProjectsImportPage}
72 public class GitProjectsImportPage extends WizardPage {
74 /**
75 * The name of the folder containing metadata information for the workspace.
77 public static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$
79 /**
80 * The import structure provider.
82 private IImportStructureProvider structureProvider;
84 class ProjectRecord {
85 File projectSystemFile;
87 String projectName;
89 Object parent;
91 int level;
93 IProjectDescription description;
95 /**
96 * Create a record for a project based on the info in the file.
98 * @param file
100 ProjectRecord(File file) {
101 projectSystemFile = file;
102 setProjectName();
106 * @param parent
107 * The parent folder of the .project file
108 * @param level
109 * The number of levels deep in the provider the file is
111 ProjectRecord(Object parent, int level) {
112 this.parent = parent;
113 this.level = level;
114 setProjectName();
118 * Set the name of the project based on the projectFile.
120 private void setProjectName() {
121 try {
122 // If we don't have the project name try again
123 if (projectName == null) {
124 IPath path = new Path(projectSystemFile.getPath());
125 // if the file is in the default location, use the directory
126 // name as the project name
127 if (isDefaultLocation(path)) {
128 projectName = path.segment(path.segmentCount() - 2);
129 description = IDEWorkbenchPlugin.getPluginWorkspace()
130 .newProjectDescription(projectName);
131 } else {
132 description = IDEWorkbenchPlugin.getPluginWorkspace()
133 .loadProjectDescription(path);
134 projectName = description.getName();
138 } catch (CoreException e) {
139 // no good couldn't get the name
144 * Returns whether the given project description file path is in the
145 * default location for a project
147 * @param path
148 * The path to examine
149 * @return Whether the given path is the default location for a project
151 private boolean isDefaultLocation(IPath path) {
152 // The project description file must at least be within the project,
153 // which is within the workspace location
154 if (path.segmentCount() < 2)
155 return false;
156 return path.removeLastSegments(2).toFile().equals(
157 Platform.getLocation().toFile());
161 * Get the name of the project
163 * @return String
165 public String getProjectName() {
166 return projectName;
170 * Gets the label to be used when rendering this project record in the
171 * UI.
173 * @return String the label
174 * @since 3.4
176 public String getProjectLabel() {
177 if (description == null)
178 return projectName;
180 String path = projectSystemFile == null ? structureProvider
181 .getLabel(parent) : projectSystemFile.getParent();
183 return NLS.bind(UIText.WizardProjectsImportPage_projectLabel,
184 projectName, path);
188 private CheckboxTreeViewer projectsList;
190 private ProjectRecord[] selectedProjects = new ProjectRecord[0];
192 private IProject[] wsProjects;
194 // The last selected path to minimize searches
195 private String lastPath;
197 // The last time that the file or folder at the selected path was modified
198 // to mimize searches
199 private long lastModified;
201 private Button shareCheckBox;
203 private boolean share;
206 * Creates a new project creation wizard page.
208 public GitProjectsImportPage() {
209 this("gitWizardExternalProjectsPage"); //$NON-NLS-1$
213 * Create a new instance of the receiver.
215 * @param pageName
217 public GitProjectsImportPage(String pageName) {
218 super(pageName);
219 setPageComplete(false);
220 setTitle(UIText.WizardProjectsImportPage_ImportProjectsTitle);
221 setDescription(UIText.WizardProjectsImportPage_ImportProjectsDescription);
225 * (non-Javadoc)
227 * @see
228 * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
229 * .Composite)
231 public void createControl(Composite parent) {
233 initializeDialogUnits(parent);
235 Composite workArea = new Composite(parent, SWT.NONE);
236 setControl(workArea);
238 workArea.setLayout(new GridLayout());
239 workArea.setLayoutData(new GridData(GridData.FILL_BOTH
240 | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
242 createProjectsRoot(workArea);
243 createProjectsList(workArea);
244 createOptionsArea(workArea);
245 Dialog.applyDialogFont(workArea);
250 * Create the area with the extra options.
252 * @param workArea
254 private void createOptionsArea(Composite workArea) {
255 Composite optionsGroup = new Composite(workArea, SWT.NONE);
256 optionsGroup.setLayout(new GridLayout());
257 optionsGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
259 shareCheckBox = new Button(optionsGroup, SWT.CHECK);
260 shareCheckBox.setText(UIText.WizardProjectsImportPage_enableGit);
261 shareCheckBox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
262 shareCheckBox.setSelection(share = true);
263 shareCheckBox.addSelectionListener(new SelectionAdapter() {
264 public void widgetSelected(SelectionEvent e) {
265 share = shareCheckBox.getSelection();
271 * Create the checkbox list for the found projects.
273 * @param workArea
275 private void createProjectsList(Composite workArea) {
277 Label title = new Label(workArea, SWT.NONE);
278 title.setText(UIText.WizardProjectsImportPage_ProjectsListTitle);
280 Composite listComposite = new Composite(workArea, SWT.NONE);
281 GridLayout layout = new GridLayout();
282 layout.numColumns = 2;
283 layout.marginWidth = 0;
284 layout.makeColumnsEqualWidth = false;
285 listComposite.setLayout(layout);
287 listComposite.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
288 | GridData.GRAB_VERTICAL | GridData.FILL_BOTH));
290 projectsList = new CheckboxTreeViewer(listComposite, SWT.BORDER);
291 GridData listData = new GridData(GridData.GRAB_HORIZONTAL
292 | GridData.GRAB_VERTICAL | GridData.FILL_BOTH);
293 projectsList.getControl().setLayoutData(listData);
295 projectsList.setContentProvider(new ITreeContentProvider() {
298 * (non-Javadoc)
300 * @see
301 * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java
302 * .lang.Object)
304 public Object[] getChildren(Object parentElement) {
305 return null;
309 * (non-Javadoc)
311 * @see
312 * org.eclipse.jface.viewers.IStructuredContentProvider#getElements
313 * (java.lang.Object)
315 public Object[] getElements(Object inputElement) {
316 return getValidProjects();
320 * (non-Javadoc)
322 * @see
323 * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java
324 * .lang.Object)
326 public boolean hasChildren(Object element) {
327 return false;
331 * (non-Javadoc)
333 * @see
334 * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java
335 * .lang.Object)
337 public Object getParent(Object element) {
338 return null;
342 * (non-Javadoc)
344 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
346 public void dispose() {
351 * (non-Javadoc)
353 * @see
354 * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse
355 * .jface.viewers.Viewer, java.lang.Object, java.lang.Object)
357 public void inputChanged(Viewer viewer, Object oldInput,
358 Object newInput) {
363 projectsList.setLabelProvider(new LabelProvider() {
365 * (non-Javadoc)
367 * @see
368 * org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
370 public String getText(Object element) {
371 return ((ProjectRecord) element).getProjectLabel();
375 projectsList.addCheckStateListener(new ICheckStateListener() {
377 * (non-Javadoc)
379 * @see
380 * org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged
381 * (org.eclipse.jface.viewers.CheckStateChangedEvent)
383 public void checkStateChanged(CheckStateChangedEvent event) {
384 setPageComplete(projectsList.getCheckedElements().length > 0);
388 projectsList.setInput(this);
389 projectsList.setComparator(new ViewerComparator());
390 createSelectionButtons(listComposite);
394 * Create the selection buttons in the listComposite.
396 * @param listComposite
398 private void createSelectionButtons(Composite listComposite) {
399 Composite buttonsComposite = new Composite(listComposite, SWT.NONE);
400 GridLayout layout = new GridLayout();
401 layout.marginWidth = 0;
402 layout.marginHeight = 0;
403 buttonsComposite.setLayout(layout);
405 buttonsComposite.setLayoutData(new GridData(
406 GridData.VERTICAL_ALIGN_BEGINNING));
408 Button selectAll = new Button(buttonsComposite, SWT.PUSH);
409 selectAll.setText(UIText.WizardProjectsImportPage_selectAll);
410 selectAll.addSelectionListener(new SelectionAdapter() {
411 public void widgetSelected(SelectionEvent e) {
412 projectsList.setCheckedElements(selectedProjects);
413 setPageComplete(projectsList.getCheckedElements().length > 0);
416 Dialog.applyDialogFont(selectAll);
417 setButtonLayoutData(selectAll);
419 Button deselectAll = new Button(buttonsComposite, SWT.PUSH);
420 deselectAll.setText(UIText.WizardProjectsImportPage_deselectAll);
421 deselectAll.addSelectionListener(new SelectionAdapter() {
423 * (non-Javadoc)
425 * @see
426 * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
427 * .swt.events.SelectionEvent)
429 public void widgetSelected(SelectionEvent e) {
431 projectsList.setCheckedElements(new Object[0]);
432 setPageComplete(false);
435 Dialog.applyDialogFont(deselectAll);
436 setButtonLayoutData(deselectAll);
441 * Create the area where you select the root directory for the projects.
443 * @param workArea
444 * Composite
446 private void createProjectsRoot(Composite workArea) {
448 // project specification group
449 Composite projectGroup = new Composite(workArea, SWT.NONE);
450 GridLayout layout = new GridLayout();
451 layout.numColumns = 3;
452 layout.makeColumnsEqualWidth = false;
453 layout.marginWidth = 0;
454 projectGroup.setLayout(layout);
455 projectGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
460 * (non-Javadoc) Method declared on IDialogPage. Set the focus on path
461 * fields when page becomes visible.
463 public void setVisible(boolean visible) {
464 super.setVisible(visible);
468 * Update the list of projects based on path.
470 * @param path
472 void setProjectsList(final String path) {
473 // on an empty path empty selectedProjects
474 if (path == null || path.length() == 0) {
475 setMessage(UIText.WizardProjectsImportPage_ImportProjectsDescription);
476 selectedProjects = new ProjectRecord[0];
477 projectsList.refresh(true);
478 projectsList.setCheckedElements(selectedProjects);
479 setPageComplete(projectsList.getCheckedElements().length > 0);
480 lastPath = path;
481 return;
484 final File directory = new File(path);
485 long modified = directory.lastModified();
486 if (path.equals(lastPath) && lastModified == modified) {
487 // since the file/folder was not modified and the path did not
488 // change, no refreshing is required
489 return;
492 lastPath = path;
493 lastModified = modified;
495 try {
496 getContainer().run(true, true, new IRunnableWithProgress() {
499 * (non-Javadoc)
501 * @see
502 * org.eclipse.jface.operation.IRunnableWithProgress#run(org
503 * .eclipse.core.runtime.IProgressMonitor)
505 public void run(IProgressMonitor monitor) {
507 monitor.beginTask(
508 UIText.WizardProjectsImportPage_SearchingMessage,
509 100);
510 selectedProjects = new ProjectRecord[0];
511 Collection<File> files = new ArrayList<File>();
512 monitor.worked(10);
513 if (directory.isDirectory()) {
515 if (!collectProjectFilesFromDirectory(files, directory,
516 null, monitor)) {
517 return;
519 Iterator<File> filesIterator = files.iterator();
520 selectedProjects = new ProjectRecord[files.size()];
521 int index = 0;
522 monitor.worked(50);
523 monitor
524 .subTask(UIText.WizardProjectsImportPage_ProcessingMessage);
525 while (filesIterator.hasNext()) {
526 File file = filesIterator.next();
527 selectedProjects[index] = new ProjectRecord(file);
528 index++;
530 } else {
531 monitor.worked(60);
533 monitor.done();
537 } catch (InvocationTargetException e) {
538 IDEWorkbenchPlugin.log(e.getMessage(), e);
539 } catch (InterruptedException e) {
540 // Nothing to do if the user interrupts.
543 projectsList.refresh(true);
544 projectsList.setCheckedElements(getValidProjects());
545 if (getValidProjects().length < selectedProjects.length) {
546 setMessage(UIText.WizardProjectsImportPage_projectsInWorkspace,
547 WARNING);
548 } else {
549 setMessage(UIText.WizardProjectsImportPage_ImportProjectsDescription);
551 setPageComplete(projectsList.getCheckedElements().length > 0);
555 * Collect the list of .project files that are under directory into files.
557 * @param files
558 * @param directory
559 * @param directoriesVisited
560 * Set of canonical paths of directories, used as recursion guard
561 * @param monitor
562 * The monitor to report to
563 * @return boolean <code>true</code> if the operation was completed.
565 private boolean collectProjectFilesFromDirectory(Collection<File> files,
566 File directory, Set<String> directoriesVisited, IProgressMonitor monitor) {
568 if (monitor.isCanceled()) {
569 return false;
571 monitor.subTask(NLS.bind(
572 UIText.WizardProjectsImportPage_CheckingMessage, directory
573 .getPath()));
574 File[] contents = directory.listFiles();
575 if (contents == null)
576 return false;
578 // Initialize recursion guard for recursive symbolic links
579 if (directoriesVisited == null) {
580 directoriesVisited = new HashSet<String>();
581 try {
582 directoriesVisited.add(directory.getCanonicalPath());
583 } catch (IOException exception) {
584 StatusManager.getManager().handle(
585 StatusUtil.newStatus(IStatus.ERROR, exception
586 .getLocalizedMessage(), exception));
590 // first look for project description files
591 final String dotProject = IProjectDescription.DESCRIPTION_FILE_NAME;
592 for (int i = 0; i < contents.length; i++) {
593 File file = contents[i];
594 if (file.isFile() && file.getName().equals(dotProject)) {
595 files.add(file);
596 // don't search sub-directories since we can't have nested
597 // projects
598 return true;
601 // no project description found, so recurse into sub-directories
602 for (int i = 0; i < contents.length; i++) {
603 if (contents[i].isDirectory()) {
604 if (!contents[i].getName().equals(METADATA_FOLDER)) {
605 try {
606 String canonicalPath = contents[i].getCanonicalPath();
607 if (!directoriesVisited.add(canonicalPath)) {
608 // already been here --> do not recurse
609 continue;
611 } catch (IOException exception) {
612 StatusManager.getManager().handle(
613 StatusUtil.newStatus(IStatus.ERROR, exception
614 .getLocalizedMessage(), exception));
617 collectProjectFilesFromDirectory(files, contents[i],
618 directoriesVisited, monitor);
622 return true;
626 * Create the selected projects
628 * @return boolean <code>true</code> if all project creations were
629 * successful.
631 boolean createProjects() {
632 final Object[] selected = projectsList.getCheckedElements();
633 WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
634 protected void execute(IProgressMonitor monitor)
635 throws InvocationTargetException, InterruptedException {
636 try {
637 monitor.beginTask("", selected.length); //$NON-NLS-1$
638 if (monitor.isCanceled()) {
639 throw new OperationCanceledException();
641 for (int i = 0; i < selected.length; i++) {
642 createExistingProject((ProjectRecord) selected[i],
643 new SubProgressMonitor(monitor, 1));
645 } finally {
646 monitor.done();
650 // run the new project creation operation
651 try {
652 getContainer().run(true, true, op);
653 } catch (InterruptedException e) {
654 return false;
655 } catch (InvocationTargetException e) {
656 // one of the steps resulted in a core exception
657 Throwable t = e.getTargetException();
658 String message = UIText.WizardProjectImportPage_errorMessage;
659 IStatus status;
660 if (t instanceof CoreException) {
661 status = ((CoreException) t).getStatus();
662 } else {
663 status = new Status(IStatus.ERROR,
664 IDEWorkbenchPlugin.IDE_WORKBENCH, 1, message, t);
666 Activator.logError(message, t);
667 ErrorDialog.openError(getShell(), message, null, status);
668 return false;
670 return true;
674 * Create the project described in record. If it is successful return true.
676 * @param record
677 * @param monitor
678 * @return boolean <code>true</code> if successful
679 * @throws InvocationTargetException
680 * @throws InterruptedException
682 private boolean createExistingProject(final ProjectRecord record,
683 IProgressMonitor monitor) throws InvocationTargetException,
684 InterruptedException {
685 String projectName = record.getProjectName();
686 final IWorkspace workspace = ResourcesPlugin.getWorkspace();
687 final IProject project = workspace.getRoot().getProject(projectName);
688 if (record.description == null) {
689 // error case
690 record.description = workspace.newProjectDescription(projectName);
691 IPath locationPath = new Path(record.projectSystemFile
692 .getAbsolutePath());
694 // If it is under the root use the default location
695 if (Platform.getLocation().isPrefixOf(locationPath)) {
696 record.description.setLocation(null);
697 } else {
698 record.description.setLocation(locationPath);
700 } else {
701 record.description.setName(projectName);
704 try {
705 monitor.beginTask(
706 UIText.WizardProjectsImportPage_CreateProjectsTask, 100);
707 project.create(record.description, new SubProgressMonitor(monitor,
708 30));
709 int openTicks = share ? 50 : 70;
710 project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(
711 monitor, openTicks));
712 if (share) {
713 ConnectProviderOperation connectProviderOperation = new ConnectProviderOperation(
714 project, null);
715 connectProviderOperation
716 .run(new SubProgressMonitor(monitor, 20));
718 } catch (CoreException e) {
719 throw new InvocationTargetException(e);
720 } finally {
721 monitor.done();
724 return true;
728 * Method used for test suite.
730 * @return CheckboxTreeViewer the viewer containing all the projects found
732 public CheckboxTreeViewer getProjectsList() {
733 return projectsList;
737 * Retrieve all the projects in the current workspace.
739 * @return IProject[] array of IProject in the current workspace
741 private IProject[] getProjectsInWorkspace() {
742 if (wsProjects == null) {
743 wsProjects = IDEWorkbenchPlugin.getPluginWorkspace().getRoot()
744 .getProjects();
746 return wsProjects;
750 * Get the array of valid project records that can be imported from the
751 * source workspace or archive, selected by the user. If a project with the
752 * same name exists in both the source workspace and the current workspace,
753 * it will not appear in the list of projects to import and thus cannot be
754 * selected for import.
756 * Method declared public for test suite.
758 * @return ProjectRecord[] array of projects that can be imported into the
759 * workspace
761 public ProjectRecord[] getValidProjects() {
762 List<ProjectRecord> validProjects = new ArrayList<ProjectRecord>();
763 for (int i = 0; i < selectedProjects.length; i++) {
764 if (!isProjectInWorkspace(selectedProjects[i].getProjectName())) {
765 validProjects.add(selectedProjects[i]);
768 return validProjects
769 .toArray(new ProjectRecord[validProjects.size()]);
773 * Determine if the project with the given name is in the current workspace.
775 * @param projectName
776 * String the project name to check
777 * @return boolean true if the project with the given name is in this
778 * workspace
780 private boolean isProjectInWorkspace(String projectName) {
781 if (projectName == null) {
782 return false;
784 IProject[] workspaceProjects = getProjectsInWorkspace();
785 for (int i = 0; i < workspaceProjects.length; i++) {
786 if (projectName.equals(workspaceProjects[i].getName())) {
787 return true;
790 return false;