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 *******************************************************************************/
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
;
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
;
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
{
75 * The name of the folder containing metadata information for the workspace.
77 public static final String METADATA_FOLDER
= ".metadata"; //$NON-NLS-1$
80 * The import structure provider.
82 private IImportStructureProvider structureProvider
;
85 File projectSystemFile
;
93 IProjectDescription description
;
96 * Create a record for a project based on the info in the file.
100 ProjectRecord(File file
) {
101 projectSystemFile
= file
;
107 * The parent folder of the .project file
109 * The number of levels deep in the provider the file is
111 ProjectRecord(Object parent
, int level
) {
112 this.parent
= parent
;
118 * Set the name of the project based on the projectFile.
120 private void setProjectName() {
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
);
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
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)
156 return path
.removeLastSegments(2).toFile().equals(
157 Platform
.getLocation().toFile());
161 * Get the name of the project
165 public String
getProjectName() {
170 * Gets the label to be used when rendering this project record in the
173 * @return String the label
176 public String
getProjectLabel() {
177 if (description
== null)
180 String path
= projectSystemFile
== null ? structureProvider
181 .getLabel(parent
) : projectSystemFile
.getParent();
183 return NLS
.bind(UIText
.WizardProjectsImportPage_projectLabel
,
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.
217 public GitProjectsImportPage(String pageName
) {
219 setPageComplete(false);
220 setTitle(UIText
.WizardProjectsImportPage_ImportProjectsTitle
);
221 setDescription(UIText
.WizardProjectsImportPage_ImportProjectsDescription
);
228 * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
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.
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.
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() {
301 * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java
304 public Object
[] getChildren(Object parentElement
) {
312 * org.eclipse.jface.viewers.IStructuredContentProvider#getElements
315 public Object
[] getElements(Object inputElement
) {
316 return getValidProjects();
323 * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java
326 public boolean hasChildren(Object element
) {
334 * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java
337 public Object
getParent(Object element
) {
344 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
346 public void dispose() {
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
,
363 projectsList
.setLabelProvider(new LabelProvider() {
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() {
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() {
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.
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.
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);
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
493 lastModified
= modified
;
496 getContainer().run(true, true, new IRunnableWithProgress() {
502 * org.eclipse.jface.operation.IRunnableWithProgress#run(org
503 * .eclipse.core.runtime.IProgressMonitor)
505 public void run(IProgressMonitor monitor
) {
508 UIText
.WizardProjectsImportPage_SearchingMessage
,
510 selectedProjects
= new ProjectRecord
[0];
511 Collection
<File
> files
= new ArrayList
<File
>();
513 if (directory
.isDirectory()) {
515 if (!collectProjectFilesFromDirectory(files
, directory
,
519 Iterator
<File
> filesIterator
= files
.iterator();
520 selectedProjects
= new ProjectRecord
[files
.size()];
524 .subTask(UIText
.WizardProjectsImportPage_ProcessingMessage
);
525 while (filesIterator
.hasNext()) {
526 File file
= filesIterator
.next();
527 selectedProjects
[index
] = new ProjectRecord(file
);
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
,
549 setMessage(UIText
.WizardProjectsImportPage_ImportProjectsDescription
);
551 setPageComplete(projectsList
.getCheckedElements().length
> 0);
555 * Collect the list of .project files that are under directory into files.
559 * @param directoriesVisited
560 * Set of canonical paths of directories, used as recursion guard
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()) {
571 monitor
.subTask(NLS
.bind(
572 UIText
.WizardProjectsImportPage_CheckingMessage
, directory
574 File
[] contents
= directory
.listFiles();
575 if (contents
== null)
578 // Initialize recursion guard for recursive symbolic links
579 if (directoriesVisited
== null) {
580 directoriesVisited
= new HashSet
<String
>();
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
)) {
596 // don't search sub-directories since we can't have nested
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
)) {
606 String canonicalPath
= contents
[i
].getCanonicalPath();
607 if (!directoriesVisited
.add(canonicalPath
)) {
608 // already been here --> do not recurse
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
);
626 * Create the selected projects
628 * @return boolean <code>true</code> if all project creations were
631 boolean createProjects() {
632 final Object
[] selected
= projectsList
.getCheckedElements();
633 WorkspaceModifyOperation op
= new WorkspaceModifyOperation() {
634 protected void execute(IProgressMonitor monitor
)
635 throws InvocationTargetException
, InterruptedException
{
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));
650 // run the new project creation operation
652 getContainer().run(true, true, op
);
653 } catch (InterruptedException e
) {
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
;
660 if (t
instanceof CoreException
) {
661 status
= ((CoreException
) t
).getStatus();
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
);
674 * Create the project described in record. If it is successful return true.
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) {
690 record
.description
= workspace
.newProjectDescription(projectName
);
691 IPath locationPath
= new Path(record
.projectSystemFile
694 // If it is under the root use the default location
695 if (Platform
.getLocation().isPrefixOf(locationPath
)) {
696 record
.description
.setLocation(null);
698 record
.description
.setLocation(locationPath
);
701 record
.description
.setName(projectName
);
706 UIText
.WizardProjectsImportPage_CreateProjectsTask
, 100);
707 project
.create(record
.description
, new SubProgressMonitor(monitor
,
709 int openTicks
= share ?
50 : 70;
710 project
.open(IResource
.BACKGROUND_REFRESH
, new SubProgressMonitor(
711 monitor
, openTicks
));
713 ConnectProviderOperation connectProviderOperation
= new ConnectProviderOperation(
715 connectProviderOperation
716 .run(new SubProgressMonitor(monitor
, 20));
718 } catch (CoreException e
) {
719 throw new InvocationTargetException(e
);
728 * Method used for test suite.
730 * @return CheckboxTreeViewer the viewer containing all the projects found
732 public CheckboxTreeViewer
getProjectsList() {
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()
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
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
]);
769 .toArray(new ProjectRecord
[validProjects
.size()]);
773 * Determine if the project with the given name is in the current workspace.
776 * String the project name to check
777 * @return boolean true if the project with the given name is in this
780 private boolean isProjectInWorkspace(String projectName
) {
781 if (projectName
== null) {
784 IProject
[] workspaceProjects
= getProjectsInWorkspace();
785 for (int i
= 0; i
< workspaceProjects
.length
; i
++) {
786 if (projectName
.equals(workspaceProjects
[i
].getName())) {