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>
8 * Copyright (C) 2010, Wim Jongman <wim.jongman@remainsoftware.com>
10 * All rights reserved. This program and the accompanying materials
11 * are made available under the terms of the Eclipse Public License v1.0
12 * which accompanies this distribution, and is available at
13 * http://www.eclipse.org/legal/epl-v10.html
14 *******************************************************************************/
17 import java
.io
.IOException
;
18 import java
.lang
.reflect
.InvocationTargetException
;
19 import java
.util
.ArrayList
;
20 import java
.util
.Collection
;
21 import java
.util
.HashSet
;
22 import java
.util
.Iterator
;
23 import java
.util
.List
;
26 import org
.eclipse
.core
.resources
.IProject
;
27 import org
.eclipse
.core
.resources
.IProjectDescription
;
28 import org
.eclipse
.core
.resources
.IResource
;
29 import org
.eclipse
.core
.resources
.IWorkspace
;
30 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
31 import org
.eclipse
.core
.runtime
.CoreException
;
32 import org
.eclipse
.core
.runtime
.IPath
;
33 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
34 import org
.eclipse
.core
.runtime
.IStatus
;
35 import org
.eclipse
.core
.runtime
.OperationCanceledException
;
36 import org
.eclipse
.core
.runtime
.Path
;
37 import org
.eclipse
.core
.runtime
.Platform
;
38 import org
.eclipse
.core
.runtime
.Status
;
39 import org
.eclipse
.core
.runtime
.SubProgressMonitor
;
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
.operation
.IRunnableWithProgress
;
44 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
45 import org
.eclipse
.jface
.viewers
.LabelProvider
;
46 import org
.eclipse
.jface
.viewers
.TreeViewer
;
47 import org
.eclipse
.jface
.viewers
.Viewer
;
48 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
49 import org
.eclipse
.jface
.wizard
.WizardPage
;
50 import org
.eclipse
.osgi
.util
.NLS
;
51 import org
.eclipse
.swt
.SWT
;
52 import org
.eclipse
.swt
.events
.MouseAdapter
;
53 import org
.eclipse
.swt
.events
.MouseEvent
;
54 import org
.eclipse
.swt
.events
.SelectionAdapter
;
55 import org
.eclipse
.swt
.events
.SelectionEvent
;
56 import org
.eclipse
.swt
.graphics
.Point
;
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
.Display
;
62 import org
.eclipse
.swt
.widgets
.Label
;
63 import org
.eclipse
.swt
.widgets
.Tree
;
64 import org
.eclipse
.swt
.widgets
.TreeItem
;
65 import org
.eclipse
.ui
.actions
.WorkspaceModifyOperation
;
66 import org
.eclipse
.ui
.dialogs
.FilteredTree
;
67 import org
.eclipse
.ui
.dialogs
.PatternFilter
;
68 import org
.eclipse
.ui
.statushandlers
.StatusManager
;
69 import org
.eclipse
.ui
.wizards
.datatransfer
.IImportStructureProvider
;
72 * The GitWizardProjectsImportPage is the page that allows the user to import
73 * projects from a particular location. This is a modified copy of the
74 * WizardProjectsImportPage class from the org.eclipse.ui.ide bundle.
76 public class GitProjectsImportPage
extends WizardPage
{
79 * The name of the folder containing metadata information for the workspace.
81 public static final String METADATA_FOLDER
= ".metadata"; //$NON-NLS-1$
83 private IImportStructureProvider structureProvider
;
85 private TreeViewer projectsList
;
87 private ProjectRecord
[] selectedProjects
= new ProjectRecord
[0];
89 final private HashSet
<Object
> checkedItems
= new HashSet
<Object
>();
91 private IProject
[] wsProjects
;
93 // The last selected path to minimize searches
94 private String lastPath
;
96 // The last time that the file or folder at the selected path was modified
97 // to minimize searches
98 private long lastModified
;
100 private Button selectAll
;
102 private Button deselectAll
;
105 * Creates a new project creation wizard page.
107 public GitProjectsImportPage() {
108 super(GitProjectsImportPage
.class.getName());
109 setPageComplete(false);
110 setTitle(UIText
.WizardProjectsImportPage_ImportProjectsTitle
);
111 setDescription(UIText
.WizardProjectsImportPage_ImportProjectsDescription
);
114 public void createControl(Composite parent
) {
116 initializeDialogUnits(parent
);
118 Composite workArea
= new Composite(parent
, SWT
.NONE
);
119 setControl(workArea
);
121 workArea
.setLayout(new GridLayout());
122 workArea
.setLayoutData(new GridData(GridData
.FILL_BOTH
123 | GridData
.GRAB_HORIZONTAL
| GridData
.GRAB_VERTICAL
));
125 createProjectsRoot(workArea
);
126 createProjectsList(workArea
);
127 Dialog
.applyDialogFont(workArea
);
132 * Create the checkbox list for the found projects.
136 private void createProjectsList(Composite workArea
) {
138 checkedItems
.clear();
140 Label title
= new Label(workArea
, SWT
.NONE
);
141 title
.setText(UIText
.WizardProjectsImportPage_ProjectsListTitle
);
143 Composite listComposite
= new Composite(workArea
, SWT
.NONE
);
144 GridLayout layout
= new GridLayout();
145 layout
.numColumns
= 2;
146 layout
.marginWidth
= 0;
147 layout
.makeColumnsEqualWidth
= false;
148 listComposite
.setLayout(layout
);
150 listComposite
.setLayoutData(new GridData(GridData
.GRAB_HORIZONTAL
151 | GridData
.GRAB_VERTICAL
| GridData
.FILL_BOTH
));
153 PatternFilter filter
= new PatternFilter() {
156 public boolean isElementVisible(Viewer viewer
, Object element
) {
158 if (checkedItems
.contains(element
)) {
162 return super.isElementVisible(viewer
, element
);
166 public void setPattern(String patternString
) {
167 super.setPattern(patternString
);
168 // TODO: is there a better way to react on changes in the tree?
169 // disable select all button when tree becomes empty due to
171 Display
.getDefault().asyncExec(new Runnable() {
173 enableSelectAllButtons();
180 // we have to use the old constructor in order to be 3.4 compatible
181 // TODO once we drop 3.4 support, we should change to the new constructor
182 FilteredTree filteredTree
= new FilteredTree(listComposite
, SWT
.CHECK
183 | SWT
.BORDER
, filter
);
184 filteredTree
.setInitialText(UIText
.WizardProjectsImportPage_filterText
);
185 projectsList
= filteredTree
.getViewer();
186 GridData listData
= new GridData(GridData
.GRAB_HORIZONTAL
187 | GridData
.GRAB_VERTICAL
| GridData
.FILL_BOTH
);
188 projectsList
.getControl().setLayoutData(listData
);
190 projectsList
.setContentProvider(new ITreeContentProvider() {
192 public Object
[] getChildren(Object parentElement
) {
196 public Object
[] getElements(Object inputElement
) {
197 return getValidProjects();
200 public boolean hasChildren(Object element
) {
204 public Object
getParent(Object element
) {
208 public void dispose() {
212 public void inputChanged(Viewer viewer
, Object oldInput
,
219 projectsList
.getTree().addMouseListener(new MouseAdapter() {
221 public void mouseUp(MouseEvent e
) {
222 if (e
.widget
instanceof Tree
) {
223 TreeItem item
= ((Tree
) e
.widget
).getItem(new Point(e
.x
,
226 if (item
.getChecked())
227 checkedItems
.add(item
.getData());
229 checkedItems
.remove(item
.getData());
230 setPageComplete(!checkedItems
.isEmpty());
236 projectsList
.setLabelProvider(new LabelProvider() {
237 public String
getText(Object element
) {
238 // Need to set the checked item state. FIXME This is clumsy.
239 for (final TreeItem item
: projectsList
.getTree().getItems()) {
240 if (checkedItems
.contains(item
.getData()))
241 item
.setChecked(true);
243 item
.setChecked(false);
245 return ((ProjectRecord
) element
).getProjectLabel(structureProvider
);
249 projectsList
.setInput(this);
250 projectsList
.setComparator(new ViewerComparator());
251 createSelectionButtons(listComposite
);
255 * Create the selection buttons in the listComposite.
257 * @param listComposite
259 private void createSelectionButtons(Composite listComposite
) {
260 Composite buttonsComposite
= new Composite(listComposite
, SWT
.NONE
);
261 GridLayout layout
= new GridLayout();
262 layout
.marginWidth
= 0;
263 layout
.marginHeight
= 0;
264 buttonsComposite
.setLayout(layout
);
266 buttonsComposite
.setLayoutData(new GridData(
267 GridData
.VERTICAL_ALIGN_BEGINNING
));
269 selectAll
= new Button(buttonsComposite
, SWT
.PUSH
);
270 selectAll
.setText(UIText
.WizardProjectsImportPage_selectAll
);
271 selectAll
.addSelectionListener(new SelectionAdapter() {
272 public void widgetSelected(SelectionEvent e
) {
273 checkedItems
.clear();
274 // only the root has children
275 for (final TreeItem item
: projectsList
.getTree().getItems()) {
276 item
.setChecked(true);
277 checkedItems
.add(item
.getData());
279 setPageComplete(true);
282 Dialog
.applyDialogFont(selectAll
);
283 setButtonLayoutData(selectAll
);
285 deselectAll
= new Button(buttonsComposite
, SWT
.PUSH
);
286 deselectAll
.setText(UIText
.WizardProjectsImportPage_deselectAll
);
287 deselectAll
.addSelectionListener(new SelectionAdapter() {
288 public void widgetSelected(SelectionEvent e
) {
289 checkedItems
.clear();
290 // only the root has children
291 for (final TreeItem item
: projectsList
.getTree().getItems()) {
292 item
.setChecked(false);
294 projectsList
.setInput(this); // filter away selected projects
295 setPageComplete(false);
298 Dialog
.applyDialogFont(deselectAll
);
299 setButtonLayoutData(deselectAll
);
304 * Create the area where you select the root directory for the projects.
309 private void createProjectsRoot(Composite workArea
) {
311 // project specification group
312 Composite projectGroup
= new Composite(workArea
, SWT
.NONE
);
313 GridLayout layout
= new GridLayout();
314 layout
.numColumns
= 3;
315 layout
.makeColumnsEqualWidth
= false;
316 layout
.marginWidth
= 0;
317 projectGroup
.setLayout(layout
);
318 projectGroup
.setLayoutData(new GridData(GridData
.FILL_HORIZONTAL
));
322 public void setVisible(boolean visible
) {
323 super.setVisible(visible
);
327 * Update the list of projects based on path. This will not check any
332 void setProjectsList(final String path
) {
333 // on an empty path empty selectedProjects
334 if (path
== null || path
.length() == 0) {
335 selectedProjects
= new ProjectRecord
[0];
336 projectsList
.refresh(true);
337 setPageComplete(checkedItems
.size() > 0);
339 setErrorMessage(UIText
.GitProjectsImportPage_NoProjectsMessage
);
343 final File directory
= new File(path
);
344 long modified
= directory
.lastModified();
345 if (path
.equals(lastPath
) && lastModified
== modified
) {
346 // since the file/folder was not modified and the path did not
347 // change, no refreshing is required
351 setErrorMessage(null);
354 lastModified
= modified
;
357 getContainer().run(true, true, new IRunnableWithProgress() {
359 public void run(IProgressMonitor monitor
) {
362 UIText
.WizardProjectsImportPage_SearchingMessage
,
364 selectedProjects
= new ProjectRecord
[0];
365 Collection
<File
> files
= new ArrayList
<File
>();
367 if (directory
.isDirectory()) {
369 if (!collectProjectFilesFromDirectory(files
, directory
,
373 Iterator
<File
> filesIterator
= files
.iterator();
374 selectedProjects
= new ProjectRecord
[files
.size()];
378 .subTask(UIText
.WizardProjectsImportPage_ProcessingMessage
);
379 while (filesIterator
.hasNext()) {
380 File file
= filesIterator
.next();
381 selectedProjects
[index
] = new ProjectRecord(file
);
382 checkedItems
.add(selectedProjects
[index
]);
387 setErrorMessage(UIText
.GitProjectsImportPage_NoProjectsMessage
);
395 } catch (InvocationTargetException e
) {
396 Activator
.logError(e
.getMessage(), e
);
397 } catch (InterruptedException e
) {
398 // Nothing to do if the user interrupts.
401 projectsList
.refresh(true);
402 if (getValidProjects().length
< selectedProjects
.length
) {
403 setMessage(UIText
.WizardProjectsImportPage_projectsInWorkspace
,
406 setMessage(UIText
.WizardProjectsImportPage_ImportProjectsDescription
);
408 enableSelectAllButtons();
409 setPageComplete(checkedItems
.size() > 0);
412 private void enableSelectAllButtons() {
413 if (projectsList
.getTree().getItemCount() > 0) {
414 selectAll
.setEnabled(true);
415 deselectAll
.setEnabled(true);
417 selectAll
.setEnabled(false);
418 deselectAll
.setEnabled(false);
423 * Collect the list of .project files that are under directory into files.
427 * @param directoriesVisited
428 * Set of canonical paths of directories, used as recursion guard
430 * The monitor to report to
431 * @return boolean <code>true</code> if the operation was completed.
433 private boolean collectProjectFilesFromDirectory(Collection
<File
> files
,
434 File directory
, Set
<String
> directoriesVisited
,
435 IProgressMonitor monitor
) {
437 if (monitor
.isCanceled()) {
440 monitor
.subTask(NLS
.bind(
441 UIText
.WizardProjectsImportPage_CheckingMessage
, directory
443 File
[] contents
= directory
.listFiles();
444 if (contents
== null)
447 // Initialize recursion guard for recursive symbolic links
448 if (directoriesVisited
== null) {
449 directoriesVisited
= new HashSet
<String
>();
451 directoriesVisited
.add(directory
.getCanonicalPath());
452 } catch (IOException exception
) {
453 StatusManager
.getManager().handle(
454 new Status(IStatus
.ERROR
, Activator
.getPluginId(),
455 exception
.getLocalizedMessage(), exception
));
459 // first look for project description files
460 final String dotProject
= IProjectDescription
.DESCRIPTION_FILE_NAME
;
461 for (int i
= 0; i
< contents
.length
; i
++) {
462 File file
= contents
[i
];
463 if (file
.isFile() && file
.getName().equals(dotProject
)) {
465 // don't search sub-directories since we can't have nested
470 // no project description found, so recurse into sub-directories
471 for (int i
= 0; i
< contents
.length
; i
++) {
472 if (contents
[i
].isDirectory()) {
473 if (!contents
[i
].getName().equals(METADATA_FOLDER
)) {
475 String canonicalPath
= contents
[i
].getCanonicalPath();
476 if (!directoriesVisited
.add(canonicalPath
)) {
477 // already been here --> do not recurse
480 } catch (IOException exception
) {
481 StatusManager
.getManager().handle(
482 new Status(IStatus
.ERROR
, Activator
483 .getPluginId(), exception
484 .getLocalizedMessage(), exception
));
487 collectProjectFilesFromDirectory(files
, contents
[i
],
488 directoriesVisited
, monitor
);
496 * Create the selected projects
498 * @return boolean <code>true</code> if all project creations were
501 boolean createProjects() {
502 final Object
[] selected
= checkedItems
.toArray();
503 WorkspaceModifyOperation op
= new WorkspaceModifyOperation() {
504 protected void execute(IProgressMonitor monitor
)
505 throws InvocationTargetException
, InterruptedException
{
507 monitor
.beginTask("", selected
.length
); //$NON-NLS-1$
508 if (monitor
.isCanceled()) {
509 throw new OperationCanceledException();
511 for (int i
= 0; i
< selected
.length
; i
++) {
512 createExistingProject((ProjectRecord
) selected
[i
],
513 new SubProgressMonitor(monitor
, 1));
520 // run the new project creation operation
522 getContainer().run(true, true, op
);
523 } catch (InterruptedException e
) {
525 } catch (InvocationTargetException e
) {
526 // one of the steps resulted in a core exception
527 Throwable t
= e
.getTargetException();
528 Activator
.handleError(UIText
.WizardProjectImportPage_errorMessage
,
536 * Create the project described in record. If it is successful return true.
540 * @return boolean <code>true</code> if successful
541 * @throws InvocationTargetException
542 * @throws InterruptedException
544 private boolean createExistingProject(final ProjectRecord record
,
545 IProgressMonitor monitor
) throws InvocationTargetException
,
546 InterruptedException
{
547 String projectName
= record
.getProjectName();
548 final IWorkspace workspace
= ResourcesPlugin
.getWorkspace();
549 final IProject project
= workspace
.getRoot().getProject(projectName
);
550 if (record
.description
== null) {
552 record
.description
= workspace
.newProjectDescription(projectName
);
553 IPath locationPath
= new Path(record
.projectSystemFile
556 // If it is under the root use the default location
557 if (Platform
.getLocation().isPrefixOf(locationPath
)) {
558 record
.description
.setLocation(null);
560 record
.description
.setLocation(locationPath
);
563 record
.description
.setName(projectName
);
568 UIText
.WizardProjectsImportPage_CreateProjectsTask
, 100);
569 project
.create(record
.description
, new SubProgressMonitor(monitor
,
571 project
.open(IResource
.BACKGROUND_REFRESH
, new SubProgressMonitor(
573 } catch (CoreException e
) {
574 throw new InvocationTargetException(e
);
583 * Method used for test suite.
585 * @return CheckboxTreeViewer the viewer containing all the projects found
587 public TreeViewer
getProjectsList() {
592 * Retrieve all the projects in the current workspace.
594 * @return IProject[] array of IProject in the current workspace
596 private IProject
[] getProjectsInWorkspace() {
597 if (wsProjects
== null) {
598 wsProjects
= ResourcesPlugin
.getWorkspace().getRoot().getProjects();
604 * Get the array of valid project records that can be imported from the
605 * source workspace or archive, selected by the user. If a project with the
606 * same name exists in both the source workspace and the current workspace,
607 * it will not appear in the list of projects to import and thus cannot be
608 * selected for import.
610 * Method declared public for test suite.
612 * @return ProjectRecord[] array of projects that can be imported into the
615 public ProjectRecord
[] getValidProjects() {
616 List
<ProjectRecord
> validProjects
= new ArrayList
<ProjectRecord
>();
617 for (int i
= 0; i
< selectedProjects
.length
; i
++) {
618 if (!isProjectInWorkspace(selectedProjects
[i
].getProjectName())) {
619 validProjects
.add(selectedProjects
[i
]);
622 return validProjects
.toArray(new ProjectRecord
[validProjects
.size()]);
626 * Determine if the project with the given name is in the current workspace.
629 * String the project name to check
630 * @return boolean true if the project with the given name is in this
633 private boolean isProjectInWorkspace(String projectName
) {
634 if (projectName
== null) {
637 IProject
[] workspaceProjects
= getProjectsInWorkspace();
638 for (int i
= 0; i
< workspaceProjects
.length
; i
++) {
639 if (projectName
.equals(workspaceProjects
[i
].getName())) {