Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Zarna2010-09-24 10:40:01 +0000
committerTomasz Zarna2010-09-24 10:40:01 +0000
commit02049cb17a1af7133310c1e96707d6e7db3487a2 (patch)
treedebb6a09a797b9b882397de4f27dea9ed5a66e50
parentc01cd79478f2adc6ecf6208f556bf174ba3ffcb7 (diff)
downloadeclipse.platform.team-02049cb17a1af7133310c1e96707d6e7db3487a2.tar.gz
eclipse.platform.team-02049cb17a1af7133310c1e96707d6e7db3487a2.tar.xz
eclipse.platform.team-02049cb17a1af7133310c1e96707d6e7db3487a2.zip
bug 162608: [Project Sets] Be able to import a .psf file directly from a URL
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java53
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java56
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ProjectSetImporter.java87
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamUIMessages.java6
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties6
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ExportProjectSetLocationPage.java6
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetMainPage.java190
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetOperation.java57
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetExportWizard.java2
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetImportWizard.java21
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfFilenameStore.java99
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfStore.java95
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfUrlStore.java47
13 files changed, 565 insertions, 160 deletions
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
index 7a582c1f0..ec9c67605 100644
--- a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
@@ -18,6 +18,8 @@ import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.net.SocketTimeoutException;
+import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
@@ -36,6 +38,7 @@ import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.SharedDocumentAdapter;
import org.eclipse.compare.contentmergeviewer.IDocumentRange;
import org.eclipse.compare.internal.core.patch.HunkResult;
+import org.eclipse.compare.internal.patch.PatchMessages;
import org.eclipse.compare.patch.IHunk;
import org.eclipse.compare.structuremergeviewer.DiffNode;
import org.eclipse.compare.structuremergeviewer.ICompareInput;
@@ -64,6 +67,8 @@ import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.util.IPropertyChangeListener;
@@ -861,4 +866,52 @@ public class Utilities {
}
return false;
}
+
+ /**
+ * Load content of file under <code>url</code> displaying progress on given
+ * context.
+ *
+ * @param url
+ * @param context
+ * @return the content of file under given URL, or <code>null</code> if URL
+ * could not be loaded
+ * @throws InvocationTargetException
+ * thrown on errors while URL loading
+ * @throws OperationCanceledException
+ * @throws InterruptedException
+ */
+ public static String getURLContents(final URL url, IRunnableContext context)
+ throws InvocationTargetException, OperationCanceledException,
+ InterruptedException {
+ final String[] result = new String[1];
+ context.run(true, true, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ SubMonitor progress = SubMonitor.convert(monitor,
+ PatchMessages.InputPatchPage_URLConnecting, 100);
+ try {
+ URLConnection connection = url.openConnection();
+ progress.worked(10);
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ setReadTimeout(connection, 60 * 1000);
+ progress.setTaskName(PatchMessages.InputPatchPage_URLFetchingContent);
+ String enc = connection.getContentEncoding();
+ if (enc == null)
+ enc = ResourcesPlugin.getEncoding();
+ result[0] = Utilities.readString(
+ connection.getInputStream(), enc,
+ connection.getContentLength(),
+ progress.newChild(90));
+ } catch (SocketTimeoutException e) {
+ throw new InvocationTargetException(e);
+ } catch (IOException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+ }
+ });
+ return result[0];
+ }
}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java
index f4609119f..7248fccb3 100644
--- a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java
@@ -21,9 +21,7 @@ import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
-import java.net.SocketTimeoutException;
import java.net.URL;
-import java.net.URLConnection;
import org.eclipse.compare.internal.ICompareContextIds;
import org.eclipse.compare.internal.Utilities;
@@ -34,15 +32,12 @@ import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
-import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
@@ -264,9 +259,17 @@ public class InputPatchPage extends WizardPage {
} else if (inputMethod==URL) {
String patchFileURL = fPatchURLField.getText();
if (patchFileURL != null) {
- String contents = getURLContents(patchFileURL);
- if (contents != null)
- reader = new StringReader(contents);
+ try {
+ String contents = Utilities.getURLContents(new URL(
+ patchFileURL), getContainer());
+ if (contents != null)
+ reader = new StringReader(contents);
+ } catch (MalformedURLException e) {
+ // ignore as we tested it with modify listener on combo
+ } catch (InvocationTargetException e) { // ignore
+ } catch (OperationCanceledException e) { // ignore
+ } catch (InterruptedException e) { // ignore
+ }
}
fPatchSource= PatchMessages.InputPatchPage_URL_title;
} else if (inputMethod==WORKSPACE) {
@@ -307,43 +310,6 @@ public class InputPatchPage extends WizardPage {
}
}
}
-
- private String getURLContents(String patchFileURL) {
- final URL url;
- try {
- url = new URL(patchFileURL);
- final String[] result= new String[1];
- try {
- getContainer().run(true, true, new IRunnableWithProgress() {
- public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
- SubMonitor progress = SubMonitor.convert(monitor, PatchMessages.InputPatchPage_URLConnecting, 100);
- try {
- URLConnection connection = url.openConnection();
- progress.worked(10);
- if (monitor.isCanceled())
- throw new OperationCanceledException();
- Utilities.setReadTimeout(connection, 60*1000);
- progress.setTaskName(PatchMessages.InputPatchPage_URLFetchingContent);
- String enc = connection.getContentEncoding();
- if (enc == null)
- enc = ResourcesPlugin.getEncoding();
- result[0] = Utilities.readString(connection.getInputStream(), enc, connection.getContentLength(), progress.newChild(90));
- } catch (SocketTimeoutException e) { // timeout
- } catch (IOException e) { //ignore
- }
- monitor.done();
- }
- });
- return result[0];
- } catch (OperationCanceledException e) { //ignore
- } catch (InvocationTargetException e) { //ignore
- } catch (InterruptedException e) { //ignore
- }
- } catch (MalformedURLException e) {
- // ignore as we tested it with modify listener on combo
- }
- return null;
- }
/* (non-JavaDoc)
* Method declared in IWizardPage.
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ProjectSetImporter.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ProjectSetImporter.java
index 577fc0a05..e3a386cd7 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ProjectSetImporter.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ProjectSetImporter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
* 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
@@ -30,10 +30,47 @@ import org.eclipse.team.internal.core.TeamPlugin;
import org.eclipse.ui.*;
public class ProjectSetImporter {
-
- public static IProject[] importProjectSet(String filename, Shell shell, IProgressMonitor monitor) throws InvocationTargetException {
+
+ /**
+ * Imports a psf file based on a file content. This may be used when psf
+ * file is imported from any other location that local filesystem.
+ *
+ * @param psfContents
+ * the content of the psf file.
+ * @param filename
+ * the name of the source file. This is included in case the
+ * provider needs to deduce relative paths
+ * @param shell
+ * @param monitor
+ * @return list of new projects
+ * @throws InvocationTargetException
+ */
+ public static IProject[] importProjectSetFromString(String psfContents,
+ String filename, Shell shell, IProgressMonitor monitor)
+ throws InvocationTargetException {
+ XMLMemento xmlMemento = stringToXMLMemento(psfContents);
+ return importProjectSet(xmlMemento, filename, shell, monitor);
+ }
+
+ /**
+ * Imports a psf file.
+ *
+ * @param filename
+ * @param shell
+ * @param monitor
+ * @return list of new projects
+ * @throws InvocationTargetException
+ */
+ public static IProject[] importProjectSet(String filename, Shell shell,
+ IProgressMonitor monitor) throws InvocationTargetException {
+ XMLMemento xmlMemento = filenameToXMLMemento(filename);
+ return importProjectSet(xmlMemento, filename, shell, monitor);
+ }
+
+ private static IProject[] importProjectSet(XMLMemento xmlMemento,
+ String filename, Shell shell, IProgressMonitor monitor)
+ throws InvocationTargetException {
try {
- XMLMemento xmlMemento = filenameToXMLMemento(filename);
String version = xmlMemento.getString("version"); //$NON-NLS-1$
List newProjects = new ArrayList();
@@ -179,7 +216,28 @@ public class ProjectSetImporter {
}
}
}
-
+
+ private static XMLMemento stringToXMLMemento(String stringContents)
+ throws InvocationTargetException {
+ StringReader reader = null;
+ try {
+ reader = new StringReader(stringContents);
+ return XMLMemento.createReadRoot(reader);
+ } catch (WorkbenchException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+ }
+
+ /**
+ * Check if given file is a valid psf file
+ *
+ * @param filename
+ * @return <code>true</code> is file is a valid psf file
+ */
public static boolean isValidProjectSetFile(String filename) {
try {
return filenameToXMLMemento(filename).getString("version") != null; //$NON-NLS-1$
@@ -187,7 +245,24 @@ public class ProjectSetImporter {
return false;
}
}
-
+
+ /**
+ * Check if given string is a valid project set
+ *
+ * @param psfContent
+ * @return <code>true</code> if psfContent is a valid project set
+ */
+ public static boolean isValidProjectSetString(String psfContent) {
+ if (psfContent == null) {
+ return false;
+ }
+ try {
+ return stringToXMLMemento(psfContent).getString("version") != null; //$NON-NLS-1$
+ } catch (InvocationTargetException e) {
+ return false;
+ }
+ }
+
private static void mergeWorkingSets(IWorkingSet newWs, IWorkingSet oldWs) {
IAdaptable[] oldElements = oldWs.getElements();
IAdaptable[] newElements = newWs.getElements();
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamUIMessages.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamUIMessages.java
index 3bc77e61a..1fc381794 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamUIMessages.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamUIMessages.java
@@ -105,6 +105,8 @@ public class TeamUIMessages extends NLS {
public static String HistoryPageCompareEditorInput_0;
public static String ImportProjectSetMainPage_AddToWorkingSet;
+ public static String ImportProjectSetMainPage_Project_Set_File;
+ public static String ImportProjectSetMainPage_Project_Set_Url;
public static String ImportProjectSetMainPage_Browse;
@@ -114,6 +116,7 @@ public class TeamUIMessages extends NLS {
public static String ImportProjectSetDialog_duplicatedWorkingSet_merge;
public static String ImportProjectSetDialog_duplicatedWorkingSet_skip;
public static String ImportProjectSetDialog_duplicatedWorkingSet_applyToAll;
+ public static String ImportProjectSetDialog_malformed_url;
public static String information;
@@ -310,8 +313,8 @@ public class TeamUIMessages extends NLS {
public static String ExportProjectSetMainPage_Initial_description;
public static String ExportProjectSetMainPage_specifyFile;
- public static String ImportProjectSetMainPage_Project_Set_File_Name__2;
public static String ImportProjectSetMainPage_Browse_3;
+ public static String ImportProjectSetMainPage_The_given_URL_cannot_be_loaded;
public static String ImportProjectSetMainPage_The_specified_file_does_not_exist_4;
public static String ImportProjectSetMainPage_You_have_specified_a_folder_5;
public static String ImportProjectSetMainPage_workingSetNameEmpty;
@@ -320,6 +323,7 @@ public class TeamUIMessages extends NLS {
public static String ImportProjectSetMainPage_runInBackground;
public static String ImportProjectSetMainPage_jobName;
public static String ImportProjectSetMainPage_specifyFile;
+ public static String ImportProjectSetMainPage_specifyURL;
public static String ImportProjectSetMainPage_selectWorkingSet;
public static String ImportProjectSetMainPage_projectSetFileInvalid;
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties
index 5e70c8bc2..2d152d910 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties
@@ -104,9 +104,11 @@ ExportProjectSetMainPage_ExportWorkingSets=E&xport working sets
ExportProjectSetMainPage_Initial_description=Define which projects or working sets should be exported into the team project file.
ExportProjectSetMainPage_specifyFile=Please specify the destination file.
-ImportProjectSetMainPage_Project_Set_File_Name__2=F&ile name:
+ImportProjectSetMainPage_Project_Set_File=File
+ImportProjectSetMainPage_Project_Set_Url=URL
ImportProjectSetMainPage_Browse_3=B&rowse...
ImportProjectSetMainPage_Browse=Br&owse...
+ImportProjectSetMainPage_The_given_URL_cannot_be_loaded=File from given URL cannot be loaded
ImportProjectSetMainPage_The_specified_file_does_not_exist_4=The specified file does not exist
ImportProjectSetMainPage_You_have_specified_a_folder_5=You have specified a folder
ImportProjectSetMainPage_workingSetNameEmpty=The working set name must not be empty
@@ -116,6 +118,7 @@ ImportProjectSetMainPage_AddToWorkingSet=&Add the imported team project set to a
ImportProjectSetMainPage_runInBackground=Run the import in the bac&kground
ImportProjectSetMainPage_jobName=Importing project set...
ImportProjectSetMainPage_specifyFile=Please specify a file to import.
+ImportProjectSetMainPage_specifyURL=Please specify an URL to import.
ImportProjectSetMainPage_selectWorkingSet=Select a working set.
ImportProjectSetMainPage_projectSetFileInvalid=The specified file is not a valid Team Project Set file.
ImportProjectSetDialog_duplicatedWorkingSet_title=Working Set Exists
@@ -124,6 +127,7 @@ ImportProjectSetDialog_duplicatedWorkingSet_replace=&Replace
ImportProjectSetDialog_duplicatedWorkingSet_merge=&Merge
ImportProjectSetDialog_duplicatedWorkingSet_skip=&Skip
ImportProjectSetDialog_duplicatedWorkingSet_applyToAll=&Apply to all working sets in this import
+ImportProjectSetDialog_malformed_url=Malformed URL
ProjectSetContentHandler_Element_provider_must_be_contained_in_element_psf_4=Element provider must be contained in element psf
ProjectSetContentHandler_Element_project_must_be_contained_in_element_provider_7=Element project must be contained in element provider
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ExportProjectSetLocationPage.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ExportProjectSetLocationPage.java
index 30774ae96..65916c580 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ExportProjectSetLocationPage.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ExportProjectSetLocationPage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
* 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
@@ -91,8 +91,8 @@ public class ExportProjectSetLocationPage extends TeamWizardPage {
inner.setLayoutData(data);
fileCombo = createDropDownCombo(inner);
- file = PsfFilenameStore.getSuggestedDefault();
- fileCombo.setItems(PsfFilenameStore.getHistory());
+ file = PsfFilenameStore.getInstance().getSuggestedDefault();
+ fileCombo.setItems(PsfFilenameStore.getInstance().getHistory());
fileCombo.setText(file);
fileCombo.addListener(SWT.Modify, new Listener() {
public void handleEvent(Event event) {
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetMainPage.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetMainPage.java
index 92fdfad3d..bd8ecbf11 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetMainPage.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetMainPage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
* 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
@@ -11,12 +11,19 @@
package org.eclipse.team.internal.ui.wizards;
import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.eclipse.compare.internal.Utilities;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
@@ -32,16 +39,51 @@ public class ImportProjectSetMainPage extends TeamWizardPage {
String file = ""; //$NON-NLS-1$
Button browseButton;
+ String urlString = ""; //$NON-NLS-1$
+ Combo urlCombo;
+
+ // input type radios
+ private Button fileInputButton;
+ private Button urlInputButton;
+
+ // input type
+ public static final int InputType_file = 0;
+ public static final int InputType_URL = 1;
+ private int inputType = InputType_file;
+
private boolean runInBackground = isRunInBackgroundPreferenceOn();
// a wizard shouldn't be in an error state until the state has been modified by the user
private int messageType = NONE;
private WorkingSetGroup workingSetGroup;
- public ImportProjectSetMainPage(String pageName, String title, ImageDescriptor titleImage) {
+ private PsfFilenameStore psfFilenameStore = PsfFilenameStore.getInstance();
+ private PsfUrlStore psfUrlStore = PsfUrlStore.getInstance();
+
+ public ImportProjectSetMainPage(String pageName, String title,
+ ImageDescriptor titleImage) {
super(pageName, title, titleImage);
- setDescription(TeamUIMessages.ImportProjectSetMainPage_description);
+ setDescription(TeamUIMessages.ImportProjectSetMainPage_description);
}
-
+
+ private void setInputType(int inputTypeSelected) {
+ this.inputType = inputTypeSelected;
+ // reset the message type and give the user fresh chance to input
+ // correct data
+ messageType = NONE;
+ // update controls
+ fileInputButton.setSelection(inputType == InputType_file);
+ fileCombo.setEnabled(inputType == InputType_file);
+ browseButton.setEnabled(inputType == InputType_file);
+ urlInputButton.setSelection(inputType == InputType_URL);
+ urlCombo.setEnabled(inputType == InputType_URL);
+ // validate field
+ if (inputType == InputType_file)
+ updateFileEnablement();
+ if (inputType == InputType_URL)
+ updateUrlEnablement();
+
+ }
+
/*
* @see IDialogPage#createControl(Composite)
*/
@@ -60,21 +102,52 @@ public class ImportProjectSetMainPage extends TeamWizardPage {
layout.marginWidth = 0;
inner.setLayout(layout);
- createLabel(inner, TeamUIMessages.ImportProjectSetMainPage_Project_Set_File_Name__2);
+ fileInputButton = new Button(inner, SWT.RADIO);
+ fileInputButton
+ .setText(TeamUIMessages.ImportProjectSetMainPage_Project_Set_File);
+ fileInputButton.setEnabled(true);
+ fileInputButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ setInputType(InputType_file);
+ }
+ });
fileCombo = createDropDownCombo(inner);
- file = PsfFilenameStore.getSuggestedDefault();
- fileCombo.setItems(PsfFilenameStore.getHistory());
+ file = psfFilenameStore.getSuggestedDefault();
+ fileCombo.setItems(psfFilenameStore.getHistory());
fileCombo.setText(file);
fileCombo.addListener(SWT.Modify, new Listener() {
public void handleEvent(Event event) {
file = fileCombo.getText();
- updateEnablement();
+ updateFileEnablement();
}
});
browseButton = new Button(inner, SWT.PUSH);
browseButton.setText(TeamUIMessages.ImportProjectSetMainPage_Browse_3);
+
+ urlInputButton = new Button(inner, SWT.RADIO);
+ urlInputButton
+ .setText(TeamUIMessages.ImportProjectSetMainPage_Project_Set_Url);
+ urlInputButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ setInputType(InputType_URL);
+ }
+ });
+ urlCombo = createDropDownCombo(inner);
+ urlString = psfUrlStore.getSuggestedDefault();
+ urlCombo.setItems(psfUrlStore.getHistory());
+ urlCombo.setText(urlString);
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 2;
+ urlCombo.setLayoutData(gd);
+ urlCombo.addListener(SWT.Modify, new Listener() {
+ public void handleEvent(Event event) {
+ urlString = urlCombo.getText();
+ updateUrlEnablement();
+ }
+ });
+
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
@@ -115,10 +188,31 @@ public class ImportProjectSetMainPage extends TeamWizardPage {
});
setControl(composite);
- updateEnablement();
+ setDefaultInputType();
Dialog.applyDialogFont(parent);
- // future messages will be of type error
- messageType = ERROR;
+ }
+
+ private void setDefaultInputType() {
+ // check for clipboard contents
+ Control c = getControl();
+ if (c != null) {
+ Clipboard clipboard = new Clipboard(c.getDisplay());
+ Object o = clipboard.getContents(TextTransfer.getInstance());
+ clipboard.dispose();
+ if (o instanceof String) {
+ try {
+ URL url = new URL((String) o);
+ if (url != null) {
+ setInputType(InputType_URL);
+ urlCombo.setText((String) o);
+ return;
+ }
+ } catch (MalformedURLException e) {
+ // ignore, it's not and URL
+ }
+ }
+ }
+ setInputType(InputType_file);
}
private void addWorkingSetSection(Composite composite) {
@@ -129,9 +223,41 @@ public class ImportProjectSetMainPage extends TeamWizardPage {
"org.eclipse.jdt.ui.JavaWorkingSetPage" /* JavaWorkingSetUpdater.ID */}); //$NON-NLS-1$
}
- private void updateEnablement() {
+ private void updateUrlEnablement() {
boolean complete = false;
setMessage(null);
+ setErrorMessage(null);
+
+ if (urlString.length() == 0) {
+ setMessage(TeamUIMessages.ImportProjectSetMainPage_specifyURL,
+ messageType);
+ complete = false;
+ } else {
+
+ try {
+ new URL(urlString);
+ // the URL is correct, we can clear the error message
+ complete = true;
+ } catch (MalformedURLException e) {
+ messageType = ERROR;
+ setMessage(TeamUIMessages.ImportProjectSetDialog_malformed_url,
+ messageType);
+ complete = false;
+ }
+ }
+
+ if (complete) {
+ setErrorMessage(null);
+ setDescription(TeamUIMessages.ImportProjectSetMainPage_description);
+ }
+
+ setPageComplete(complete);
+ }
+
+ private void updateFileEnablement() {
+ boolean complete = false;
+ setMessage(null);
+ setErrorMessage(null);
if (file.length() == 0) {
setMessage(TeamUIMessages.ImportProjectSetMainPage_specifyFile, messageType);
@@ -141,14 +267,17 @@ public class ImportProjectSetMainPage extends TeamWizardPage {
// See if the file exists
File f = new File(file);
if (!f.exists()) {
+ messageType = ERROR;
setMessage(TeamUIMessages.ImportProjectSetMainPage_The_specified_file_does_not_exist_4, messageType);
setPageComplete(false);
return;
} else if (f.isDirectory()) {
+ messageType = ERROR;
setMessage(TeamUIMessages.ImportProjectSetMainPage_You_have_specified_a_folder_5, messageType);
setPageComplete(false);
return;
} else if (!ProjectSetImporter.isValidProjectSetFile(file)) {
+ messageType = ERROR;
setMessage(TeamUIMessages.ImportProjectSetMainPage_projectSetFileInvalid, messageType);
setPageComplete(false);
return;
@@ -168,6 +297,10 @@ public class ImportProjectSetMainPage extends TeamWizardPage {
return file;
}
+ public String getUrl() {
+ return urlString;
+ }
+
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible) {
@@ -193,4 +326,37 @@ public class ImportProjectSetMainPage extends TeamWizardPage {
public boolean isRunInBackgroundOn() {
return runInBackground;
}
+
+ public int getInputType() {
+ return inputType;
+ }
+
+ public String getURLContents() {
+ try {
+ PsfUrlStore.getInstance().remember(urlString);
+ String urlContent = Utilities.getURLContents(new URL(urlString),
+ getContainer());
+ if (ProjectSetImporter.isValidProjectSetString(urlContent)) {
+ return urlContent;
+ } else {
+ messageType = ERROR;
+ setMessage(
+ TeamUIMessages.ImportProjectSetMainPage_projectSetFileInvalid,
+ messageType);
+ setPageComplete(false);
+ return null;
+ }
+ } catch (OperationCanceledException e) { // ignore
+ } catch (InterruptedException e) { // ignore
+ } catch (InvocationTargetException e) {
+ messageType = ERROR;
+ setMessage(
+ TeamUIMessages.ImportProjectSetMainPage_The_given_URL_cannot_be_loaded,
+ messageType);
+ setPageComplete(false);
+ } catch (MalformedURLException e) {
+ // ignore as we tested it with modify listener on combo
+ }
+ return null;
+ }
}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetOperation.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetOperation.java
index e2dc46abb..1d6418f14 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetOperation.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ImportProjectSetOperation.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2008 IBM Corporation and others.
+ * Copyright (c) 2007, 2010 IBM Corporation and others.
* 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
@@ -22,9 +22,36 @@ import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
public class ImportProjectSetOperation extends TeamOperation {
-
+
+ private String psfFileContents;
+ private String urlString;
private String psfFile;
private IWorkingSet[] workingSets;
+
+
+ /**
+ * Operation for importing a Team Project Set stored in a String
+ *
+ * @param context
+ * a runnable context,
+ * <code>null</null> for running in background
+ * @param psfFileContents
+ * the psf file content to load
+ * @param urlString
+ * url or path to file loaded into <code>psfFileContents</code>
+ * @param workingSets
+ * an array of working sets where imported project should be
+ * added. If a working set doesn't exist it will be created. The
+ * array cannot be <code>null</code>, pass an empty array if you
+ * don't want to add projects to any working set.
+ */
+ public ImportProjectSetOperation(IRunnableContext context,
+ String psfFileContents, String urlString, IWorkingSet[] workingSets) {
+ super(context);
+ this.psfFileContents = psfFileContents;
+ this.workingSets = workingSets;
+ this.urlString = urlString;
+ }
/**
* Operation for importing a Team Project Set file
@@ -45,18 +72,32 @@ public class ImportProjectSetOperation extends TeamOperation {
this.psfFile = psfFile;
this.workingSets = workingSets;
}
+
+ private void runForStringContent(IProgressMonitor monitor) throws InvocationTargetException{
+ IProject[] newProjects = ProjectSetImporter.importProjectSetFromString(
+ psfFileContents, urlString, getShell(), monitor);
+ createWorkingSet(workingSets, newProjects);
+ }
+
+ private void runForFile(IProgressMonitor monitor) throws InvocationTargetException{
+ PsfFilenameStore.getInstance().remember(psfFile);
+ IProject[] newProjects = ProjectSetImporter.importProjectSet(psfFile,
+ getShell(), monitor);
+ createWorkingSet(workingSets, newProjects);
+ }
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor)
*/
- public void run(IProgressMonitor monitor) throws InvocationTargetException,
- InterruptedException {
- PsfFilenameStore.remember(psfFile);
- IProject[] newProjects = ProjectSetImporter.importProjectSet(psfFile,
- getShell(), monitor);
- createWorkingSet(workingSets, newProjects);
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException{
+ if (psfFileContents != null) {
+ runForStringContent(monitor);
+ } else {
+ runForFile(monitor);
+ }
}
/*
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetExportWizard.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetExportWizard.java
index 344d70825..c362d7955 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetExportWizard.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetExportWizard.java
@@ -59,7 +59,7 @@ public class ProjectSetExportWizard extends Wizard implements IExportWizard {
if (path.getFileExtension() == null) {
filename = filename + ".psf"; //$NON-NLS-1$
}
- PsfFilenameStore.remember(filename);
+ PsfFilenameStore.getInstance().remember(filename);
File file = new File(filename);
File parentFile = file.getParentFile();
if (parentFile != null && !parentFile.exists()) {
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetImportWizard.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetImportWizard.java
index 7e442a837..6d338d776 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetImportWizard.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/ProjectSetImportWizard.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
* 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
@@ -40,9 +40,20 @@ public class ProjectSetImportWizard extends Wizard implements IImportWizard {
public boolean performFinish() {
final boolean[] result = new boolean[] {false};
try {
- ImportProjectSetOperation op = new ImportProjectSetOperation(
- mainPage.isRunInBackgroundOn() ? null : getContainer(),
- mainPage.getFileName(), mainPage.getWorkingSets());
+ ImportProjectSetOperation op;
+ if (mainPage.getInputType() == ImportProjectSetMainPage.InputType_URL) {
+ String psfContent = mainPage.getURLContents();
+ if(psfContent==null){
+ return false;
+ }
+ op = new ImportProjectSetOperation(
+ mainPage.isRunInBackgroundOn() ? null : getContainer(),
+ psfContent, mainPage.getUrl(), mainPage.getWorkingSets());
+ } else {
+ op = new ImportProjectSetOperation(
+ mainPage.isRunInBackgroundOn() ? null : getContainer(),
+ mainPage.getFileName(), mainPage.getWorkingSets());
+ }
op.run();
result[0] = true;
} catch (InterruptedException e) {
@@ -71,6 +82,6 @@ public class ProjectSetImportWizard extends Wizard implements IImportWizard {
public void init(IWorkbench workbench, IStructuredSelection selection) {
// The code that finds "selection" is broken (it is always empty), so we
// must dig for the selection in the workbench.
- PsfFilenameStore.setDefaultFromSelection(workbench);
+ PsfFilenameStore.getInstance().setDefaultFromSelection(workbench);
}
}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfFilenameStore.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfFilenameStore.java
index 571fb49fa..aa5c735f6 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfFilenameStore.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfFilenameStore.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005 IBM Corporation and others.
+ * Copyright (c) 2005, 2010 IBM Corporation and others.
* 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
@@ -10,27 +10,16 @@
*******************************************************************************/
package org.eclipse.team.internal.ui.wizards;
-import java.util.Vector;
-
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
-import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.team.internal.ui.TeamUIPlugin;
-import org.eclipse.ui.IWorkbench;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchWindow;
-
-public class PsfFilenameStore {
- // Most recently used filename is first in the array.
- // Least recently used filename is at the end of the list.
- // When the list overflows, items drop off the end.
- private static final int HISTORY_LENGTH = 10;
-
- private static final String STORE_SECTION = "ImportPSFDialog"; //$NON-NLS-1$
+import org.eclipse.ui.*;
+
+public class PsfFilenameStore extends PsfStore {
+
private static final String FILENAMES = "filenames"; //$NON-NLS-1$
private static final String PREVIOUS = "previous"; //$NON-NLS-1$
@@ -38,13 +27,20 @@ public class PsfFilenameStore {
// This is only a cache; it is not part of the history until the user has used it.
private static String _selectedFilename = null;
- private static IDialogSettings _section;
+ private static PsfFilenameStore instance;
+
+ public static PsfFilenameStore getInstance(){
+ if(instance==null){
+ instance = new PsfFilenameStore();
+ }
+ return instance;
+ }
private PsfFilenameStore() {
- // All-static
+ // Singleton
}
- public static void setDefaultFromSelection(IWorkbench workbench) {
+ public void setDefaultFromSelection(IWorkbench workbench) {
// Scan the workbench for a selected PSF file
IWorkbenchWindow wnd = workbench.getActiveWorkbenchWindow();
IWorkbenchPage pg = wnd.getActivePage();
@@ -85,73 +81,20 @@ public class PsfFilenameStore {
_selectedFilename = path.toOSString();
}
- public static String getSuggestedDefault() {
+ public String getSuggestedDefault() {
if (_selectedFilename != null) {
return _selectedFilename;
}
return getPrevious();
}
- private static String getPrevious() {
- IDialogSettings section = getSettingsSection();
- String retval = section.get(PREVIOUS);
- if (retval == null) {
- retval = ""; //$NON-NLS-1$
- }
- return retval;
- }
-
- public static String[] getHistory() {
- IDialogSettings section = getSettingsSection();
- String[] arr = section.getArray(FILENAMES);
- if (arr == null) {
- arr = new String[0];
- }
- return arr;
- }
-
- public static void remember(String filename) {
- Vector filenames = createVector(getHistory());
- if (filenames.contains(filename)) {
- // The item is in the list. Remove it and add it back at the
- // beginning. If it already was at the beginning this will be a
- // waste of time, but it's not even measurable so I don't care.
- filenames.remove(filename);
- }
- // Most recently used filename goes to the beginning of the list
- filenames.add(0, filename);
-
- // Forget any overflowing items
- while (filenames.size() > HISTORY_LENGTH) {
- filenames.remove(HISTORY_LENGTH);
- }
-
- // Make it an array
- String[] arr = (String[]) filenames.toArray(new String[filenames.size()]);
-
- IDialogSettings section = getSettingsSection();
- section.put(FILENAMES, arr);
- section.put(PREVIOUS, filename);
+ protected String getPreviousTag() {
+ return PREVIOUS;
}
- private static Vector createVector(Object[] arr) {
- Vector v = new Vector();
- for (int ix = 0; ix < arr.length; ++ix) {
- v.add(ix, arr[ix]);
- }
- return v;
+ protected String getListTag() {
+ return FILENAMES;
}
- private static IDialogSettings getSettingsSection() {
- if (_section != null)
- return _section;
-
- IDialogSettings settings = TeamUIPlugin.getPlugin().getDialogSettings();
- _section = settings.getSection(STORE_SECTION);
- if (_section != null)
- return _section;
-
- _section = settings.addNewSection(STORE_SECTION);
- return _section;
- }
+
}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfStore.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfStore.java
new file mode 100644
index 000000000..9c7d2ee69
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfStore.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation and others.
+ * 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
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.wizards;
+
+import java.util.Vector;
+
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.team.internal.ui.TeamUIPlugin;
+
+public abstract class PsfStore {
+ // Most recently used filename is first in the array.
+ // Least recently used filename is at the end of the list.
+ // When the list overflows, items drop off the end.
+ private static final int HISTORY_LENGTH = 10;
+
+ private static final String STORE_SECTION = "ImportPSFDialog"; //$NON-NLS-1$
+
+ private static IDialogSettings _section;
+
+ protected abstract String getPreviousTag();
+ protected abstract String getListTag();
+
+ public abstract String getSuggestedDefault();
+
+ protected String getPrevious() {
+ IDialogSettings section = getSettingsSection();
+ String retval = section.get(getPreviousTag());
+ if (retval == null) {
+ retval = ""; //$NON-NLS-1$
+ }
+ return retval;
+ }
+
+ public String[] getHistory() {
+ IDialogSettings section = getSettingsSection();
+ String[] arr = section.getArray(getListTag());
+ if (arr == null) {
+ arr = new String[0];
+ }
+ return arr;
+ }
+
+ public void remember(String filename) {
+ Vector filenames = createVector(getHistory());
+ if (filenames.contains(filename)) {
+ // The item is in the list. Remove it and add it back at the
+ // beginning. If it already was at the beginning this will be a
+ // waste of time, but it's not even measurable so I don't care.
+ filenames.remove(filename);
+ }
+ // Most recently used filename goes to the beginning of the list
+ filenames.add(0, filename);
+
+ // Forget any overflowing items
+ while (filenames.size() > HISTORY_LENGTH) {
+ filenames.remove(HISTORY_LENGTH);
+ }
+
+ // Make it an array
+ String[] arr = (String[]) filenames.toArray(new String[filenames.size()]);
+
+ IDialogSettings section = getSettingsSection();
+ section.put(getListTag(), arr);
+ section.put(getPreviousTag(), filename);
+ }
+
+ private Vector createVector(Object[] arr) {
+ Vector v = new Vector();
+ for (int ix = 0; ix < arr.length; ++ix) {
+ v.add(ix, arr[ix]);
+ }
+ return v;
+ }
+
+ private IDialogSettings getSettingsSection() {
+ if (_section != null)
+ return _section;
+
+ IDialogSettings settings = TeamUIPlugin.getPlugin().getDialogSettings();
+ _section = settings.getSection(STORE_SECTION);
+ if (_section != null)
+ return _section;
+
+ _section = settings.addNewSection(STORE_SECTION);
+ return _section;
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfUrlStore.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfUrlStore.java
new file mode 100644
index 000000000..bbeec617b
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/wizards/PsfUrlStore.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation and others.
+ * 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
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.wizards;
+
+/**
+ * Stores URL history for importing project sets window.
+ *
+ */
+public class PsfUrlStore extends PsfStore {
+
+ private static final String URLS = "urls"; //$NON-NLS-1$
+ private static final String PREVIOUS = "previous_url"; //$NON-NLS-1$
+
+ private static PsfUrlStore instance;
+
+ public static PsfUrlStore getInstance() {
+ if (instance == null) {
+ instance = new PsfUrlStore();
+ }
+ return instance;
+ }
+
+ private PsfUrlStore() {
+ // Singleton
+ }
+
+ protected String getPreviousTag() {
+ return PREVIOUS;
+ }
+
+ protected String getListTag() {
+ return URLS;
+ }
+
+ public String getSuggestedDefault() {
+ return getPrevious();
+ }
+
+}

Back to the top