Java tutorial
/******************************************************************************* * Copyright 2011 Google Inc. All Rights Reserved. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.google.gdt.eclipse.gph.egit.wizard; import com.google.gdt.eclipse.gph.egit.EGitCheckoutProviderPlugin; import com.google.gdt.eclipse.gph.wizards.AbstractWizardPage; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.PixelConverter; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.IWorkingSetManager; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.dialogs.IOverwriteQuery; import org.eclipse.ui.dialogs.WorkingSetGroup; import org.eclipse.ui.statushandlers.StatusManager; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * The WizardProjectsImportPage is the page that allows the user to import * projects from a particular location. */ public class ImportProjectsWizardPage extends AbstractWizardPage implements IOverwriteQuery { /** * Class declared public only for test suite. */ public class ProjectRecord { File projectSystemFile; String projectName; Object parent; int level; boolean hasConflicts; IProjectDescription description; /** * Create a record for a project based on the info in the file. * * @param file */ ProjectRecord(File file) { projectSystemFile = file; setProjectName(); } /** * @param file The Object representing the .project file * @param parent The parent folder of the .project file * @param level The number of levels deep in the provider the file is */ ProjectRecord(Object file, Object parent, int level) { this.parent = parent; this.level = level; setProjectName(); } /** * Gets the label to be used when rendering this project record in the UI. * * @return String the label * @since 3.4 */ public String getProjectLabel() { if (description == null) return projectName; String path = projectSystemFile.getParent(); return NLS.bind("{0} ({1})", projectName, path); } /** * Get the name of the project * * @return String */ public String getProjectName() { return projectName; } /** * @return Returns the hasConflicts. */ public boolean hasConflicts() { return hasConflicts; } /** * Returns whether the given project description file path is in the default * location for a project * * @param path The path to examine * @return Whether the given path is the default location for a project */ private boolean isDefaultLocation(IPath path) { // The project description file must at least be within the project, // which is within the workspace location if (path.segmentCount() < 2) return false; return path.removeLastSegments(2).toFile().equals(Platform.getLocation().toFile()); } /** * Set the name of the project based on the projectFile. */ private void setProjectName() { try { // If we don't have the project name try again if (projectName == null) { IPath path = new Path(projectSystemFile.getPath()); // if the file is in the default location, use the directory // name as the project name if (isDefaultLocation(path)) { projectName = path.segment(path.segmentCount() - 2); description = ResourcesPlugin.getWorkspace().newProjectDescription(projectName); } else { description = ResourcesPlugin.getWorkspace().loadProjectDescription(path); projectName = description.getName(); } } } catch (CoreException e) { // no good couldn't get the name } } } private final class ProjectLabelProvider extends LabelProvider implements IColorProvider { @Override public Color getBackground(Object element) { return null; } @Override public Color getForeground(Object element) { ProjectRecord projectRecord = (ProjectRecord) element; if (projectRecord.hasConflicts) return getShell().getDisplay().getSystemColor(SWT.COLOR_GRAY); return null; } @Override public String getText(Object element) { return ((ProjectRecord) element).getProjectLabel(); } } /** * The name of the folder containing metadata information for the workspace. */ public static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$ private EGitCheckoutWizard wizard; private CheckboxTreeViewer projectsList; private ProjectRecord[] selectedProjects = new ProjectRecord[0]; private IProject[] wsProjects; // The initial path to set //private String initialPath; // The last selected path to minimize searches private File lastPath; // The last time that the file or folder at the selected path was modified // to mimize searches private long lastModified; private WorkingSetGroup workingSetGroup; private IStructuredSelection currentSelection; List<IProject> createdProjects; /** * Create a new instance of the receiver. * * @param pageName */ public ImportProjectsWizardPage(EGitCheckoutWizard wizard) { super("checkoutProjectPage"); this.wizard = wizard; setTitle("Import Projects from Git"); setDescription("Select the projects to import"); setImageDescriptor(ImageDescriptor.createFromImage(wizard.getDefaultPageImage())); setPageComplete(false); } /** * Create the selected projects * * @return boolean <code>true</code> if all project creations were successful. */ public boolean createProjects() { final Object[] selected = projectsList.getCheckedElements(); createdProjects = new ArrayList<IProject>(); WorkspaceModifyOperation op = new WorkspaceModifyOperation() { @Override protected void execute(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { monitor.beginTask("", selected.length); //$NON-NLS-1$ if (monitor.isCanceled()) { throw new OperationCanceledException(); } for (int i = 0; i < selected.length; i++) { createExistingProject((ProjectRecord) selected[i], new SubProgressMonitor(monitor, 1)); } } finally { monitor.done(); } } }; // run the new project creation operation try { getContainer().run(true, true, op); } catch (InterruptedException e) { return false; } catch (InvocationTargetException e) { // one of the steps resulted in a core exception Throwable t = e.getTargetException(); String message = "Creation Problems"; IStatus status; if (t instanceof CoreException) { status = ((CoreException) t).getStatus(); } else { status = new Status(IStatus.ERROR, EGitCheckoutProviderPlugin.PLUGIN_ID, 1, message, t); } ErrorDialog.openError(getShell(), message, null, status); return false; } // Adds the projects to the working sets addToWorkingSets(); return true; } /** * Get the array of project records that can be imported from the source * workspace or archive, selected by the user. If a project with the same name * exists in both the source workspace and the current workspace, then the * hasConflicts flag would be set on that project record. Method declared * public for test suite. * * @return ProjectRecord[] array of projects that can be imported into the * workspace */ public ProjectRecord[] getProjectRecords() { List<ProjectRecord> projectRecords = new ArrayList<ProjectRecord>(); for (int i = 0; i < selectedProjects.length; i++) { if (isProjectInWorkspace(selectedProjects[i].getProjectName())) { selectedProjects[i].hasConflicts = true; } projectRecords.add(selectedProjects[i]); } return projectRecords.toArray(new ProjectRecord[projectRecords.size()]); } /** * Method used for test suite. * * @return CheckboxTreeViewer the viewer containing all the projects found */ public CheckboxTreeViewer getProjectsList() { return projectsList; } /** * Performs clean-up if the user cancels the wizard without doing anything */ public void performCancel() { } /** * The <code>WizardDataTransfer</code> implementation of this * <code>IOverwriteQuery</code> method asks the user whether the existing * resource at the given path should be overwritten. * * @param pathString * @return the user's reply: one of <code>"YES"</code>, <code>"NO"</code>, * <code>"ALL"</code>, or <code>"CANCEL"</code> */ @Override public String queryOverwrite(String pathString) { Path path = new Path(pathString); String messageString; // Break the message up if there is a file name and a directory // and there are at least 2 segments. if (path.getFileExtension() == null || path.segmentCount() < 2) { messageString = NLS.bind("''{0}'' already exists. Would you like to overwrite it?", pathString); } else { messageString = NLS.bind("Overwrite ''{0}'' in folder ''{1}''?", path.lastSegment(), path.removeLastSegments(1).toOSString()); } final MessageDialog dialog = new MessageDialog(getContainer().getShell(), "Question", null, messageString, MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.YES_TO_ALL_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.NO_TO_ALL_LABEL, IDialogConstants.CANCEL_LABEL }, 0) { @Override protected int getShellStyle() { return super.getShellStyle() | SWT.SHEET; } }; String[] response = new String[] { YES, ALL, NO, NO_ALL, CANCEL }; // run in syncExec because callback is from an operation, // which is probably not running in the UI thread. getControl().getDisplay().syncExec(new Runnable() { @Override public void run() { dialog.open(); } }); return dialog.getReturnCode() < 0 ? CANCEL : response[dialog.getReturnCode()]; } /** * Update the list of projects based on path. Method declared public only for * test suite. * * @param path */ public void updateProjectsList(final File directory) { // on an empty path empty selectedProjects if (directory == null || !directory.exists()) { setMessage("Invalid directory for the repository clone."); selectedProjects = new ProjectRecord[0]; projectsList.refresh(true); projectsList.setCheckedElements(selectedProjects); setPageComplete(projectsList.getCheckedElements().length > 0); lastPath = directory; return; } long modified = directory.lastModified(); if (directory.equals(lastPath) && lastModified == modified) { // since the file/folder was not modified and the path did not // change, no refreshing is required return; } lastPath = directory; lastModified = modified; try { getContainer().run(true, true, new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) { monitor.beginTask("Searching for projects", 100); selectedProjects = new ProjectRecord[0]; Collection<File> files = new ArrayList<File>(); monitor.worked(10); if (directory.isDirectory()) { if (!collectProjectFilesFromDirectory(files, directory, null, monitor)) { return; } Iterator<File> filesIterator = files.iterator(); selectedProjects = new ProjectRecord[files.size()]; int index = 0; monitor.worked(50); monitor.subTask("Processing results"); while (filesIterator.hasNext()) { File file = filesIterator.next(); selectedProjects[index] = new ProjectRecord(file); index++; } } else { monitor.worked(60); } monitor.done(); } }); } catch (InvocationTargetException e) { EGitCheckoutProviderPlugin.logError(e.getMessage(), e); } catch (InterruptedException e) { // Nothing to do if the user interrupts. } projectsList.refresh(true); ProjectRecord[] projects = getProjectRecords(); boolean displayWarning = false; for (int i = 0; i < projects.length; i++) { if (projects[i].hasConflicts) { displayWarning = true; projectsList.setGrayed(projects[i], true); } else { projectsList.setChecked(projects[i], true); } } if (displayWarning) { setMessage("Some projects cannot be imported because they already exist in the workspace", WARNING); } else { setMessage("Select a directory to search for existing Eclipse projects."); } setPageComplete(projectsList.getCheckedElements().length > 0); if (selectedProjects.length == 0) { setMessage("No projects are found to import", WARNING); } } @Override protected Control createPageContents(Composite parent) { initializeDialogUnits(parent); Composite workArea = new Composite(parent, SWT.NONE); workArea.setLayout(new GridLayout()); workArea.setLayoutData( new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL)); createProjectsList(workArea); createWorkingSetGroup(workArea); Dialog.applyDialogFont(workArea); return workArea; } /** * Display an error dialog with the specified message. * * @param message the error message */ protected void displayErrorDialog(String message) { MessageDialog.open(MessageDialog.ERROR, getContainer().getShell(), getErrorDialogTitle(), message, SWT.SHEET); } @Override protected void enteringPage() { File cloneDir = wizard.getRepoCloneDirectory(); updateProjectsList(cloneDir); } /** * Get the title for an error dialog. Subclasses should override. */ protected String getErrorDialogTitle() { return "Internal error"; } private void addToWorkingSets() { IWorkingSet[] selectedWorkingSets = workingSetGroup.getSelectedWorkingSets(); if (selectedWorkingSets == null || selectedWorkingSets.length == 0) { return; // no Working set is selected } IWorkingSetManager workingSetManager = PlatformUI.getWorkbench().getWorkingSetManager(); for (Iterator<IProject> i = createdProjects.iterator(); i.hasNext();) { IProject project = i.next(); workingSetManager.addToWorkingSets(project, selectedWorkingSets); } } /** * Collect the list of .project files that are under directory into files. * * @param files * @param directory * @param directoriesVisited Set of canonical paths of directories, used as * recursion guard * @param monitor The monitor to report to * @return boolean <code>true</code> if the operation was completed. */ private boolean collectProjectFilesFromDirectory(Collection<File> files, File directory, Set<String> directoriesVisited, IProgressMonitor monitor) { if (monitor.isCanceled()) { return false; } monitor.subTask(NLS.bind("Checking: {0}", directory.getPath())); File[] contents = directory.listFiles(); if (contents == null) return false; // Initialize recursion guard for recursive symbolic links if (directoriesVisited == null) { directoriesVisited = new HashSet<String>(); try { directoriesVisited.add(directory.getCanonicalPath()); } catch (IOException exception) { StatusManager.getManager().handle(EGitCheckoutProviderPlugin.createStatus(IStatus.ERROR, exception.getLocalizedMessage(), exception)); } } // first look for project description files final String dotProject = IProjectDescription.DESCRIPTION_FILE_NAME; for (int i = 0; i < contents.length; i++) { File file = contents[i]; if (file.isFile() && file.getName().equals(dotProject)) { files.add(file); // don't search sub-directories since we can't have nested // projects return true; } } // no project description found, so recurse into sub-directories for (int i = 0; i < contents.length; i++) { if (contents[i].isDirectory()) { if (!contents[i].getName().equals(METADATA_FOLDER)) { try { String canonicalPath = contents[i].getCanonicalPath(); if (!directoriesVisited.add(canonicalPath)) { // already been here --> do not recurse continue; } } catch (IOException exception) { StatusManager.getManager().handle(EGitCheckoutProviderPlugin.createStatus(IStatus.ERROR, exception.getLocalizedMessage(), exception)); } collectProjectFilesFromDirectory(files, contents[i], directoriesVisited, monitor); } } } return true; } /** * Create the project described in record. If it is successful return true. * * @param record * @return boolean <code>true</code> if successful * @throws InterruptedException */ private boolean createExistingProject(final ProjectRecord record, IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { String projectName = record.getProjectName(); final IWorkspace workspace = ResourcesPlugin.getWorkspace(); final IProject project = workspace.getRoot().getProject(projectName); createdProjects.add(project); if (record.description == null) { // error case record.description = workspace.newProjectDescription(projectName); IPath locationPath = new Path(record.projectSystemFile.getAbsolutePath()); // If it is under the root use the default location if (Platform.getLocation().isPrefixOf(locationPath)) { record.description.setLocation(null); } else { record.description.setLocation(locationPath); } } else { record.description.setName(projectName); } // import from file system // File importSource = null; // if (copyFiles) { // // import project from location copying files - use default project // // location for this workspace // URI locationURI = record.description.getLocationURI(); // // if location is null, project already exists in this location or // // some error condition occured. // if (locationURI != null) { // importSource = new File(locationURI); // IProjectDescription desc = workspace // .newProjectDescription(projectName); // desc.setBuildSpec(record.description.getBuildSpec()); // desc.setComment(record.description.getComment()); // desc.setDynamicReferences(record.description // .getDynamicReferences()); // desc.setNatureIds(record.description.getNatureIds()); // desc.setReferencedProjects(record.description // .getReferencedProjects()); // record.description = desc; // } // } try { monitor.beginTask("Creating Projects", 100); project.create(record.description, new SubProgressMonitor(monitor, 30)); project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(monitor, 70)); } catch (CoreException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } // import operation to import project files if copy checkbox is selected // if (copyFiles && importSource != null) { // List filesToImport = FileSystemStructureProvider.INSTANCE // .getChildren(importSource); // ImportOperation operation = new ImportOperation(project // .getFullPath(), importSource, // FileSystemStructureProvider.INSTANCE, this, filesToImport); // operation.setContext(getShell()); // operation.setOverwriteResources(true); // need to overwrite // // .project, .classpath // // files // operation.setCreateContainerStructure(false); // operation.run(monitor); // } return true; } /** * Create the checkbox list for the found projects. * * @param workArea */ private void createProjectsList(Composite workArea) { Group listComposite = new Group(workArea, SWT.NONE); listComposite.setText("Projects to import"); GridLayout layout = new GridLayout(); layout.numColumns = 2; //layout.marginWidth = 0; layout.makeColumnsEqualWidth = false; layout.marginHeight = 10; layout.marginWidth = 10; listComposite.setLayout(layout); listComposite.setLayoutData( new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH)); projectsList = new CheckboxTreeViewer(listComposite, SWT.BORDER); GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); gridData.widthHint = new PixelConverter(projectsList.getControl()).convertWidthInCharsToPixels(25); gridData.heightHint = new PixelConverter(projectsList.getControl()).convertHeightInCharsToPixels(10); projectsList.getControl().setLayoutData(gridData); projectsList.setContentProvider(new ITreeContentProvider() { @Override public void dispose() { } @Override public Object[] getChildren(Object parentElement) { return null; } @Override public Object[] getElements(Object inputElement) { return getProjectRecords(); } @Override public Object getParent(Object element) { return null; } @Override public boolean hasChildren(Object element) { return false; } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } }); projectsList.setLabelProvider(new ProjectLabelProvider()); projectsList.addCheckStateListener(new ICheckStateListener() { @Override public void checkStateChanged(CheckStateChangedEvent event) { ProjectRecord element = (ProjectRecord) event.getElement(); if (element.hasConflicts) { projectsList.setChecked(element, false); } setPageComplete(projectsList.getCheckedElements().length > 0); } }); projectsList.setInput(this); projectsList.setComparator(new ViewerComparator()); createSelectionButtons(listComposite); } /** * Create the selection buttons in the listComposite. * * @param listComposite */ private void createSelectionButtons(Composite listComposite) { Composite buttonsComposite = new Composite(listComposite, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; buttonsComposite.setLayout(layout); buttonsComposite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); Button selectAll = new Button(buttonsComposite, SWT.PUSH); selectAll.setText("&Select All"); selectAll.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { for (int i = 0; i < selectedProjects.length; i++) { if (selectedProjects[i].hasConflicts) projectsList.setChecked(selectedProjects[i], false); else projectsList.setChecked(selectedProjects[i], true); } setPageComplete(projectsList.getCheckedElements().length > 0); } }); Dialog.applyDialogFont(selectAll); setButtonLayoutData(selectAll); Button deselectAll = new Button(buttonsComposite, SWT.PUSH); deselectAll.setText("&Deselect All"); deselectAll.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { projectsList.setCheckedElements(new Object[0]); setPageComplete(false); } }); Dialog.applyDialogFont(deselectAll); setButtonLayoutData(deselectAll); } /** * @param workArea */ private void createWorkingSetGroup(Composite workArea) { String[] workingSetIds = new String[] { "org.eclipse.ui.resourceWorkingSetPage", //$NON-NLS-1$ "org.eclipse.jdt.ui.JavaWorkingSetPage" }; //$NON-NLS-1$ workingSetGroup = new WorkingSetGroup(workArea, currentSelection, workingSetIds); } /** * Retrieve all the projects in the current workspace. * * @return IProject[] array of IProject in the current workspace */ private IProject[] getProjectsInWorkspace() { if (wsProjects == null) { wsProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); } return wsProjects; } /** * Determine if the project with the given name is in the current workspace. * * @param projectName String the project name to check * @return boolean true if the project with the given name is in this * workspace */ private boolean isProjectInWorkspace(String projectName) { if (projectName == null) { return false; } IProject[] workspaceProjects = getProjectsInWorkspace(); for (int i = 0; i < workspaceProjects.length; i++) { if (projectName.equals(workspaceProjects[i].getName())) { return true; } } return false; } }